diff --git a/libphobos/testsuite/libphobos.phobos/phobos.exp b/libphobos/testsuite/libphobos.phobos/phobos.exp
new file mode 100644
index 0000000000000000000000000000000000000000..21ce4cea448210134c7870b6cb61e25bf66af044
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/phobos.exp
@@ -0,0 +1,50 @@
+# Copyright (C) 2019-2025 Free Software Foundation, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+# Immediately exit if we can't run target executables.
+if { ![isnative] } {
+    return
+}
+
+# Skip running test if phobos was not built on the target.
+if { ![is-effective-target d_runtime_has_std_library] } {
+    return
+}
+
+# Gather a list of all tests.
+set tests [lsort [find $srcdir/$subdir *.d]]
+
+set version_flags "-fversion=StdUnittest"
+
+if { [is-effective-target linux_pre_2639] } {
+    lappend version_flags "-fversion=Linux_Pre_2639"
+}
+
+set libphobos_skip_tests {
+    # Skip concurrency.d test: SEGVs or hangs on macOS 13+ (PR d/111628).
+    { libphobos.phobos/std_concurrency.d { x86_64-apple-darwin2[2-9]* } }
+}
+
+# Initialize dg.
+dg-init
+
+# Main loop.
+foreach test $tests {
+    dg-runtest $test "" "-Wno-deprecated -fmain $version_flags"
+}
+
+# All done.
+dg-finish
diff --git a/libphobos/testsuite/libphobos.phobos/shared/phobos-shared.exp b/libphobos/testsuite/libphobos.phobos/shared/phobos-shared.exp
index 31cc13dd9ae22db930a6d88cc0146726bbbb7f5d..3f62864b42a20f9bd06019489942581731875ba9 100644
--- a/libphobos/testsuite/libphobos.phobos/shared/phobos-shared.exp
+++ b/libphobos/testsuite/libphobos.phobos/shared/phobos-shared.exp
@@ -24,6 +24,11 @@ if { ![is-effective-target d_runtime_has_std_library] } {
     return
 }
 
+# Skip running test if not doing expensive tests.
+if { ![is-effective-target run_expensive_tests] } {
+    return
+}
+
 # Gather a list of all tests.
 set tests [lsort [filter_libphobos_unittests [find $srcdir/../src "*.d"]]]
 
diff --git a/libphobos/testsuite/libphobos.phobos/static/phobos-static.exp b/libphobos/testsuite/libphobos.phobos/static/phobos-static.exp
index 642019c7f6d7027c4d97d4c425722c6921920d74..ecccbc952cf8b1653ea04cd946c8df1c21f6ca18 100644
--- a/libphobos/testsuite/libphobos.phobos/static/phobos-static.exp
+++ b/libphobos/testsuite/libphobos.phobos/static/phobos-static.exp
@@ -24,6 +24,11 @@ if { ![is-effective-target d_runtime_has_std_library] } {
     return
 }
 
+# Skip running test if not doing expensive tests.
+if { ![is-effective-target run_expensive_tests] } {
+    return
+}
+
 # Gather a list of all tests.
 set tests [lsort [filter_libphobos_unittests [find $srcdir/../src "*.d"]]]
 
diff --git a/libphobos/testsuite/libphobos.phobos/std_algorithm_comparison.d b/libphobos/testsuite/libphobos.phobos/std_algorithm_comparison.d
new file mode 100644
index 0000000000000000000000000000000000000000..800694039a09b2b9517277b817d4ee6a502176da
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_algorithm_comparison.d
@@ -0,0 +1,454 @@
+@safe @nogc unittest
+{
+    import std.algorithm.comparison;
+
+    assert(3.among(1, 42, 24, 3, 2));
+
+    if (auto pos = "bar".among("foo", "bar", "baz"))
+        assert(pos == 2);
+    else
+        assert(false);
+
+    // 42 is larger than 24
+    assert(42.among!((lhs, rhs) => lhs > rhs)(43, 24, 100) == 2);
+}
+
+@safe @nogc unittest
+{
+    import std.algorithm.comparison;
+
+    assert(3.among!(2, 3, 4));
+    assert("bar".among!("foo", "bar", "baz") == 2);
+}
+
+@system unittest
+{
+    import std.algorithm.comparison;
+
+    import std.algorithm.iteration : map;
+    import std.format : format;
+
+    class A
+    {
+        int a;
+        this(int a) {this.a = a;}
+        @property int i() { return a; }
+    }
+    interface I { }
+    class B : I { }
+
+    Object[] arr = [new A(1), new B(), null];
+
+    auto results = arr.map!(castSwitch!(
+                                (A a) => "A with a value of %d".format(a.a),
+                                (I i) => "derived from I",
+                                ()    => "null reference",
+                            ))();
+
+    // A is handled directly:
+    assert(results[0] == "A with a value of 1");
+    // B has no handler - it is handled by the handler of I:
+    assert(results[1] == "derived from I");
+    // null is handled by the null handler:
+    assert(results[2] == "null reference");
+}
+
+@system unittest
+{
+    import std.algorithm.comparison;
+
+    import std.exception : assertThrown;
+
+    class A { }
+    class B { }
+    // Void handlers are allowed if they throw:
+    assertThrown!Exception(
+        new B().castSwitch!(
+            (A a) => 1,
+            (B d)    { throw new Exception("B is not allowed!"); }
+        )()
+    );
+
+    // Void handlers are also allowed if all the handlers are void:
+    new A().castSwitch!(
+        (A a) { },
+        (B b) { assert(false); },
+    )();
+}
+
+@safe @nogc unittest
+{
+    import std.algorithm.comparison;
+
+    assert(clamp(2, 1, 3) == 2);
+    assert(clamp(0, 1, 3) == 1);
+    assert(clamp(4, 1, 3) == 3);
+
+    assert(clamp(1, 1, 1) == 1);
+
+    assert(clamp(5, -1, 2u) == 2);
+
+    auto x = clamp(42, uint.max, uint.max);
+    static assert(is(typeof(x) == int));
+    assert(x == -1);
+}
+
+pure @safe unittest
+{
+    import std.algorithm.comparison;
+
+    int result;
+
+    result = cmp("abc", "abc");
+    assert(result == 0);
+    result = cmp("", "");
+    assert(result == 0);
+    result = cmp("abc", "abcd");
+    assert(result < 0);
+    result = cmp("abcd", "abc");
+    assert(result > 0);
+    result = cmp("abc"d, "abd");
+    assert(result < 0);
+    result = cmp("bbc", "abc"w);
+    assert(result > 0);
+    result = cmp("aaa", "aaaa"d);
+    assert(result < 0);
+    result = cmp("aaaa", "aaa"d);
+    assert(result > 0);
+    result = cmp("aaa", "aaa"d);
+    assert(result == 0);
+    result = cmp("aaa"d, "aaa"d);
+    assert(result == 0);
+    result = cmp(cast(int[])[], cast(int[])[]);
+    assert(result == 0);
+    result = cmp([1, 2, 3], [1, 2, 3]);
+    assert(result == 0);
+    result = cmp([1, 3, 2], [1, 2, 3]);
+    assert(result > 0);
+    result = cmp([1, 2, 3], [1L, 2, 3, 4]);
+    assert(result < 0);
+    result = cmp([1L, 2, 3], [1, 2]);
+    assert(result > 0);
+}
+
+pure @safe unittest
+{
+    import std.algorithm.comparison;
+
+    int result;
+
+    result = cmp!"a > b"("abc", "abc");
+    assert(result == 0);
+    result = cmp!"a > b"("", "");
+    assert(result == 0);
+    result = cmp!"a > b"("abc", "abcd");
+    assert(result < 0);
+    result = cmp!"a > b"("abcd", "abc");
+    assert(result > 0);
+    result = cmp!"a > b"("abc"d, "abd");
+    assert(result > 0);
+    result = cmp!"a > b"("bbc", "abc"w);
+    assert(result < 0);
+    result = cmp!"a > b"("aaa", "aaaa"d);
+    assert(result < 0);
+    result = cmp!"a > b"("aaaa", "aaa"d);
+    assert(result > 0);
+    result = cmp!"a > b"("aaa", "aaa"d);
+    assert(result == 0);
+    result = cmp("aaa"d, "aaa"d);
+    assert(result == 0);
+    result = cmp!"a > b"(cast(int[])[], cast(int[])[]);
+    assert(result == 0);
+    result = cmp!"a > b"([1, 2, 3], [1, 2, 3]);
+    assert(result == 0);
+    result = cmp!"a > b"([1, 3, 2], [1, 2, 3]);
+    assert(result < 0);
+    result = cmp!"a > b"([1, 2, 3], [1L, 2, 3, 4]);
+    assert(result < 0);
+    result = cmp!"a > b"([1L, 2, 3], [1, 2]);
+    assert(result > 0);
+}
+
+@safe @nogc unittest
+{
+    import std.algorithm.comparison;
+
+    import std.algorithm.comparison : equal;
+    import std.math.operations : isClose;
+
+    int[4] a = [ 1, 2, 4, 3 ];
+    assert(!equal(a[], a[1..$]));
+    assert(equal(a[], a[]));
+    assert(equal!((a, b) => a == b)(a[], a[]));
+
+    // different types
+    double[4] b = [ 1.0, 2, 4, 3];
+    assert(!equal(a[], b[1..$]));
+    assert(equal(a[], b[]));
+
+    // predicated: ensure that two vectors are approximately equal
+    double[4] c = [ 1.0000000005, 2, 4, 3];
+    assert(equal!isClose(b[], c[]));
+}
+
+@safe unittest
+{
+    import std.algorithm.comparison;
+
+    import std.algorithm.comparison : equal;
+    import std.range : iota, chunks;
+    assert(equal!(equal!equal)(
+        [[[0, 1], [2, 3]], [[4, 5], [6, 7]]],
+        iota(0, 8).chunks(2).chunks(2)
+    ));
+}
+
+@safe unittest
+{
+    import std.algorithm.comparison;
+
+    with(EditOp)
+    {
+        assert(levenshteinDistanceAndPath("foo", "foobar")[1] == [none, none, none, insert, insert, insert]);
+        assert(levenshteinDistanceAndPath("banana", "fazan")[1] == [substitute, none, substitute, none, none, remove]);
+    }
+}
+
+@safe unittest
+{
+    import std.algorithm.comparison;
+
+    import std.algorithm.iteration : filter;
+    import std.uni : toUpper;
+
+    assert(levenshteinDistance("cat", "rat") == 1);
+    assert(levenshteinDistance("parks", "spark") == 2);
+    assert(levenshteinDistance("abcde", "abcde") == 0);
+    assert(levenshteinDistance("abcde", "abCde") == 1);
+    assert(levenshteinDistance("kitten", "sitting") == 3);
+    assert(levenshteinDistance!((a, b) => toUpper(a) == toUpper(b))
+        ("parks", "SPARK") == 2);
+    assert(levenshteinDistance("parks".filter!"true", "spark".filter!"true") == 2);
+    assert(levenshteinDistance("ID", "I♥D") == 1);
+}
+
+@safe unittest
+{
+    import std.algorithm.comparison;
+
+    string a = "Saturday", b = "Sundays";
+    auto p = levenshteinDistanceAndPath(a, b);
+    assert(p[0] == 4);
+    assert(equal(p[1], "nrrnsnnni"));
+}
+
+@safe @nogc unittest
+{
+    import std.algorithm.comparison;
+
+    int a = 5;
+    short b = 6;
+    double c = 2;
+    auto d = max(a, b);
+    assert(is(typeof(d) == int));
+    assert(d == 6);
+    auto e = min(a, b, c);
+    assert(is(typeof(e) == double));
+    assert(e == 2);
+}
+
+@safe @nogc unittest
+{
+    import std.algorithm.comparison;
+
+    int a = 5;
+    short b = 6;
+    double c = 2;
+    auto d = min(a, b);
+    static assert(is(typeof(d) == int));
+    assert(d == 5);
+    auto e = min(a, b, c);
+    static assert(is(typeof(e) == double));
+    assert(e == 2);
+    ulong f = 0xffff_ffff_ffff;
+    const uint g = min(f, 0xffff_0000);
+    assert(g == 0xffff_0000);
+    dchar h = 100;
+    uint i = 101;
+    static assert(is(typeof(min(h, i)) == dchar));
+    static assert(is(typeof(min(i, h)) == uint));
+    assert(min(h, i) == 100);
+}
+
+@safe @nogc unittest
+{
+    import std.algorithm.comparison;
+
+    int a = -10;
+    uint f = 10;
+    static assert(is(typeof(min(a, f)) == int));
+    assert(min(a, f) == -10);
+}
+
+@safe unittest
+{
+    import std.algorithm.comparison;
+
+    import std.datetime;
+    assert(min(Date(2012, 12, 21), Date(1982, 1, 4)) == Date(1982, 1, 4));
+    assert(min(Date(1982, 1, 4), Date(2012, 12, 21)) == Date(1982, 1, 4));
+    assert(min(Date(1982, 1, 4), Date.min) == Date.min);
+    assert(min(Date.min, Date(1982, 1, 4)) == Date.min);
+    assert(min(Date(1982, 1, 4), Date.max) == Date(1982, 1, 4));
+    assert(min(Date.max, Date(1982, 1, 4)) == Date(1982, 1, 4));
+    assert(min(Date.min, Date.max) == Date.min);
+    assert(min(Date.max, Date.min) == Date.min);
+}
+
+@safe @nogc unittest
+{
+    import std.algorithm.comparison;
+
+    int[6] x = [ 1,   5, 2, 7,   4, 3 ];
+    double[6] y = [ 1.0, 5, 2, 7.3, 4, 8 ];
+    auto m = mismatch(x[], y[]);
+    assert(m[0] == x[3 .. $]);
+    assert(m[1] == y[3 .. $]);
+
+    auto m2 = mismatch(x[], y[], x[], y[]);
+    assert(m2[0] == x[3 .. $]);
+    assert(m2[1] == y[3 .. $]);
+    assert(m2[2] == x[3 .. $]);
+    assert(m2[3] == y[3 .. $]);
+}
+
+@safe unittest
+{
+    import std.algorithm.comparison;
+
+    string res = 2.predSwitch!"a < b"(
+        1, "less than 1",
+        5, "less than 5",
+        10, "less than 10",
+        "greater or equal to 10");
+
+    assert(res == "less than 5");
+
+    //The arguments are lazy, which allows us to use predSwitch to create
+    //recursive functions:
+    int factorial(int n)
+    {
+        return n.predSwitch!"a <= b"(
+            -1, {throw new Exception("Can not calculate n! for n < 0");}(),
+            0, 1, // 0! = 1
+            n * factorial(n - 1) // n! = n * (n - 1)! for n >= 0
+            );
+    }
+    assert(factorial(3) == 6);
+
+    //Void return expressions are allowed if they always throw:
+    import std.exception : assertThrown;
+    assertThrown!Exception(factorial(-9));
+}
+
+@safe nothrow pure unittest
+{
+    import std.algorithm.comparison;
+
+    assert(isSameLength([1, 2, 3], [4, 5, 6]));
+    assert(isSameLength([1, 2, 3], [4, 5, 6], [7, 8, 9]));
+    assert(isSameLength([0.3, 90.4, 23.7, 119.2], [42.6, 23.6, 95.5, 6.3]));
+    assert(isSameLength("abc", "xyz"));
+    assert(isSameLength("abc", "xyz", [1, 2, 3]));
+
+    int[] a;
+    int[] b;
+    assert(isSameLength(a, b));
+    assert(isSameLength(a, b, a, a, b, b, b));
+
+    assert(!isSameLength([1, 2, 3], [4, 5]));
+    assert(!isSameLength([1, 2, 3], [4, 5, 6], [7, 8]));
+    assert(!isSameLength([0.3, 90.4, 23.7], [42.6, 23.6, 95.5, 6.3]));
+    assert(!isSameLength("abcd", "xyz"));
+    assert(!isSameLength("abcd", "xyz", "123"));
+    assert(!isSameLength("abcd", "xyz", "1234"));
+}
+
+@safe pure unittest
+{
+    import std.algorithm.comparison;
+
+    import std.typecons : Yes;
+
+    assert(isPermutation([1, 2, 3], [3, 2, 1]));
+    assert(isPermutation([1.1, 2.3, 3.5], [2.3, 3.5, 1.1]));
+    assert(isPermutation("abc", "bca"));
+
+    assert(!isPermutation([1, 2], [3, 4]));
+    assert(!isPermutation([1, 1, 2, 3], [1, 2, 2, 3]));
+    assert(!isPermutation([1, 1], [1, 1, 1]));
+
+    // Faster, but allocates GC handled memory
+    assert(isPermutation!(Yes.allocateGC)([1.1, 2.3, 3.5], [2.3, 3.5, 1.1]));
+    assert(!isPermutation!(Yes.allocateGC)([1, 2], [3, 4]));
+}
+
+@safe pure unittest
+{
+    import std.algorithm.comparison;
+
+    const a = 1;
+    const b = 2;
+    auto ab = either(a, b);
+    static assert(is(typeof(ab) == const(int)));
+    assert(ab == a);
+
+    auto c = 2;
+    const d = 3;
+    auto cd = either!(a => a == 3)(c, d); // use predicate
+    static assert(is(typeof(cd) == int));
+    assert(cd == d);
+
+    auto e = 0;
+    const f = 2;
+    auto ef = either(e, f);
+    static assert(is(typeof(ef) == int));
+    assert(ef == f);
+}
+
+@safe pure unittest
+{
+    import std.algorithm.comparison;
+
+    immutable p = 1;
+    immutable q = 2;
+    auto pq = either(p, q);
+    static assert(is(typeof(pq) == immutable(int)));
+    assert(pq == p);
+
+    assert(either(3, 4) == 3);
+    assert(either(0, 4) == 4);
+    assert(either(0, 0) == 0);
+    assert(either("", "a") == "");
+}
+
+@safe pure unittest
+{
+    import std.algorithm.comparison;
+
+    string r = null;
+    assert(either(r, "a") == "a");
+    assert(either("a", "") == "a");
+
+    immutable s = [1, 2];
+    assert(either(s, s) == s);
+
+    assert(either([0, 1], [1, 2]) == [0, 1]);
+    assert(either([0, 1], [1]) == [0, 1]);
+    assert(either("a", "b") == "a");
+
+    static assert(!__traits(compiles, either(1, "a")));
+    static assert(!__traits(compiles, either(1.0, "a")));
+    static assert(!__traits(compiles, either('a', "a")));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_algorithm_iteration.d b/libphobos/testsuite/libphobos.phobos/std_algorithm_iteration.d
new file mode 100644
index 0000000000000000000000000000000000000000..a4f74fb9394ae3a7949738e954b64bf9a961c256
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_algorithm_iteration.d
@@ -0,0 +1,1004 @@
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.range, std.stdio;
+    import std.typecons : tuple;
+
+    ulong counter = 0;
+    double fun(int x)
+    {
+        ++counter;
+        // http://en.wikipedia.org/wiki/Quartic_function
+        return ( (x + 4.0) * (x + 1.0) * (x - 1.0) * (x - 3.0) ) / 14.0 + 0.5;
+    }
+    // Without cache, with array (greedy)
+    auto result1 = iota(-4, 5).map!(a =>tuple(a, fun(a)))()
+                             .filter!(a => a[1] < 0)()
+                             .map!(a => a[0])()
+                             .array();
+
+    // the values of x that have a negative y are:
+    assert(equal(result1, [-3, -2, 2]));
+
+    // Check how many times fun was evaluated.
+    // As many times as the number of items in both source and result.
+    assert(counter == iota(-4, 5).length + result1.length);
+
+    counter = 0;
+    // Without array, with cache (lazy)
+    auto result2 = iota(-4, 5).map!(a =>tuple(a, fun(a)))()
+                             .cache()
+                             .filter!(a => a[1] < 0)()
+                             .map!(a => a[0])();
+
+    // the values of x that have a negative y are:
+    assert(equal(result2, [-3, -2, 2]));
+
+    // Check how many times fun was evaluated.
+    // Only as many times as the number of items in source.
+    assert(counter == iota(-4, 5).length);
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.range;
+    int i = 0;
+
+    auto r = iota(0, 4).tee!((a){i = a;}, No.pipeOnPop);
+    auto r1 = r.take(3).cache();
+    auto r2 = r.cache().take(3);
+
+    assert(equal(r1, [0, 1, 2]));
+    assert(i == 2); //The last "seen" element was 2. The data in cache has been cleared.
+
+    assert(equal(r2, [0, 1, 2]));
+    assert(i == 3); //cache has accessed 3. It is still stored internally by cache.
+}
+
+@safe @nogc unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.range : chain, only;
+    auto squares =
+        chain(only(1, 2, 3, 4), only(5, 6)).map!(a => a * a);
+    assert(equal(squares, only(1, 4, 9, 16, 25, 36)));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    auto sums = [2, 4, 6, 8];
+    auto products = [1, 4, 9, 16];
+
+    size_t i = 0;
+    foreach (result; [ 1, 2, 3, 4 ].map!("a + a", "a * a"))
+    {
+        assert(result[0] == sums[i]);
+        assert(result[1] == products[i]);
+        ++i;
+    }
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.conv : to;
+
+    alias stringize = map!(to!string);
+    assert(equal(stringize([ 1, 2, 3, 4 ]), [ "1", "2", "3", "4" ]));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.range : iota;
+    import std.typecons : No;
+
+    int[] arr;
+    iota(5).each!(n => arr ~= n);
+    assert(arr == [0, 1, 2, 3, 4]);
+
+    // stop iterating early
+    iota(5).each!((n) { arr ~= n; return No.each; });
+    assert(arr == [0, 1, 2, 3, 4, 0]);
+
+    // If the range supports it, the value can be mutated in place
+    arr.each!((ref n) => n++);
+    assert(arr == [1, 2, 3, 4, 5, 1]);
+
+    arr.each!"a++";
+    assert(arr == [2, 3, 4, 5, 6, 2]);
+
+    auto m = arr.map!(n => n);
+    // by-ref lambdas are not allowed for non-ref ranges
+    static assert(!__traits(compiles, m.each!((ref n) => n++)));
+
+    // The default predicate consumes the range
+    (&m).each();
+    assert(m.empty);
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    auto arr = new size_t[4];
+
+    arr.each!"a=i"();
+    assert(arr == [0, 1, 2, 3]);
+
+    arr.each!((i, ref e) => e = i * 2);
+    assert(arr == [0, 2, 4, 6]);
+}
+
+@system unittest
+{
+    import std.algorithm.iteration;
+
+    static class S
+    {
+        int x;
+        int opApply(scope int delegate(ref int _x) dg) { return dg(x); }
+    }
+
+    auto s = new S;
+    s.each!"a++";
+    assert(s.x == 1);
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.math.operations : isClose;
+    import std.range;
+
+    int[] arr = [ 1, 2, 3, 4, 5 ];
+
+    // Filter below 3
+    auto small = filter!(a => a < 3)(arr);
+    assert(equal(small, [ 1, 2 ]));
+
+    // Filter again, but with Uniform Function Call Syntax (UFCS)
+    auto sum = arr.filter!(a => a < 3);
+    assert(equal(sum, [ 1, 2 ]));
+
+    // In combination with chain() to span multiple ranges
+    int[] a = [ 3, -2, 400 ];
+    int[] b = [ 100, -101, 102 ];
+    auto r = chain(a, b).filter!(a => a > 0);
+    assert(equal(r, [ 3, 400, 100, 102 ]));
+
+    // Mixing convertible types is fair game, too
+    double[] c = [ 2.5, 3.0 ];
+    auto r1 = chain(c, a, b).filter!(a => cast(int) a != a);
+    assert(isClose(r1, [ 2.5 ]));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.range;
+
+    int[] arr = [ 1, 2, 3, 4, 5 ];
+    auto small = filterBidirectional!("a < 3")(arr);
+    static assert(isBidirectionalRange!(typeof(small)));
+    assert(small.back == 2);
+    assert(equal(small, [ 1, 2 ]));
+    assert(equal(retro(small), [ 2, 1 ]));
+    // In combination with chain() to span multiple ranges
+    int[] a = [ 3, -2, 400 ];
+    int[] b = [ 100, -101, 102 ];
+    auto r = filterBidirectional!("a > 0")(chain(a, b));
+    assert(r.back == 102);
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.typecons : tuple, Tuple;
+
+    int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
+    assert(equal(group(arr), [ tuple(1, 1u), tuple(2, 4u), tuple(3, 1u),
+        tuple(4, 3u), tuple(5, 1u) ][]));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.sorting : sort;
+    import std.array : assocArray;
+
+    uint[string] result;
+    auto range = ["a", "b", "a", "c", "b", "c", "c", "d", "e"];
+    result = range.sort!((a, b) => a < b)
+        .group
+        .assocArray;
+
+    assert(result == ["a": 2U, "b": 2U, "c": 3U, "d": 1U, "e": 1U]);
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+
+    // Grouping by particular attribute of each element:
+    auto data = [
+        [1, 1],
+        [1, 2],
+        [2, 2],
+        [2, 3]
+    ];
+
+    auto r1 = data.chunkBy!((a,b) => a[0] == b[0]);
+    assert(r1.equal!equal([
+        [[1, 1], [1, 2]],
+        [[2, 2], [2, 3]]
+    ]));
+
+    auto r2 = data.chunkBy!((a,b) => a[1] == b[1]);
+    assert(r2.equal!equal([
+        [[1, 1]],
+        [[1, 2], [2, 2]],
+        [[2, 3]]
+    ]));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.range.primitives;
+    import std.typecons : tuple;
+
+    // Grouping by particular attribute of each element:
+    auto range =
+    [
+        [1, 1],
+        [1, 1],
+        [1, 2],
+        [2, 2],
+        [2, 3],
+        [2, 3],
+        [3, 3]
+    ];
+
+    auto byX = chunkBy!(a => a[0])(range);
+    auto expected1 =
+    [
+        tuple(1, [[1, 1], [1, 1], [1, 2]]),
+        tuple(2, [[2, 2], [2, 3], [2, 3]]),
+        tuple(3, [[3, 3]])
+    ];
+    foreach (e; byX)
+    {
+        assert(!expected1.empty);
+        assert(e[0] == expected1.front[0]);
+        assert(e[1].equal(expected1.front[1]));
+        expected1.popFront();
+    }
+
+    auto byY = chunkBy!(a => a[1])(range);
+    auto expected2 =
+    [
+        tuple(1, [[1, 1], [1, 1]]),
+        tuple(2, [[1, 2], [2, 2]]),
+        tuple(3, [[2, 3], [2, 3], [3, 3]])
+    ];
+    foreach (e; byY)
+    {
+        assert(!expected2.empty);
+        assert(e[0] == expected2.front[0]);
+        assert(e[1].equal(expected2.front[1]));
+        expected2.popFront();
+    }
+}
+
+nothrow pure @safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.range : dropExactly;
+    auto source = [4, 3, 2, 11, 0, -3, -3, 5, 3, 0];
+
+    auto result1 = source.splitWhen!((a,b) => a <= b);
+    assert(result1.save.equal!equal([
+        [4, 3, 2],
+        [11, 0, -3],
+        [-3],
+        [5, 3, 0]
+    ]));
+
+    //splitWhen, like chunkBy, is currently a reference range (this may change
+    //in future). Remember to call `save` when appropriate.
+    auto result2 = result1.dropExactly(2);
+    assert(result1.save.equal!equal([
+        [-3],
+        [5, 3, 0]
+    ]));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.conv : text;
+
+    assert(["abc", "def"].joiner.equal("abcdef"));
+    assert(["Mary", "has", "a", "little", "lamb"]
+        .joiner("...")
+        .equal("Mary...has...a...little...lamb"));
+    assert(["", "abc"].joiner("xyz").equal("xyzabc"));
+    assert([""].joiner("xyz").equal(""));
+    assert(["", ""].joiner("xyz").equal("xyz"));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.range : repeat;
+
+    assert([""].joiner.equal(""));
+    assert(["", ""].joiner.equal(""));
+    assert(["", "abc"].joiner.equal("abc"));
+    assert(["abc", ""].joiner.equal("abc"));
+    assert(["abc", "def"].joiner.equal("abcdef"));
+    assert(["Mary", "has", "a", "little", "lamb"].joiner.equal("Maryhasalittlelamb"));
+    assert("abc".repeat(3).joiner.equal("abcabcabc"));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    auto a = [ [1, 2, 3], [42, 43] ];
+    auto j = joiner(a);
+    j.front = 44;
+    assert(a == [ [44, 2, 3], [42, 43] ]);
+    assert(equal(j, [44, 2, 3, 42, 43]));
+}
+
+@safe pure unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.range : chain, cycle, iota, only, retro, take, zip;
+    import std.format : format;
+
+    static immutable number = "12345678";
+    static immutable delimiter = ",";
+    auto formatted = number.retro
+        .zip(3.iota.cycle.take(number.length))
+        .map!(z => chain(z[0].only, z[1] == 2 ? delimiter : null))
+        .joiner
+        .retro;
+    static immutable expected = "12,345,678";
+    assert(formatted.equal(expected));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.range : retro;
+
+    auto a = [[1, 2, 3], [4, 5]];
+    auto j = a.joiner;
+    j.back = 44;
+    assert(a == [[1, 2, 3], [4, 44]]);
+    assert(equal(j.retro, [44, 4, 3, 2, 1]));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : max, min;
+    import std.math.operations : isClose;
+    import std.range;
+
+    int[] arr = [ 1, 2, 3, 4, 5 ];
+    // Sum all elements
+    auto sum = reduce!((a,b) => a + b)(0, arr);
+    assert(sum == 15);
+
+    // Sum again, using a string predicate with "a" and "b"
+    sum = reduce!"a + b"(0, arr);
+    assert(sum == 15);
+
+    // Compute the maximum of all elements
+    auto largest = reduce!(max)(arr);
+    assert(largest == 5);
+
+    // Max again, but with Uniform Function Call Syntax (UFCS)
+    largest = arr.reduce!(max);
+    assert(largest == 5);
+
+    // Compute the number of odd elements
+    auto odds = reduce!((a,b) => a + (b & 1))(0, arr);
+    assert(odds == 3);
+
+    // Compute the sum of squares
+    auto ssquares = reduce!((a,b) => a + b * b)(0, arr);
+    assert(ssquares == 55);
+
+    // Chain multiple ranges into seed
+    int[] a = [ 3, 4 ];
+    int[] b = [ 100 ];
+    auto r = reduce!("a + b")(chain(a, b));
+    assert(r == 107);
+
+    // Mixing convertible types is fair game, too
+    double[] c = [ 2.5, 3.0 ];
+    auto r1 = reduce!("a + b")(chain(a, b, c));
+    assert(isClose(r1, 112.5));
+
+    // To minimize nesting of parentheses, Uniform Function Call Syntax can be used
+    auto r2 = chain(a, b, c).reduce!("a + b");
+    assert(isClose(r2, 112.5));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : max, min;
+    import std.math.operations : isClose;
+    import std.math.algebraic : sqrt;
+    import std.typecons : tuple, Tuple;
+
+    double[] a = [ 3.0, 4, 7, 11, 3, 2, 5 ];
+    // Compute minimum and maximum in one pass
+    auto r = reduce!(min, max)(a);
+    // The type of r is Tuple!(int, int)
+    assert(isClose(r[0], 2));  // minimum
+    assert(isClose(r[1], 11)); // maximum
+
+    // Compute sum and sum of squares in one pass
+    r = reduce!("a + b", "a + b * b")(tuple(0.0, 0.0), a);
+    assert(isClose(r[0], 35));  // sum
+    assert(isClose(r[1], 233)); // sum of squares
+    // Compute average and standard deviation from the above
+    auto avg = r[0] / a.length;
+    assert(avg == 5);
+    auto stdev = sqrt(r[1] / a.length - avg * avg);
+    assert(cast(int) stdev == 2);
+}
+
+@safe pure unittest
+{
+    import std.algorithm.iteration;
+
+    immutable arr = [1, 2, 3, 4, 5];
+
+    // Sum all elements
+    assert(arr.fold!((a, e) => a + e) == 15);
+
+    // Sum all elements with explicit seed
+    assert(arr.fold!((a, e) => a + e)(6) == 21);
+
+    import std.algorithm.comparison : min, max;
+    import std.typecons : tuple;
+
+    // Compute minimum and maximum at the same time
+    assert(arr.fold!(min, max) == tuple(1, 5));
+
+    // Compute minimum and maximum at the same time with seeds
+    assert(arr.fold!(min, max)(0, 7) == tuple(0, 7));
+
+    // Can be used in a UFCS chain
+    assert(arr.map!(a => a + 1).fold!((a, e) => a + e) == 20);
+
+    // Return the last element of any range
+    assert(arr.fold!((a, e) => e) == 5);
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : max, min;
+    import std.array : array;
+    import std.math.operations : isClose;
+    import std.range : chain;
+
+    int[] arr = [1, 2, 3, 4, 5];
+    // Partial sum of all elements
+    auto sum = cumulativeFold!((a, b) => a + b)(arr, 0);
+    assert(sum.array == [1, 3, 6, 10, 15]);
+
+    // Partial sum again, using a string predicate with "a" and "b"
+    auto sum2 = cumulativeFold!"a + b"(arr, 0);
+    assert(sum2.array == [1, 3, 6, 10, 15]);
+
+    // Compute the partial maximum of all elements
+    auto largest = cumulativeFold!max(arr);
+    assert(largest.array == [1, 2, 3, 4, 5]);
+
+    // Partial max again, but with Uniform Function Call Syntax (UFCS)
+    largest = arr.cumulativeFold!max;
+    assert(largest.array == [1, 2, 3, 4, 5]);
+
+    // Partial count of odd elements
+    auto odds = arr.cumulativeFold!((a, b) => a + (b & 1))(0);
+    assert(odds.array == [1, 1, 2, 2, 3]);
+
+    // Compute the partial sum of squares
+    auto ssquares = arr.cumulativeFold!((a, b) => a + b * b)(0);
+    assert(ssquares.array == [1, 5, 14, 30, 55]);
+
+    // Chain multiple ranges into seed
+    int[] a = [3, 4];
+    int[] b = [100];
+    auto r = cumulativeFold!"a + b"(chain(a, b));
+    assert(r.array == [3, 7, 107]);
+
+    // Mixing convertible types is fair game, too
+    double[] c = [2.5, 3.0];
+    auto r1 = cumulativeFold!"a + b"(chain(a, b, c));
+    assert(isClose(r1, [3, 7, 107, 109.5, 112.5]));
+
+    // To minimize nesting of parentheses, Uniform Function Call Syntax can be used
+    auto r2 = chain(a, b, c).cumulativeFold!"a + b";
+    assert(isClose(r2, [3, 7, 107, 109.5, 112.5]));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : max, min;
+    import std.algorithm.iteration : map;
+    import std.math.operations : isClose;
+    import std.typecons : tuple;
+
+    double[] a = [3.0, 4, 7, 11, 3, 2, 5];
+    // Compute minimum and maximum in one pass
+    auto r = a.cumulativeFold!(min, max);
+    // The type of r is Tuple!(int, int)
+    assert(isClose(r.map!"a[0]", [3, 3, 3, 3, 3, 2, 2]));     // minimum
+    assert(isClose(r.map!"a[1]", [3, 4, 7, 11, 11, 11, 11])); // maximum
+
+    // Compute sum and sum of squares in one pass
+    auto r2 = a.cumulativeFold!("a + b", "a + b * b")(tuple(0.0, 0.0));
+    assert(isClose(r2.map!"a[0]", [3, 7, 14, 25, 28, 30, 35]));      // sum
+    assert(isClose(r2.map!"a[1]", [9, 25, 74, 195, 204, 208, 233])); // sum of squares
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+
+    assert("a|bc|def".splitter('|').equal([ "a", "bc", "def" ]));
+
+    int[] a = [1, 0, 2, 3, 0, 4, 5, 6];
+    int[][] w = [ [1], [2, 3], [4, 5, 6] ];
+    assert(a.splitter(0).equal(w));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.typecons : Yes;
+
+    assert("a|bc|def".splitter!("a == b", Yes.keepSeparators)('|')
+        .equal([ "a", "|", "bc", "|", "def" ]));
+
+    int[] a = [1, 0, 2, 3, 0, 4, 5, 6];
+    int[][] w = [ [1], [0], [2, 3], [0], [4, 5, 6] ];
+    assert(a.splitter!("a == b", Yes.keepSeparators)(0).equal(w));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+
+    assert("|ab|".splitter('|').equal([ "", "ab", "" ]));
+    assert("ab".splitter('|').equal([ "ab" ]));
+
+    assert("a|b||c".splitter('|').equal([ "a", "b", "", "c" ]));
+    assert("hello  world".splitter(' ').equal([ "hello", "", "world" ]));
+
+    auto a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
+    auto w = [ [1, 2], [], [3], [4, 5], [] ];
+    assert(a.splitter(0).equal(w));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.typecons : Yes;
+
+    assert("|ab|".splitter!("a == b", Yes.keepSeparators)('|')
+        .equal([ "", "|", "ab", "|", "" ]));
+    assert("ab".splitter!("a == b", Yes.keepSeparators)('|')
+        .equal([ "ab" ]));
+
+    assert("a|b||c".splitter!("a == b", Yes.keepSeparators)('|')
+        .equal([ "a", "|", "b", "|", "", "|", "c" ]));
+    assert("hello  world".splitter!("a == b", Yes.keepSeparators)(' ')
+        .equal([ "hello", " ", "", " ", "world" ]));
+
+    auto a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
+    auto w = [ [1, 2], [0], [], [0], [3], [0], [4, 5], [0], [] ];
+    assert(a.splitter!("a == b", Yes.keepSeparators)(0).equal(w));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.range : empty;
+
+    assert("".splitter('|').empty);
+    assert("|".splitter('|').equal([ "", "" ]));
+    assert("||".splitter('|').equal([ "", "", "" ]));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.typecons : Yes;
+    import std.range : empty;
+
+    assert("".splitter!("a == b", Yes.keepSeparators)('|').empty);
+    assert("|".splitter!("a == b", Yes.keepSeparators)('|')
+        .equal([ "", "|", "" ]));
+    assert("||".splitter!("a == b", Yes.keepSeparators)('|')
+        .equal([ "", "|", "", "|", "" ]));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+
+    assert("a=>bc=>def".splitter("=>").equal([ "a", "bc", "def" ]));
+    assert("a|b||c".splitter("||").equal([ "a|b", "c" ]));
+    assert("hello  world".splitter("  ").equal([ "hello", "world" ]));
+
+    int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
+    int[][] w = [ [1, 2], [3, 0, 4, 5, 0] ];
+    assert(a.splitter([0, 0]).equal(w));
+
+    a = [ 0, 0 ];
+    assert(a.splitter([0, 0]).equal([ (int[]).init, (int[]).init ]));
+
+    a = [ 0, 0, 1 ];
+    assert(a.splitter([0, 0]).equal([ [], [1] ]));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.typecons : Yes;
+
+    assert("a=>bc=>def".splitter!("a == b", Yes.keepSeparators)("=>")
+        .equal([ "a", "=>", "bc", "=>", "def" ]));
+    assert("a|b||c".splitter!("a == b", Yes.keepSeparators)("||")
+        .equal([ "a|b", "||", "c" ]));
+    assert("hello  world".splitter!("a == b", Yes.keepSeparators)("  ")
+        .equal([ "hello", "  ",  "world" ]));
+
+    int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
+    int[][] w = [ [1, 2], [0, 0], [3, 0, 4, 5, 0] ];
+    assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0]).equal(w));
+
+    a = [ 0, 0 ];
+    assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0])
+        .equal([ (int[]).init, [0, 0], (int[]).init ]));
+
+    a = [ 0, 0, 1 ];
+    assert(a.splitter!("a == b", Yes.keepSeparators)([0, 0])
+        .equal([ [], [0, 0], [1] ]));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.ascii : toLower;
+
+    assert("abXcdxef".splitter!"a.toLower == b"('x').equal(
+                 [ "ab", "cd", "ef" ]));
+
+    auto w = [ [0], [1], [2] ];
+    assert(w.splitter!"a.front == b"(1).equal([ [[0]], [[2]] ]));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.typecons : Yes;
+    import std.ascii : toLower;
+
+    assert("abXcdxef".splitter!("a.toLower == b", Yes.keepSeparators)('x')
+        .equal([ "ab", "X", "cd", "x", "ef" ]));
+
+    auto w = [ [0], [1], [2] ];
+    assert(w.splitter!("a.front == b", Yes.keepSeparators)(1)
+        .equal([ [[0]], [[1]], [[2]] ]));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.range.primitives : front;
+
+    assert(equal(splitter!(a => a == '|')("a|bc|def"), [ "a", "bc", "def" ]));
+    assert(equal(splitter!(a => a == ' ')("hello  world"), [ "hello", "", "world" ]));
+
+    int[] a = [ 1, 2, 0, 0, 3, 0, 4, 5, 0 ];
+    int[][] w = [ [1, 2], [], [3], [4, 5], [] ];
+    assert(equal(splitter!(a => a == 0)(a), w));
+
+    a = [ 0 ];
+    assert(equal(splitter!(a => a == 0)(a), [ (int[]).init, (int[]).init ]));
+
+    a = [ 0, 1 ];
+    assert(equal(splitter!(a => a == 0)(a), [ [], [1] ]));
+
+    w = [ [0], [1], [2] ];
+    assert(equal(splitter!(a => a.front == 1)(w), [ [[0]], [[2]] ]));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+
+    assert("|ab|".splitter('|').equal([ "", "ab", "" ]));
+    assert("ab".splitter('|').equal([ "ab" ]));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.typecons : Yes;
+
+    assert("|ab|".splitter!("a == b", Yes.keepSeparators)('|')
+        .equal([ "", "|", "ab", "|", "" ]));
+    assert("ab".splitter!("a == b", Yes.keepSeparators)('|')
+        .equal([ "ab" ]));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.range : retro;
+    assert("a|bc|def".splitter('|').retro.equal([ "def", "bc", "a" ]));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.typecons : Yes;
+    import std.range : retro;
+    assert("a|bc|def".splitter!("a == b", Yes.keepSeparators)('|')
+        .retro.equal([ "def", "|", "bc", "|", "a" ]));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.ascii : isWhite;
+    import std.algorithm.comparison : equal;
+    import std.algorithm.iteration : splitter;
+
+    string str = "Hello World!";
+    assert(str.splitter!(isWhite).equal(["Hello", "World!"]));
+}
+
+@safe pure unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    auto a = " a     bcd   ef gh ";
+    assert(equal(splitter(a), ["a", "bcd", "ef", "gh"][]));
+}
+
+@safe pure unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+
+    // substitute single elements
+    assert("do_it".substitute('_', ' ').equal("do it"));
+
+    // substitute multiple, single elements
+    assert("do_it".substitute('_', ' ',
+                               'd', 'g',
+                               'i', 't',
+                               't', 'o')
+                  .equal("go to"));
+
+    // substitute subranges
+    assert("do_it".substitute("_", " ",
+                              "do", "done")
+                  .equal("done it"));
+
+    // substitution works for any ElementType
+    int[] x = [1, 2, 3];
+    auto y = x.substitute(1, 0.1);
+    assert(y.equal([0.1, 2, 3]));
+    static assert(is(typeof(y.front) == double));
+
+    import std.range : retro;
+    assert([1, 2, 3].substitute(1, 0.1).retro.equal([3, 2, 0.1]));
+}
+
+@safe pure unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+
+    // substitute subranges of a range
+    assert("apple_tree".substitute!("apple", "banana",
+                                    "tree", "shrub").equal("banana_shrub"));
+
+    // substitute subranges of a range
+    assert("apple_tree".substitute!('a', 'b',
+                                    't', 'f').equal("bpple_free"));
+
+    // substitute values
+    assert('a'.substitute!('a', 'b', 't', 'f') == 'b');
+}
+
+@safe pure unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.range.primitives : ElementType;
+
+    int[3] x = [1, 2, 3];
+    auto y = x[].substitute(1, 0.1)
+                .substitute(0.1, 0.2);
+    static assert(is(typeof(y.front) == double));
+    assert(y.equal([0.2, 2, 3]));
+
+    auto z = "42".substitute('2', '3')
+                 .substitute('3', '1');
+    static assert(is(ElementType!(typeof(z)) == dchar));
+    assert(equal(z, "41"));
+}
+
+@safe pure nothrow unittest
+{
+    import std.algorithm.iteration;
+
+    import std.range;
+
+    //simple integral sumation
+    assert(sum([ 1, 2, 3, 4]) == 10);
+
+    //with integral promotion
+    assert(sum([false, true, true, false, true]) == 3);
+    assert(sum(ubyte.max.repeat(100)) == 25500);
+
+    //The result may overflow
+    assert(uint.max.repeat(3).sum()           ==  4294967293U );
+    //But a seed can be used to change the sumation primitive
+    assert(uint.max.repeat(3).sum(ulong.init) == 12884901885UL);
+
+    //Floating point sumation
+    assert(sum([1.0, 2.0, 3.0, 4.0]) == 10);
+
+    //Floating point operations have double precision minimum
+    static assert(is(typeof(sum([1F, 2F, 3F, 4F])) == double));
+    assert(sum([1F, 2, 3, 4]) == 10);
+
+    //Force pair-wise floating point sumation on large integers
+    import std.math.operations : isClose;
+    assert(iota(ulong.max / 2, ulong.max / 2 + 4096).sum(0.0)
+               .isClose((ulong.max / 2) * 4096.0 + 4096^^2 / 2));
+}
+
+@safe @nogc pure nothrow unittest
+{
+    import std.algorithm.iteration;
+
+    import std.math.operations : isClose;
+    import std.math.traits : isNaN;
+
+    static immutable arr1 = [1, 2, 3];
+    static immutable arr2 = [1.5, 2.5, 12.5];
+
+    assert(arr1.mean.isClose(2));
+    assert(arr2.mean.isClose(5.5));
+
+    assert(arr1[0 .. 0].mean.isNaN);
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.algorithm.mutation : copy;
+
+    int[] arr = [ 1, 2, 2, 2, 2, 3, 4, 4, 4, 5 ];
+    assert(equal(uniq(arr), [ 1, 2, 3, 4, 5 ][]));
+
+    // Filter duplicates in-place using copy
+    arr.length -= arr.uniq().copy(arr).length;
+    assert(arr == [ 1, 2, 3, 4, 5 ]);
+
+    // Note that uniqueness is only determined consecutively; duplicated
+    // elements separated by an intervening different element will not be
+    // eliminated:
+    assert(equal(uniq([ 1, 1, 2, 1, 1, 3, 1]), [1, 2, 1, 3, 1]));
+}
+
+@safe unittest
+{
+    import std.algorithm.iteration;
+
+    import std.algorithm.comparison : equal;
+    import std.range : iota;
+    assert(equal!equal(iota(3).permutations,
+        [[0, 1, 2],
+         [1, 0, 2],
+         [2, 0, 1],
+         [0, 2, 1],
+         [1, 2, 0],
+         [2, 1, 0]]));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_algorithm_mutation.d b/libphobos/testsuite/libphobos.phobos/std_algorithm_mutation.d
new file mode 100644
index 0000000000000000000000000000000000000000..3f38d60b8596e2837adb7a6f43f2e6adaaf0ad0d
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_algorithm_mutation.d
@@ -0,0 +1,627 @@
+@safe unittest
+{
+    import std.algorithm.mutation;
+
+    auto arr = [4, 5, 6, 7, 1, 2, 3];
+    auto p = bringToFront(arr[0 .. 4], arr[4 .. $]);
+    assert(p == arr.length - 4);
+    assert(arr == [ 1, 2, 3, 4, 5, 6, 7 ]);
+}
+
+@safe unittest
+{
+    import std.algorithm.mutation;
+
+    import std.algorithm.comparison : equal;
+    import std.container : SList;
+    import std.range.primitives : popFrontN;
+
+    auto list = SList!(int)(4, 5, 6, 7, 1, 2, 3);
+    auto r1 = list[];
+    auto r2 = list[]; popFrontN(r2, 4);
+    assert(equal(r2, [ 1, 2, 3 ]));
+    bringToFront(r1, r2);
+    assert(equal(list[], [ 1, 2, 3, 4, 5, 6, 7 ]));
+}
+
+@safe unittest
+{
+    import std.algorithm.mutation;
+
+    import std.algorithm.comparison : equal;
+    import std.container : SList;
+
+    auto list = SList!(int)(4, 5, 6, 7);
+    auto vec = [ 1, 2, 3 ];
+    bringToFront(list[], vec);
+    assert(equal(list[], [ 1, 2, 3, 4 ]));
+    assert(equal(vec, [ 5, 6, 7 ]));
+}
+
+@safe unittest
+{
+    import std.algorithm.mutation;
+
+    import std.string : representation;
+    auto ar = representation("a".dup);
+    auto br = representation("ç".dup);
+
+    bringToFront(ar, br);
+
+    auto a = cast(char[]) ar;
+    auto b = cast(char[]) br;
+
+    // Illegal UTF-8
+    assert(a == "\303");
+    // Illegal UTF-8
+    assert(b == "\247a");
+}
+
+@safe unittest
+{
+    import std.algorithm.mutation;
+
+    int[] a = [ 1, 5 ];
+    int[] b = [ 9, 8 ];
+    int[] buf = new int[](a.length + b.length + 10);
+    auto rem = a.copy(buf);    // copy a into buf
+    rem = b.copy(rem);         // copy b into remainder of buf
+    assert(buf[0 .. a.length + b.length] == [1, 5, 9, 8]);
+    assert(rem.length == 10);   // unused slots in buf
+}
+
+@safe unittest
+{
+    import std.algorithm.mutation;
+
+    float[] src = [ 1.0f, 5 ];
+    double[] dest = new double[src.length];
+    src.copy(dest);
+}
+
+@safe unittest
+{
+    import std.algorithm.mutation;
+
+    import std.range;
+    int[] src = [ 1, 5, 8, 9, 10 ];
+    auto dest = new int[](3);
+    src.take(dest.length).copy(dest);
+    assert(dest == [ 1, 5, 8 ]);
+}
+
+@safe unittest
+{
+    import std.algorithm.mutation;
+
+    import std.algorithm.iteration : filter;
+    int[] src = [ 1, 5, 8, 9, 10, 1, 2, 0 ];
+    auto dest = new int[src.length];
+    auto rem = src
+        .filter!(a => (a & 1) == 1)
+        .copy(dest);
+    assert(dest[0 .. $ - rem.length] == [ 1, 5, 9, 1 ]);
+}
+
+@safe unittest
+{
+    import std.algorithm.mutation;
+
+    import std.algorithm, std.range;
+    int[] src = [1, 2, 4];
+    int[] dest = [0, 0, 0, 0, 0];
+    src.retro.copy(dest.retro);
+    assert(dest == [0, 0, 1, 2, 4]);
+}
+
+@safe unittest
+{
+    import std.algorithm.mutation;
+
+    int[] a = [ 1, 2, 3, 4 ];
+    fill(a, 5);
+    assert(a == [ 5, 5, 5, 5 ]);
+}
+
+@safe unittest
+{
+    import std.algorithm.mutation;
+
+    int[] a = [ 1, 2, 3, 4, 5 ];
+    int[] b = [ 8, 9 ];
+    fill(a, b);
+    assert(a == [ 8, 9, 8, 9, 8 ]);
+}
+
+@system unittest
+{
+    import std.algorithm.mutation;
+
+    import core.stdc.stdlib : malloc, free;
+
+    struct S
+    {
+        int a = 10;
+    }
+
+    auto s = (cast(S*) malloc(5 * S.sizeof))[0 .. 5];
+    initializeAll(s);
+    assert(s == [S(10), S(10), S(10), S(10), S(10)]);
+
+    scope(exit) free(s.ptr);
+}
+
+@safe unittest
+{
+    import std.algorithm.mutation;
+
+    Object obj1 = new Object;
+    Object obj2 = obj1;
+    Object obj3;
+
+    move(obj2, obj3);
+    assert(obj3 is obj1);
+    // obj2 unchanged
+    assert(obj2 is obj1);
+}
+
+pure nothrow @safe @nogc unittest
+{
+    import std.algorithm.mutation;
+
+    // Structs without destructors are simply copied
+    struct S1
+    {
+        int a = 1;
+        int b = 2;
+    }
+    S1 s11 = { 10, 11 };
+    S1 s12;
+
+    move(s11, s12);
+
+    assert(s12 == S1(10, 11));
+    assert(s11 == s12);
+
+    // But structs with destructors or postblits are reset to their .init value
+    // after copying to the target.
+    struct S2
+    {
+        int a = 1;
+        int b = 2;
+
+        ~this() pure nothrow @safe @nogc { }
+    }
+    S2 s21 = { 3, 4 };
+    S2 s22;
+
+    move(s21, s22);
+
+    assert(s21 == S2(1, 2));
+    assert(s22 == S2(3, 4));
+}
+
+pure nothrow @safe @nogc unittest
+{
+    import std.algorithm.mutation;
+
+    struct S
+    {
+        int a = 1;
+        @disable this(this);
+        ~this() pure nothrow @safe @nogc {}
+    }
+    S s1;
+    s1.a = 2;
+    S s2 = move(s1);
+    assert(s1.a == 1);
+    assert(s2.a == 2);
+}
+
+pure nothrow @safe @nogc unittest
+{
+    import std.algorithm.mutation;
+
+    struct S
+    {
+        int a;
+        void opPostMove(const ref S old)
+        {
+            assert(a == old.a);
+            a++;
+        }
+    }
+    S s1;
+    s1.a = 41;
+    S s2 = move(s1);
+    assert(s2.a == 42);
+}
+
+pure nothrow @nogc @system unittest
+{
+    import std.algorithm.mutation;
+
+    static struct Foo
+    {
+    pure nothrow @nogc:
+        this(int* ptr) { _ptr = ptr; }
+        ~this() { if (_ptr) ++*_ptr; }
+        int* _ptr;
+    }
+
+    int val;
+    Foo foo1 = void; // uninitialized
+    auto foo2 = Foo(&val); // initialized
+    assert(foo2._ptr is &val);
+
+    // Using `move(foo2, foo1)` would have an undefined effect because it would destroy
+    // the uninitialized foo1.
+    // moveEmplace directly overwrites foo1 without destroying or initializing it first.
+    moveEmplace(foo2, foo1);
+    assert(foo1._ptr is &val);
+    assert(foo2._ptr is null);
+    assert(val == 0);
+}
+
+pure nothrow @safe @nogc unittest
+{
+    import std.algorithm.mutation;
+
+    int[3] a = [ 1, 2, 3 ];
+    int[5] b;
+    assert(moveAll(a[], b[]) is b[3 .. $]);
+    assert(a[] == b[0 .. 3]);
+    int[3] cmp = [ 1, 2, 3 ];
+    assert(a[] == cmp[]);
+}
+
+pure nothrow @nogc @system unittest
+{
+    import std.algorithm.mutation;
+
+    static struct Foo
+    {
+        ~this() pure nothrow @nogc { if (_ptr) ++*_ptr; }
+        int* _ptr;
+    }
+    int[3] refs = [0, 1, 2];
+    Foo[3] src = [Foo(&refs[0]), Foo(&refs[1]), Foo(&refs[2])];
+    Foo[5] dst = void;
+
+    auto tail = moveEmplaceAll(src[], dst[]); // move 3 value from src over dst
+    assert(tail.length == 2); // returns remaining uninitialized values
+    initializeAll(tail);
+
+    import std.algorithm.searching : all;
+    assert(src[].all!(e => e._ptr is null));
+    assert(dst[0 .. 3].all!(e => e._ptr !is null));
+}
+
+pure nothrow @safe @nogc unittest
+{
+    import std.algorithm.mutation;
+
+    int[5] a = [ 1, 2, 3, 4, 5 ];
+    int[3] b;
+    assert(moveSome(a[], b[])[0] is a[3 .. $]);
+    assert(a[0 .. 3] == b);
+    assert(a == [ 1, 2, 3, 4, 5 ]);
+}
+
+pure nothrow @nogc @system unittest
+{
+    import std.algorithm.mutation;
+
+    static struct Foo
+    {
+        ~this() pure nothrow @nogc { if (_ptr) ++*_ptr; }
+        int* _ptr;
+    }
+    int[4] refs = [0, 1, 2, 3];
+    Foo[4] src = [Foo(&refs[0]), Foo(&refs[1]), Foo(&refs[2]), Foo(&refs[3])];
+    Foo[3] dst = void;
+
+    auto res = moveEmplaceSome(src[], dst[]);
+    assert(res.length == 2);
+
+    import std.algorithm.searching : all;
+    assert(src[0 .. 3].all!(e => e._ptr is null));
+    assert(src[3]._ptr !is null);
+    assert(dst[].all!(e => e._ptr !is null));
+}
+
+@safe unittest
+{
+    import std.algorithm.mutation;
+
+    int[] a = [0, 1, 2, 3];
+    assert(remove!(SwapStrategy.stable)(a, 1) == [0, 2, 3]);
+    a = [0, 1, 2, 3];
+    assert(remove!(SwapStrategy.unstable)(a, 1) == [0, 3, 2]);
+}
+
+@safe unittest
+{
+    import std.algorithm.mutation;
+
+    import std.algorithm.sorting : partition;
+
+    // Put stuff greater than 3 on the left
+    auto arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+    assert(partition!(a => a > 3, SwapStrategy.stable)(arr) == [1, 2, 3]);
+    assert(arr == [4, 5, 6, 7, 8, 9, 10, 1, 2, 3]);
+
+    arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+    assert(partition!(a => a > 3, SwapStrategy.semistable)(arr) == [2, 3, 1]);
+    assert(arr == [4, 5, 6, 7, 8, 9, 10, 2, 3, 1]);
+
+    arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+    assert(partition!(a => a > 3, SwapStrategy.unstable)(arr) == [3, 2, 1]);
+    assert(arr == [10, 9, 8, 4, 5, 6, 7, 3, 2, 1]);
+}
+
+@safe pure unittest
+{
+    import std.algorithm.mutation;
+
+    import std.typecons : tuple;
+
+    auto a = [ 0, 1, 2, 3, 4, 5 ];
+    assert(remove!(SwapStrategy.stable)(a, 1) == [ 0, 2, 3, 4, 5 ]);
+    a = [ 0, 1, 2, 3, 4, 5 ];
+    assert(remove!(SwapStrategy.stable)(a, 1, 3) == [ 0, 2, 4, 5] );
+    a = [ 0, 1, 2, 3, 4, 5 ];
+    assert(remove!(SwapStrategy.stable)(a, 1, tuple(3, 6)) == [ 0, 2 ]);
+
+    a = [ 0, 1, 2, 3, 4, 5 ];
+    assert(remove!(SwapStrategy.unstable)(a, 1) == [0, 5, 2, 3, 4]);
+    a = [ 0, 1, 2, 3, 4, 5 ];
+    assert(remove!(SwapStrategy.unstable)(a, tuple(1, 4)) == [0, 5, 4]);
+}
+
+@safe pure unittest
+{
+    import std.algorithm.mutation;
+
+    import std.typecons : tuple;
+
+    // Delete an index
+    assert([4, 5, 6].remove(1) == [4, 6]);
+
+    // Delete multiple indices
+    assert([4, 5, 6, 7, 8].remove(1, 3) == [4, 6, 8]);
+
+    // Use an indices range
+    assert([4, 5, 6, 7, 8].remove(tuple(1, 3)) == [4, 7, 8]);
+
+    // Use an indices range and individual indices
+    assert([4, 5, 6, 7, 8].remove(0, tuple(1, 3), 4) == [7]);
+}
+
+@safe pure unittest
+{
+    import std.algorithm.mutation;
+
+    assert([5, 6, 7, 8].remove!(SwapStrategy.stable)(1) == [5, 7, 8]);
+    assert([5, 6, 7, 8].remove!(SwapStrategy.unstable)(1) == [5, 8, 7]);
+}
+
+@safe unittest
+{
+    import std.algorithm.mutation;
+
+    static immutable base = [1, 2, 3, 2, 4, 2, 5, 2];
+
+    int[] arr = base[].dup;
+
+    // using a string-based predicate
+    assert(remove!("a == 2")(arr) == [ 1, 3, 4, 5 ]);
+
+    // The original array contents have been modified,
+    // so we need to reset it to its original state.
+    // The length is unmodified however.
+    arr[] = base[];
+
+    // using a lambda predicate
+    assert(remove!(a => a == 2)(arr) == [ 1, 3, 4, 5 ]);
+}
+
+@safe unittest
+{
+    import std.algorithm.mutation;
+
+    int[] arr = [ 1, 2, 3 ];
+    assert(arr.reverse == [ 3, 2, 1 ]);
+}
+
+@safe unittest
+{
+    import std.algorithm.mutation;
+
+    char[] arr = "hello\U00010143\u0100\U00010143".dup;
+    assert(arr.reverse == "\U00010143\u0100\U00010143olleh");
+}
+
+@safe pure unittest
+{
+    import std.algorithm.mutation;
+
+    assert("  foobar  ".strip(' ') == "foobar");
+    assert("00223.444500".strip('0') == "223.4445");
+    assert("ëëêéüŗōpéêëë".strip('ë') == "êéüŗōpéê");
+    assert([1, 1, 0, 1, 1].strip(1) == [0]);
+    assert([0.0, 0.01, 0.01, 0.0].strip(0).length == 2);
+}
+
+@safe pure unittest
+{
+    import std.algorithm.mutation;
+
+    assert("  foobar  ".strip!(a => a == ' ')() == "foobar");
+    assert("00223.444500".strip!(a => a == '0')() == "223.4445");
+    assert("ëëêéüŗōpéêëë".strip!(a => a == 'ë')() == "êéüŗōpéê");
+    assert([1, 1, 0, 1, 1].strip!(a => a == 1)() == [0]);
+    assert([0.0, 0.01, 0.5, 0.6, 0.01, 0.0].strip!(a => a < 0.4)().length == 2);
+}
+
+@safe pure unittest
+{
+    import std.algorithm.mutation;
+
+    assert("  foobar  ".stripLeft(' ') == "foobar  ");
+    assert("00223.444500".stripLeft('0') == "223.444500");
+    assert("ůůűniçodêéé".stripLeft('ů') == "űniçodêéé");
+    assert([1, 1, 0, 1, 1].stripLeft(1) == [0, 1, 1]);
+    assert([0.0, 0.01, 0.01, 0.0].stripLeft(0).length == 3);
+}
+
+@safe pure unittest
+{
+    import std.algorithm.mutation;
+
+    assert("  foobar  ".stripLeft!(a => a == ' ')() == "foobar  ");
+    assert("00223.444500".stripLeft!(a => a == '0')() == "223.444500");
+    assert("ůůűniçodêéé".stripLeft!(a => a == 'ů')() == "űniçodêéé");
+    assert([1, 1, 0, 1, 1].stripLeft!(a => a == 1)() == [0, 1, 1]);
+    assert([0.0, 0.01, 0.10, 0.5, 0.6].stripLeft!(a => a < 0.4)().length == 2);
+}
+
+@safe pure unittest
+{
+    import std.algorithm.mutation;
+
+    assert("  foobar  ".stripRight(' ') == "  foobar");
+    assert("00223.444500".stripRight('0') == "00223.4445");
+    assert("ùniçodêéé".stripRight('é') == "ùniçodê");
+    assert([1, 1, 0, 1, 1].stripRight(1) == [1, 1, 0]);
+    assert([0.0, 0.01, 0.01, 0.0].stripRight(0).length == 3);
+}
+
+@safe pure unittest
+{
+    import std.algorithm.mutation;
+
+    assert("  foobar  ".stripRight!(a => a == ' ')() == "  foobar");
+    assert("00223.444500".stripRight!(a => a == '0')() == "00223.4445");
+    assert("ùniçodêéé".stripRight!(a => a == 'é')() == "ùniçodê");
+    assert([1, 1, 0, 1, 1].stripRight!(a => a == 1)() == [1, 1, 0]);
+    assert([0.0, 0.01, 0.10, 0.5, 0.6].stripRight!(a => a > 0.4)().length == 3);
+}
+
+@safe unittest
+{
+    import std.algorithm.mutation;
+
+    // Swapping POD (plain old data) types:
+    int a = 42, b = 34;
+    swap(a, b);
+    assert(a == 34 && b == 42);
+
+    // Swapping structs with indirection:
+    static struct S { int x; char c; int[] y; }
+    S s1 = { 0, 'z', [ 1, 2 ] };
+    S s2 = { 42, 'a', [ 4, 6 ] };
+    swap(s1, s2);
+    assert(s1.x == 42);
+    assert(s1.c == 'a');
+    assert(s1.y == [ 4, 6 ]);
+
+    assert(s2.x == 0);
+    assert(s2.c == 'z');
+    assert(s2.y == [ 1, 2 ]);
+
+    // Immutables cannot be swapped:
+    immutable int imm1 = 1, imm2 = 2;
+    static assert(!__traits(compiles, swap(imm1, imm2)));
+
+    int c = imm1 + 0;
+    int d = imm2 + 0;
+    swap(c, d);
+    assert(c == 2);
+    assert(d == 1);
+}
+
+@safe unittest
+{
+    import std.algorithm.mutation;
+
+    // Non-copyable types can still be swapped.
+    static struct NoCopy
+    {
+        this(this) { assert(0); }
+        int n;
+        string s;
+    }
+    NoCopy nc1, nc2;
+    nc1.n = 127; nc1.s = "abc";
+    nc2.n = 513; nc2.s = "uvwxyz";
+
+    swap(nc1, nc2);
+    assert(nc1.n == 513 && nc1.s == "uvwxyz");
+    assert(nc2.n == 127 && nc2.s == "abc");
+
+    swap(nc1, nc1);
+    swap(nc2, nc2);
+    assert(nc1.n == 513 && nc1.s == "uvwxyz");
+    assert(nc2.n == 127 && nc2.s == "abc");
+
+    // Types containing non-copyable fields can also be swapped.
+    static struct NoCopyHolder
+    {
+        NoCopy noCopy;
+    }
+    NoCopyHolder h1, h2;
+    h1.noCopy.n = 31; h1.noCopy.s = "abc";
+    h2.noCopy.n = 65; h2.noCopy.s = null;
+
+    swap(h1, h2);
+    assert(h1.noCopy.n == 65 && h1.noCopy.s == null);
+    assert(h2.noCopy.n == 31 && h2.noCopy.s == "abc");
+
+    swap(h1, h1);
+    swap(h2, h2);
+    assert(h1.noCopy.n == 65 && h1.noCopy.s == null);
+    assert(h2.noCopy.n == 31 && h2.noCopy.s == "abc");
+
+    // Const types cannot be swapped.
+    const NoCopy const1, const2;
+    assert(const1.n == 0 && const2.n == 0);
+    static assert(!__traits(compiles, swap(const1, const2)));
+}
+
+pure @safe nothrow unittest
+{
+    import std.algorithm.mutation;
+
+    import std.algorithm.comparison : equal;
+    auto a = [1, 2, 3];
+    a.swapAt(1, 2);
+    assert(a.equal([1, 3, 2]));
+}
+
+@safe unittest
+{
+    import std.algorithm.mutation;
+
+    import std.range : empty;
+    int[] a = [ 100, 101, 102, 103 ];
+    int[] b = [ 0, 1, 2, 3 ];
+    auto c = swapRanges(a[1 .. 3], b[2 .. 4]);
+    assert(c[0].empty && c[1].empty);
+    assert(a == [ 100, 2, 3, 103 ]);
+    assert(b == [ 0, 1, 101, 102 ]);
+}
+
+nothrow @system unittest
+{
+    import std.algorithm.mutation;
+
+    import core.stdc.stdlib : malloc, free;
+
+    auto s = (cast(int*) malloc(5 * int.sizeof))[0 .. 5];
+    uninitializedFill(s, 42);
+    assert(s == [ 42, 42, 42, 42, 42 ]);
+
+    scope(exit) free(s.ptr);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_algorithm_searching.d b/libphobos/testsuite/libphobos.phobos/std_algorithm_searching.d
new file mode 100644
index 0000000000000000000000000000000000000000..fd09d0abcd442e77cab2e212fda39eb04b7995de
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_algorithm_searching.d
@@ -0,0 +1,668 @@
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    assert( all!"a & 1"([1, 3, 5, 7, 9]));
+    assert(!all!"a & 1"([1, 2, 3, 5, 7, 9]));
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    int[3] vals = [5, 3, 18];
+    assert( all(vals[]));
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    import std.ascii : isWhite;
+    assert( all!(any!isWhite)(["a a", "b b"]));
+    assert(!any!(all!isWhite)(["a a", "b b"]));
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    int[3] vals1 = [0, 0, 0];
+    assert(!any(vals1[])); //none of vals1 evaluate to true
+
+    int[3] vals2 = [2, 0, 2];
+    assert( any(vals2[]));
+    assert(!all(vals2[]));
+
+    int[3] vals3 = [3, 3, 3];
+    assert( any(vals3[]));
+    assert( all(vals3[]));
+}
+
+@safe pure unittest
+{
+    import std.algorithm.searching;
+
+    auto s = "1 + (2 * (3 + 1 / 2)";
+    assert(!balancedParens(s, '(', ')'));
+    s = "1 + (2 * (3 + 1) / 2)";
+    assert(balancedParens(s, '(', ')'));
+    s = "1 + (2 * (3 + 1) / 2)";
+    assert(!balancedParens(s, '(', ')', 0));
+    s = "1 + (2 * 3 + 1) / (2 - 5)";
+    assert(balancedParens(s, '(', ')', 0));
+    s = "f(x) = ⌈x⌉";
+    assert(balancedParens(s, '⌈', '⌉'));
+}
+
+@safe pure nothrow unittest
+{
+    import std.algorithm.searching;
+
+    auto bmFinder = boyerMooreFinder("TG");
+
+    string r = "TAGTGCCTGA";
+    // search for the first match in the haystack r
+    r = bmFinder.beFound(r);
+    assert(r == "TGCCTGA");
+
+    // continue search in haystack
+    r = bmFinder.beFound(r[2 .. $]);
+    assert(r == "TGA");
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    assert(commonPrefix("hello, world", "hello, there") == "hello, ");
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    // count elements in range
+    int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ];
+    assert(count(a, 2) == 3);
+    assert(count!("a > b")(a, 2) == 5);
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    import std.uni : toLower;
+    // count range in range
+    assert(count("abcadfabf", "ab") == 2);
+    assert(count("ababab", "abab") == 1);
+    assert(count("ababab", "abx") == 0);
+    // fuzzy count range in range
+    assert(count!((a, b) => toLower(a) == toLower(b))("AbcAdFaBf", "ab") == 2);
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    // count elements in range
+    int[] a = [ 1, 2, 4, 3, 2, 5, 3, 2, 4 ];
+    assert(count(a) == 9);
+    // count predicate in range
+    assert(count!("a > 2")(a) == 5);
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    assert(countUntil("hello world", "world") == 6);
+    assert(countUntil("hello world", 'r') == 8);
+    assert(countUntil("hello world", "programming") == -1);
+    assert(countUntil("日本語", "本語") == 1);
+    assert(countUntil("日本語", '語')   == 2);
+    assert(countUntil("日本語", "五") == -1);
+    assert(countUntil("日本語", '五') == -1);
+    assert(countUntil([0, 7, 12, 22, 9], [12, 22]) == 2);
+    assert(countUntil([0, 7, 12, 22, 9], 9) == 4);
+    assert(countUntil!"a > b"([0, 7, 12, 22, 9], 20) == 3);
+
+    // supports multiple needles
+    auto res = "...hello".countUntil("ha", "he");
+    assert(res.steps == 3);
+    assert(res.needle == 1);
+
+    // returns -1 if no needle was found
+    res = "hello".countUntil("ha", "hu");
+    assert(res.steps == -1);
+    assert(res.needle == -1);
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    import std.ascii : isDigit;
+    import std.uni : isWhite;
+
+    assert(countUntil!(isWhite)("hello world") == 5);
+    assert(countUntil!(isDigit)("hello world") == -1);
+    assert(countUntil!"a > 20"([0, 7, 12, 22, 9]) == 3);
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    import std.ascii : isAlpha;
+    assert("abc".endsWith!(a => a.isAlpha));
+    assert("abc".endsWith!isAlpha);
+
+    assert(!"ab1".endsWith!(a => a.isAlpha));
+
+    assert(!"ab1".endsWith!isAlpha);
+    assert(!"".endsWith!(a => a.isAlpha));
+
+    import std.algorithm.comparison : among;
+    assert("abc".endsWith!(a => a.among('c', 'd') != 0));
+    assert(!"abc".endsWith!(a => a.among('a', 'b') != 0));
+
+    assert(endsWith("abc", ""));
+    assert(!endsWith("abc", "b"));
+    assert(endsWith("abc", "a", 'c') == 2);
+    assert(endsWith("abc", "c", "a") == 1);
+    assert(endsWith("abc", "c", "c") == 1);
+    assert(endsWith("abc", "bc", "c") == 2);
+    assert(endsWith("abc", "x", "c", "b") == 2);
+    assert(endsWith("abc", "x", "aa", "bc") == 3);
+    assert(endsWith("abc", "x", "aaa", "sab") == 0);
+    assert(endsWith("abc", "x", "aaa", 'c', "sab") == 3);
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    auto arr = [ 1, 2, 3, 4, 1 ];
+    assert(find!("a > 2")(arr) == [ 3, 4, 1 ]);
+
+    // with predicate alias
+    bool pred(int e) => e + 1 > 1.5;
+    assert(find!(pred)(arr) == arr);
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    import std.range.primitives;
+
+    auto arr = [1, 2, 4, 4, 4, 4, 5, 6, 9];
+    assert(arr.find(4) == [4, 4, 4, 4, 5, 6, 9]);
+    assert(arr.find(1) == arr);
+    assert(arr.find(9) == [9]);
+    assert(arr.find!((e, n) => e > n)(4) == [5, 6, 9]);
+    assert(arr.find!((e, n) => e < n)(4) == arr);
+    assert(arr.find(0).empty);
+    assert(arr.find(10).empty);
+    assert(arr.find(8).empty);
+
+    assert(find("hello, world", ',') == ", world");
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    import std.range.primitives;
+    import std.uni : toLower;
+
+    string[] s = ["Hello", "world", "!"];
+    assert(s.find!((e, n) => toLower(e) == n)("hello") == s);
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    import std.container : SList;
+    import std.range.primitives : empty;
+    import std.typecons : Tuple;
+
+    assert(find("hello, world", "World").empty);
+    assert(find("hello, world", "wo") == "world");
+    assert([1, 2, 3, 4].find(SList!int(2, 3)[]) == [2, 3, 4]);
+    alias C = Tuple!(int, "x", int, "y");
+    auto a = [C(1,0), C(2,0), C(3,1), C(4,0)];
+    assert(a.find!"a.x == b"([2, 3]) == [C(2,0), C(3,1), C(4,0)]);
+    assert(a[1 .. $].find!"a.x == b"([2, 3]) == [C(2,0), C(3,1), C(4,0)]);
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    import std.typecons : tuple;
+    int[] a = [ 1, 4, 2, 3 ];
+    assert(find(a, 4) == [ 4, 2, 3 ]);
+    assert(find(a, [ 1, 4 ]) == [ 1, 4, 2, 3 ]);
+    assert(find(a, [ 1, 3 ], 4) == tuple([ 4, 2, 3 ], 2));
+    // Mixed types allowed if comparable
+    assert(find(a, 5, [ 1.2, 3.5 ], 2.0) == tuple([ 2, 3 ], 3));
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    import std.range.primitives : empty;
+    int[] a = [ -1, 0, 1, 2, 3, 4, 5 ];
+    int[] b = [ 1, 2, 3 ];
+
+    assert(find(a, boyerMooreFinder(b)) == [ 1, 2, 3, 4, 5 ]);
+    assert(find(b, boyerMooreFinder(a)).empty);
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    const arr = [0, 1, 2, 3];
+    assert(canFind(arr, 2));
+    assert(!canFind(arr, 4));
+
+    // find one of several needles
+    assert(arr.canFind(3, 2));
+    assert(arr.canFind(3, 2) == 2); // second needle found
+    assert(arr.canFind([1, 3], 2) == 2);
+
+    assert(canFind(arr, [1, 2], [2, 3]));
+    assert(canFind(arr, [1, 2], [2, 3]) == 1);
+    assert(canFind(arr, [1, 7], [2, 3]));
+    assert(canFind(arr, [1, 7], [2, 3]) == 2);
+    assert(!canFind(arr, [1, 3], [2, 4]));
+    assert(canFind(arr, [1, 3], [2, 4]) == 0);
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    auto words = [
+        "apple",
+        "beeswax",
+        "cardboard"
+    ];
+    assert(!canFind(words, "bees"));
+    assert( canFind!((string elem, string needle) => elem.startsWith(needle))(words, "bees"));
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    string s1 = "aaa111aaa";
+    string s2 = "aaa222aaa";
+    string s3 = "aaa333aaa";
+    string s4 = "aaa444aaa";
+    const hay = [s1, s2, s3, s4];
+    assert(hay.canFind!(e => e.canFind("111", "222")));
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    int[] a = [ 11, 10, 10, 9, 8, 8, 7, 8, 9 ];
+    auto r = findAdjacent(a);
+    assert(r == [ 10, 10, 9, 8, 8, 7, 8, 9 ]);
+    auto p = findAdjacent!("a < b")(a);
+    assert(p == [ 7, 8, 9 ]);
+
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    int[] a = [ -1, 0, 1, 2, 3, 4, 5 ];
+    int[] b = [ 3, 1, 2 ];
+    assert(findAmong(a, b) == a[2 .. $]);
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    import std.range.primitives : empty;
+    // Needle is found; s is replaced by the substring following the first
+    // occurrence of the needle.
+    string s = "abcdef";
+    assert(findSkip(s, "cd") && s == "ef");
+
+    // Needle is not found; s is left untouched.
+    s = "abcdef";
+    assert(!findSkip(s, "cxd") && s == "abcdef");
+
+    // If the needle occurs at the end of the range, the range is left empty.
+    s = "abcdef";
+    assert(findSkip(s, "def") && s.empty);
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    import std.ascii : isWhite;
+    string s = "    abc";
+    assert(findSkip!isWhite(s) && s == "abc");
+    assert(!findSkip!isWhite(s) && s == "abc");
+
+    s = "  ";
+    assert(findSkip!isWhite(s) == 2);
+}
+
+@safe pure nothrow unittest
+{
+    import std.algorithm.searching;
+
+    // findSplit returns a triplet
+    if (auto split = "dlang-rocks".findSplit("-"))
+    {
+        assert(split[0] == "dlang");
+        assert(split[1] == "-");
+        assert(split[2] == "rocks");
+    }
+    else assert(0);
+
+    // findSplitBefore returns 2 ranges
+    if (const split = [2, 3, 2, 3, 4, 1].findSplitBefore!"a > b"([2, 2]))
+    {
+        assert(split[0] == [2, 3, 2]);
+        // [3, 4] each greater than [2, 2]
+        assert(split[1] == [3, 4, 1]);
+    }
+    else assert(0);
+}
+
+@safe pure nothrow unittest
+{
+    import std.algorithm.searching;
+
+    import std.range.primitives : empty;
+
+    auto a = "Carl Sagan Memorial Station";
+    auto r = findSplit(a, "Velikovsky");
+    import std.typecons : isTuple;
+    static assert(isTuple!(typeof(r.asTuple)));
+    static assert(isTuple!(typeof(r)));
+    assert(!r);
+    assert(r[0] == a);
+    assert(r[1].empty);
+    assert(r[2].empty);
+    r = findSplit(a, " ");
+    assert(r[0] == "Carl");
+    assert(r[1] == " ");
+    assert(r[2] == "Sagan Memorial Station");
+    if (const r1 = findSplitBefore(a, "Sagan"))
+    {
+        assert(r1);
+        assert(r1[0] == "Carl ");
+        assert(r1[1] == "Sagan Memorial Station");
+    }
+    if (const r2 = findSplitAfter(a, "Sagan"))
+    {
+        assert(r2);
+        assert(r2[0] == "Carl Sagan");
+        assert(r2[1] == " Memorial Station");
+    }
+}
+
+@safe pure nothrow unittest
+{
+    import std.algorithm.searching;
+
+    import std.range : only;
+    assert([1, 2, 3, 4].findSplitBefore(only(3))[0] == [1, 2]);
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    import std.conv : text;
+    import std.typecons : tuple;
+
+    int[] a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ];
+    // Minimum is 1 and occurs 3 times
+    assert(a.minCount == tuple(1, 3));
+    // Maximum is 4 and occurs 2 times
+    assert(a.maxCount == tuple(4, 2));
+}
+
+@safe pure unittest
+{
+    import std.algorithm.searching;
+
+    import std.range : enumerate;
+    import std.typecons : tuple;
+
+    assert([2, 7, 1, 3].minElement == 1);
+
+    // allows to get the index of an element too
+    assert([5, 3, 7, 9].enumerate.minElement!"a.value" == tuple(1, 3));
+
+    // any custom accessor can be passed
+    assert([[0, 4], [1, 2]].minElement!"a[1]" == [1, 2]);
+
+    // can be seeded
+    int[] arr;
+    assert(arr.minElement(1) == 1);
+}
+
+@safe pure unittest
+{
+    import std.algorithm.searching;
+
+    import std.range : enumerate;
+    import std.typecons : tuple;
+    assert([2, 1, 4, 3].maxElement == 4);
+
+    // allows to get the index of an element too
+    assert([2, 1, 4, 3].enumerate.maxElement!"a.value" == tuple(2, 4));
+
+    // any custom accessor can be passed
+    assert([[0, 4], [1, 2]].maxElement!"a[1]" == [0, 4]);
+
+    // can be seeded
+    int[] arr;
+    assert(arr.minElement(1) == 1);
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    assert(extrema([5,2,9,4,1]) == [1, 9]);
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    int[] a = [ 2, 3, 4, 1, 2, 4, 1, 1, 2 ];
+    // Minimum is 1 and first occurs in position 3
+    assert(a.minPos == [ 1, 2, 4, 1, 1, 2 ]);
+    // Maximum is 4 and first occurs in position 2
+    assert(a.maxPos == [ 4, 1, 2, 4, 1, 1, 2 ]);
+}
+
+@safe pure nothrow unittest
+{
+    import std.algorithm.searching;
+
+    int[] a = [2, 3, 4, 1, 2, 4, 1, 1, 2];
+
+    // Minimum is 1 and first occurs in position 3
+    assert(a.minIndex == 3);
+    // Get maximum index with minIndex
+    assert(a.minIndex!"a > b" == 2);
+
+    // Range is empty, so return value is -1
+    int[] b;
+    assert(b.minIndex == -1);
+
+    // Works with more custom types
+    struct Dog { int age; }
+    Dog[] dogs = [Dog(10), Dog(5), Dog(15)];
+    assert(dogs.minIndex!"a.age < b.age" == 1);
+}
+
+@safe pure nothrow unittest
+{
+    import std.algorithm.searching;
+
+    // Maximum is 4 and first occurs in position 2
+    int[] a = [2, 3, 4, 1, 2, 4, 1, 1, 2];
+    assert(a.maxIndex == 2);
+
+    // Empty range
+    int[] b;
+    assert(b.maxIndex == -1);
+
+    // Works with more custom types
+    struct Dog { int age; }
+    Dog[] dogs = [Dog(10), Dog(15), Dog(5)];
+    assert(dogs.maxIndex!"a.age < b.age" == 1);
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    import std.algorithm.comparison : equal;
+
+    auto s1 = "Hello world";
+    assert(!skipOver(s1, "Ha"));
+    assert(s1 == "Hello world");
+    assert(skipOver(s1, "Hell") && s1 == "o world", s1);
+
+    string[]  r1 = ["abc", "def", "hij"];
+    dstring[] r2 = ["abc"d];
+    assert(!skipOver!((a, b) => a.equal(b))(r1, ["def"d]), r1[0]);
+    assert(r1 == ["abc", "def", "hij"]);
+    assert(skipOver!((a, b) => a.equal(b))(r1, r2));
+    assert(r1 == ["def", "hij"]);
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    import std.ascii : isWhite;
+    import std.range.primitives : empty;
+
+    auto s2 = "\t\tvalue";
+    auto s3 = "";
+    auto s4 = "\t\t\t";
+    assert(s2.skipOver!isWhite && s2 == "value");
+    assert(!s3.skipOver!isWhite);
+    assert(s4.skipOver!isWhite && s3.empty);
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    auto s = "Hello world";
+    assert(!skipOver(s, "hello", "HellO"));
+    assert(s == "Hello world");
+
+    // the range is skipped over the longest matching needle is skipped
+    assert(skipOver(s, "foo", "hell", "Hello "));
+    assert(s == "world");
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    import std.algorithm.comparison : equal;
+
+    auto s1 = "Hello world";
+    assert(!skipOver(s1, 'a'));
+    assert(s1 == "Hello world");
+    assert(skipOver(s1, 'H') && s1 == "ello world");
+
+    string[] r = ["abc", "def", "hij"];
+    dstring e = "abc"d;
+    assert(!skipOver!((a, b) => a.equal(b))(r, "def"d));
+    assert(r == ["abc", "def", "hij"]);
+    assert(skipOver!((a, b) => a.equal(b))(r, e));
+    assert(r == ["def", "hij"]);
+
+    auto s2 = "";
+    assert(!s2.skipOver('a'));
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    import std.ascii : isWhite;
+    import std.range.primitives : empty;
+
+    alias whitespaceSkiper = skipOver!isWhite;
+
+    auto s2 = "\t\tvalue";
+    auto s3 = "";
+    auto s4 = "\t\t\t";
+    assert(whitespaceSkiper(s2) && s2 == "value");
+    assert(!whitespaceSkiper(s2));
+    assert(whitespaceSkiper(s4) && s3.empty);
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    import std.ascii : isAlpha;
+
+    assert("abc".startsWith!(a => a.isAlpha));
+    assert("abc".startsWith!isAlpha);
+    assert(!"1ab".startsWith!(a => a.isAlpha));
+    assert(!"".startsWith!(a => a.isAlpha));
+
+    import std.algorithm.comparison : among;
+    assert("abc".startsWith!(a => a.among('a', 'b') != 0));
+    assert(!"abc".startsWith!(a => a.among('b', 'c') != 0));
+
+    assert(startsWith("abc", ""));
+    assert(startsWith("abc", "a"));
+    assert(!startsWith("abc", "b"));
+    assert(startsWith("abc", 'a', "b") == 1);
+    assert(startsWith("abc", "b", "a") == 2);
+    assert(startsWith("abc", "a", "a") == 1);
+    assert(startsWith("abc", "ab", "a") == 2);
+    assert(startsWith("abc", "x", "a", "b") == 2);
+    assert(startsWith("abc", "x", "aa", "ab") == 3);
+    assert(startsWith("abc", "x", "aaa", "sab") == 0);
+    assert(startsWith("abc", "x", "aaa", "a", "sab") == 3);
+
+    import std.typecons : Tuple;
+    alias C = Tuple!(int, "x", int, "y");
+    assert(startsWith!"a.x == b"([ C(1,1), C(1,2), C(2,2) ], [1, 1]));
+    assert(startsWith!"a.x == b"([ C(1,1), C(2,1), C(2,2) ], [1, 1], [1, 2], [1, 3]) == 2);
+}
+
+@safe unittest
+{
+    import std.algorithm.searching;
+
+    import std.algorithm.comparison : equal;
+    import std.typecons : No;
+    int[] a = [ 1, 2, 4, 7, 7, 2, 4, 7, 3, 5];
+    assert(equal(a.until(7), [1, 2, 4]));
+    assert(equal(a.until(7, No.openRight), [1, 2, 4, 7]));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_algorithm_setops.d b/libphobos/testsuite/libphobos.phobos/std_algorithm_setops.d
new file mode 100644
index 0000000000000000000000000000000000000000..ecbfc5857a87fe22c0b249d105078985f4b319d9
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_algorithm_setops.d
@@ -0,0 +1,281 @@
+@safe unittest
+{
+    import std.algorithm.setops;
+
+    import std.algorithm.searching : canFind;
+    import std.range;
+    import std.typecons : tuple;
+
+    auto N = sequence!"n"(0);         // the range of natural numbers
+    auto N2 = cartesianProduct(N, N); // the range of all pairs of natural numbers
+
+    // Various arbitrary number pairs can be found in the range in finite time.
+    assert(canFind(N2, tuple(0, 0)));
+    assert(canFind(N2, tuple(123, 321)));
+    assert(canFind(N2, tuple(11, 35)));
+    assert(canFind(N2, tuple(279, 172)));
+}
+
+@safe unittest
+{
+    import std.algorithm.setops;
+
+    import std.algorithm.searching : canFind;
+    import std.typecons : tuple;
+
+    auto B = [ 1, 2, 3 ];
+    auto C = [ 4, 5, 6 ];
+    auto BC = cartesianProduct(B, C);
+
+    foreach (n; [[1, 4], [2, 4], [3, 4], [1, 5], [2, 5], [3, 5], [1, 6],
+                 [2, 6], [3, 6]])
+    {
+        assert(canFind(BC, tuple(n[0], n[1])));
+    }
+}
+
+@safe unittest
+{
+    import std.algorithm.setops;
+
+    import std.algorithm.comparison : equal;
+    import std.typecons : tuple;
+
+    auto A = [ 1, 2, 3 ];
+    auto B = [ 'a', 'b', 'c' ];
+    auto C = [ "x", "y", "z" ];
+    auto ABC = cartesianProduct(A, B, C);
+
+    assert(ABC.equal([
+        tuple(1, 'a', "x"), tuple(1, 'a', "y"), tuple(1, 'a', "z"),
+        tuple(1, 'b', "x"), tuple(1, 'b', "y"), tuple(1, 'b', "z"),
+        tuple(1, 'c', "x"), tuple(1, 'c', "y"), tuple(1, 'c', "z"),
+        tuple(2, 'a', "x"), tuple(2, 'a', "y"), tuple(2, 'a', "z"),
+        tuple(2, 'b', "x"), tuple(2, 'b', "y"), tuple(2, 'b', "z"),
+        tuple(2, 'c', "x"), tuple(2, 'c', "y"), tuple(2, 'c', "z"),
+        tuple(3, 'a', "x"), tuple(3, 'a', "y"), tuple(3, 'a', "z"),
+        tuple(3, 'b', "x"), tuple(3, 'b', "y"), tuple(3, 'b', "z"),
+        tuple(3, 'c', "x"), tuple(3, 'c', "y"), tuple(3, 'c', "z")
+    ]));
+}
+
+@system unittest
+{
+    import std.algorithm.setops;
+
+    import std.typecons : tuple, Tuple;
+
+    // Figure which number can be found in most arrays of the set of
+    // arrays below.
+    double[][] a =
+    [
+        [ 1, 4, 7, 8 ],
+        [ 1, 7 ],
+        [ 1, 7, 8],
+        [ 4 ],
+        [ 7 ],
+    ];
+    auto b = new Tuple!(double, uint)[1];
+    // it will modify the input range, hence we need to create a duplicate
+    largestPartialIntersection(a.dup, b);
+    // First member is the item, second is the occurrence count
+    assert(b[0] == tuple(7.0, 4u));
+    // 7.0 occurs in 4 out of 5 inputs, more than any other number
+
+    // If more of the top-frequent numbers are needed, just create a larger
+    // tgt range
+    auto c = new Tuple!(double, uint)[2];
+    largestPartialIntersection(a, c);
+    assert(c[0] == tuple(1.0, 3u));
+    // 1.0 occurs in 3 inputs
+
+    // multiset
+    double[][] x =
+    [
+        [1, 1, 1, 1, 4, 7, 8],
+        [1, 7],
+        [1, 7, 8],
+        [4, 7],
+        [7]
+    ];
+    auto y = new Tuple!(double, uint)[2];
+    largestPartialIntersection(x.dup, y);
+    // 7.0 occurs 5 times
+    assert(y[0] == tuple(7.0, 5u));
+    // 1.0 occurs 6 times
+    assert(y[1] == tuple(1.0, 6u));
+}
+
+@system unittest
+{
+    import std.algorithm.setops;
+
+    import std.typecons : tuple, Tuple;
+
+    // Figure which number can be found in most arrays of the set of
+    // arrays below, with specific per-element weights
+    double[][] a =
+    [
+        [ 1, 4, 7, 8 ],
+        [ 1, 7 ],
+        [ 1, 7, 8],
+        [ 4 ],
+        [ 7 ],
+    ];
+    auto b = new Tuple!(double, uint)[1];
+    double[double] weights = [ 1:1.2, 4:2.3, 7:1.1, 8:1.1 ];
+    largestPartialIntersectionWeighted(a, b, weights);
+    // First member is the item, second is the occurrence count
+    assert(b[0] == tuple(4.0, 2u));
+    // 4.0 occurs 2 times -> 4.6 (2 * 2.3)
+    // 7.0 occurs 3 times -> 4.4 (3 * 1.1)
+
+   // multiset
+    double[][] x =
+    [
+        [ 1, 1, 1, 4, 7, 8 ],
+        [ 1, 7 ],
+        [ 1, 7, 8],
+        [ 4 ],
+        [ 7 ],
+    ];
+    auto y = new Tuple!(double, uint)[1];
+    largestPartialIntersectionWeighted(x, y, weights);
+    assert(y[0] == tuple(1.0, 5u));
+    // 1.0 occurs 5 times -> 1.2 * 5 = 6
+}
+
+@system unittest
+{
+    import std.algorithm.setops;
+
+    import std.algorithm.comparison : equal;
+
+    double[][] a =
+    [
+        [ 1, 4, 7, 8 ],
+        [ 1, 7 ],
+        [ 1, 7, 8],
+        [ 4 ],
+        [ 7 ],
+    ];
+    auto witness = [
+        1, 1, 1, 4, 4, 7, 7, 7, 7, 8, 8
+    ];
+    assert(equal(multiwayMerge(a), witness));
+
+    double[][] b =
+    [
+        // range with duplicates
+        [ 1, 1, 4, 7, 8 ],
+        [ 7 ],
+        [ 1, 7, 8],
+        [ 4 ],
+        [ 7 ],
+    ];
+    // duplicates are propagated to the resulting range
+    assert(equal(multiwayMerge(b), witness));
+}
+
+@system unittest
+{
+    import std.algorithm.setops;
+
+    import std.algorithm.comparison : equal;
+
+    // sets
+    double[][] a =
+    [
+        [ 1, 4, 7, 8 ],
+        [ 1, 7 ],
+        [ 1, 7, 8],
+        [ 4 ],
+        [ 7 ],
+    ];
+
+    auto witness = [1, 4, 7, 8];
+    assert(equal(multiwayUnion(a), witness));
+
+    // multisets
+    double[][] b =
+    [
+        [ 1, 1, 1, 4, 7, 8 ],
+        [ 1, 7 ],
+        [ 1, 7, 7, 8],
+        [ 4 ],
+        [ 7 ],
+    ];
+    assert(equal(multiwayUnion(b), witness));
+
+    double[][] c =
+    [
+        [9, 8, 8, 8, 7, 6],
+        [9, 8, 6],
+        [9, 8, 5]
+    ];
+    auto witness2 = [9, 8, 7, 6, 5];
+    assert(equal(multiwayUnion!"a > b"(c), witness2));
+}
+
+@safe unittest
+{
+    import std.algorithm.setops;
+
+    import std.algorithm.comparison : equal;
+    import std.range.primitives : isForwardRange;
+
+    //sets
+    int[] a = [ 1, 2, 4, 5, 7, 9 ];
+    int[] b = [ 0, 1, 2, 4, 7, 8 ];
+    assert(equal(setDifference(a, b), [5, 9]));
+    static assert(isForwardRange!(typeof(setDifference(a, b))));
+
+    // multisets
+    int[] x = [1, 1, 1, 2, 3];
+    int[] y = [1, 1, 2, 4, 5];
+    auto r = setDifference(x, y);
+    assert(equal(r, [1, 3]));
+    assert(setDifference(r, x).empty);
+}
+
+@safe unittest
+{
+    import std.algorithm.setops;
+
+    import std.algorithm.comparison : equal;
+
+    // sets
+    int[] a = [ 1, 2, 4, 5, 7, 9 ];
+    int[] b = [ 0, 1, 2, 4, 7, 8 ];
+    int[] c = [ 0, 1, 4, 5, 7, 8 ];
+    assert(equal(setIntersection(a, a), a));
+    assert(equal(setIntersection(a, b), [1, 2, 4, 7]));
+    assert(equal(setIntersection(a, b, c), [1, 4, 7]));
+
+    // multisets
+    int[] d = [ 1, 1, 2, 2, 7, 7 ];
+    int[] e = [ 1, 1, 1, 7];
+    assert(equal(setIntersection(a, d), [1, 2, 7]));
+    assert(equal(setIntersection(d, e), [1, 1, 7]));
+}
+
+@safe unittest
+{
+    import std.algorithm.setops;
+
+    import std.algorithm.comparison : equal;
+    import std.range.primitives : isForwardRange;
+
+    // sets
+    int[] a = [ 1, 2, 4, 5, 7, 9 ];
+    int[] b = [ 0, 1, 2, 4, 7, 8 ];
+    assert(equal(setSymmetricDifference(a, b), [0, 5, 8, 9][]));
+    static assert(isForwardRange!(typeof(setSymmetricDifference(a, b))));
+
+    //mutisets
+    int[] c = [1, 1, 1, 1, 2, 2, 2, 4, 5, 6];
+    int[] d = [1, 1, 2, 2, 2, 2, 4, 7, 9];
+    assert(equal(setSymmetricDifference(c, d), setSymmetricDifference(d, c)));
+    assert(equal(setSymmetricDifference(c, d), [1, 1, 2, 5, 6, 7, 9]));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_algorithm_sorting.d b/libphobos/testsuite/libphobos.phobos/std_algorithm_sorting.d
new file mode 100644
index 0000000000000000000000000000000000000000..df4bca62761ab43d6d7710f1ebff6f36f9d29cb0
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_algorithm_sorting.d
@@ -0,0 +1,462 @@
+@safe unittest
+{
+    import std.algorithm.sorting;
+
+    import std.range : assumeSorted;
+    int[] a = [ 1, 2, 3 ];
+    int[] b = [ 4, 0, 6, 5 ];
+    completeSort(assumeSorted(a), b);
+    assert(a == [ 0, 1, 2 ]);
+    assert(b == [ 3, 4, 5, 6 ]);
+}
+
+@safe unittest
+{
+    import std.algorithm.sorting;
+
+    assert([1, 1, 2].isSorted);
+    // strictly monotonic doesn't allow duplicates
+    assert(![1, 1, 2].isStrictlyMonotonic);
+
+    int[] arr = [4, 3, 2, 1];
+    assert(!isSorted(arr));
+    assert(!isStrictlyMonotonic(arr));
+
+    assert(isSorted!"a > b"(arr));
+    assert(isStrictlyMonotonic!"a > b"(arr));
+
+    sort(arr);
+    assert(isSorted(arr));
+    assert(isStrictlyMonotonic(arr));
+}
+
+@safe unittest
+{
+    import std.algorithm.sorting;
+
+    assert(ordered(42, 42, 43));
+    assert(!strictlyOrdered(43, 42, 45));
+    assert(ordered(42, 42, 43));
+    assert(!strictlyOrdered(42, 42, 43));
+    assert(!ordered(43, 42, 45));
+    // Ordered lexicographically
+    assert(ordered("Jane", "Jim", "Joe"));
+    assert(strictlyOrdered("Jane", "Jim", "Joe"));
+    // Incidentally also ordered by length decreasing
+    assert(ordered!((a, b) => a.length > b.length)("Jane", "Jim", "Joe"));
+    // ... but not strictly so: "Jim" and "Joe" have the same length
+    assert(!strictlyOrdered!((a, b) => a.length > b.length)("Jane", "Jim", "Joe"));
+}
+
+@safe unittest
+{
+    import std.algorithm.sorting;
+
+    import std.algorithm.mutation : SwapStrategy;
+    import std.algorithm.searching : count, find;
+    import std.conv : text;
+    import std.range.primitives : empty;
+
+    auto Arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+    auto arr = Arr.dup;
+    static bool even(int a) { return (a & 1) == 0; }
+    // Partition arr such that even numbers come first
+    auto r = partition!(even)(arr);
+    // Now arr is separated in evens and odds.
+    // Numbers may have become shuffled due to instability
+    assert(r == arr[5 .. $]);
+    assert(count!(even)(arr[0 .. 5]) == 5);
+    assert(find!(even)(r).empty);
+
+    // Can also specify the predicate as a string.
+    // Use 'a' as the predicate argument name
+    arr[] = Arr[];
+    r = partition!(q{(a & 1) == 0})(arr);
+    assert(r == arr[5 .. $]);
+
+    // Now for a stable partition:
+    arr[] = Arr[];
+    r = partition!(q{(a & 1) == 0}, SwapStrategy.stable)(arr);
+    // Now arr is [2 4 6 8 10 1 3 5 7 9], and r points to 1
+    assert(arr == [2, 4, 6, 8, 10, 1, 3, 5, 7, 9] && r == arr[5 .. $]);
+
+    // In case the predicate needs to hold its own state, use a delegate:
+    arr[] = Arr[];
+    int x = 3;
+    // Put stuff greater than 3 on the left
+    bool fun(int a) { return a > x; }
+    r = partition!(fun, SwapStrategy.semistable)(arr);
+    // Now arr is [4 5 6 7 8 9 10 2 3 1] and r points to 2
+    assert(arr == [4, 5, 6, 7, 8, 9, 10, 2, 3, 1] && r == arr[7 .. $]);
+}
+
+@safe nothrow unittest
+{
+    import std.algorithm.sorting;
+
+    int[] a = [5, 3, 2, 6, 4, 1, 3, 7];
+    size_t pivot = pivotPartition(a, a.length / 2);
+    import std.algorithm.searching : all;
+    assert(a[0 .. pivot].all!(x => x <= a[pivot]));
+    assert(a[pivot .. $].all!(x => x >= a[pivot]));
+}
+
+@safe unittest
+{
+    import std.algorithm.sorting;
+
+    int[] r = [ 1, 3, 5, 7, 8, 2, 4, ];
+    assert(isPartitioned!"a & 1"(r));
+}
+
+@safe unittest
+{
+    import std.algorithm.sorting;
+
+    auto a = [ 8, 3, 4, 1, 4, 7, 4 ];
+    auto pieces = partition3(a, 4);
+    assert(pieces[0] == [ 1, 3 ]);
+    assert(pieces[1] == [ 4, 4, 4 ]);
+    assert(pieces[2] == [ 8, 7 ]);
+}
+
+@system unittest
+{
+    import std.algorithm.sorting;
+
+    immutable(int[]) arr = [ 2, 3, 1, 5, 0 ];
+    // index using pointers
+    auto index1 = new immutable(int)*[arr.length];
+    makeIndex!("a < b")(arr, index1);
+    assert(isSorted!("*a < *b")(index1));
+    // index using offsets
+    auto index2 = new size_t[arr.length];
+    makeIndex!("a < b")(arr, index2);
+    assert(isSorted!
+        ((size_t a, size_t b){ return arr[a] < arr[b];})
+        (index2));
+}
+
+@safe pure nothrow unittest
+{
+    import std.algorithm.sorting;
+
+    import std.algorithm.comparison : equal;
+    import std.range : retro;
+
+    int[] a = [1, 3, 5];
+    int[] b = [2, 3, 4];
+
+    assert(a.merge(b).equal([1, 2, 3, 3, 4, 5]));
+    assert(a.merge(b).retro.equal([5, 4, 3, 3, 2, 1]));
+}
+
+@safe pure nothrow unittest
+{
+    import std.algorithm.sorting;
+
+    import std.algorithm.comparison : equal;
+    import std.range : retro;
+    import std.traits : CommonType;
+
+    alias S = short;
+    alias I = int;
+    alias D = double;
+
+    S[] a = [1, 2, 3];
+    I[] b = [50, 60];
+    D[] c = [10, 20, 30, 40];
+
+    auto m = merge(a, b, c);
+
+    static assert(is(typeof(m.front) == CommonType!(S, I, D)));
+
+    assert(equal(m, [1, 2, 3, 10, 20, 30, 40, 50, 60]));
+    assert(equal(m.retro, [60, 50, 40, 30, 20, 10, 3, 2, 1]));
+
+    m.popFront();
+    assert(equal(m, [2, 3, 10, 20, 30, 40, 50, 60]));
+    m.popBack();
+    assert(equal(m, [2, 3, 10, 20, 30, 40, 50]));
+    m.popFront();
+    assert(equal(m, [3, 10, 20, 30, 40, 50]));
+    m.popBack();
+    assert(equal(m, [3, 10, 20, 30, 40]));
+    m.popFront();
+    assert(equal(m, [10, 20, 30, 40]));
+    m.popBack();
+    assert(equal(m, [10, 20, 30]));
+    m.popFront();
+    assert(equal(m, [20, 30]));
+    m.popBack();
+    assert(equal(m, [20]));
+    m.popFront();
+    assert(m.empty);
+}
+
+@safe unittest
+{
+    import std.algorithm.sorting;
+
+    import std.algorithm.mutation : SwapStrategy;
+    static struct Point { int x, y; }
+    auto pts1 = [ Point(0, 0), Point(5, 5), Point(0, 1), Point(0, 2) ];
+    auto pts2 = [ Point(0, 0), Point(0, 1), Point(0, 2), Point(5, 5) ];
+    multiSort!("a.x < b.x", "a.y < b.y", SwapStrategy.unstable)(pts1);
+    assert(pts1 == pts2);
+}
+
+@safe pure nothrow unittest
+{
+    import std.algorithm.sorting;
+
+    int[] array = [ 1, 2, 3, 4 ];
+
+    // sort in descending order
+    array.sort!("a > b");
+    assert(array == [ 4, 3, 2, 1 ]);
+
+    // sort in ascending order
+    array.sort();
+    assert(array == [ 1, 2, 3, 4 ]);
+
+    // sort with reusable comparator and chain
+    alias myComp = (x, y) => x > y;
+    assert(array.sort!(myComp).release == [ 4, 3, 2, 1 ]);
+}
+
+@safe unittest
+{
+    import std.algorithm.sorting;
+
+    // Showcase stable sorting
+    import std.algorithm.mutation : SwapStrategy;
+    string[] words = [ "aBc", "a", "abc", "b", "ABC", "c" ];
+    sort!("toUpper(a) < toUpper(b)", SwapStrategy.stable)(words);
+    assert(words == [ "a", "aBc", "abc", "ABC", "b", "c" ]);
+}
+
+@safe unittest
+{
+    import std.algorithm.sorting;
+
+    // Sorting floating-point numbers in presence of NaN
+    double[] numbers = [-0.0, 3.0, -2.0, double.nan, 0.0, -double.nan];
+
+    import std.algorithm.comparison : equal;
+    import std.math.operations : cmp;
+    import std.math.traits : isIdentical;
+
+    sort!((a, b) => cmp(a, b) < 0)(numbers);
+
+    double[] sorted = [-double.nan, -2.0, -0.0, 0.0, 3.0, double.nan];
+    assert(numbers.equal!isIdentical(sorted));
+}
+
+@safe pure unittest
+{
+    import std.algorithm.sorting;
+
+    import std.algorithm.iteration : map;
+    import std.numeric : entropy;
+
+    auto lowEnt = [ 1.0, 0, 0 ],
+         midEnt = [ 0.1, 0.1, 0.8 ],
+        highEnt = [ 0.31, 0.29, 0.4 ];
+    auto arr = new double[][3];
+    arr[0] = midEnt;
+    arr[1] = lowEnt;
+    arr[2] = highEnt;
+
+    schwartzSort!(entropy, "a > b")(arr);
+
+    assert(arr[0] == highEnt);
+    assert(arr[1] == midEnt);
+    assert(arr[2] == lowEnt);
+    assert(isSorted!("a > b")(map!(entropy)(arr)));
+}
+
+@system unittest
+{
+    import std.algorithm.sorting;
+
+    int[] a = [ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ];
+    partialSort(a, 5);
+    assert(a[0 .. 5] == [ 0, 1, 2, 3, 4 ]);
+}
+
+@system unittest
+{
+    import std.algorithm.sorting;
+
+    int[] a = [5, 7, 2, 6, 7];
+    int[] b = [2, 1, 5, 6, 7, 3, 0];
+
+    partialSort(a, b);
+    assert(a == [0, 1, 2, 2, 3]);
+}
+
+@safe unittest
+{
+    import std.algorithm.sorting;
+
+    int[] v = [ 25, 7, 9, 2, 0, 5, 21 ];
+    topN!"a < b"(v, 100);
+    assert(v == [ 25, 7, 9, 2, 0, 5, 21 ]);
+    auto n = 4;
+    topN!((a, b) => a < b)(v, n);
+    assert(v[n] == 9);
+}
+
+@system unittest
+{
+    import std.algorithm.sorting;
+
+    int[] a = [ 5, 7, 2, 6, 7 ];
+    int[] b = [ 2, 1, 5, 6, 7, 3, 0 ];
+    topN(a, b);
+    sort(a);
+    assert(a == [0, 1, 2, 2, 3]);
+}
+
+@system unittest
+{
+    import std.algorithm.sorting;
+
+    import std.typecons : Yes;
+
+    int[] a = [ 10, 16, 2, 3, 1, 5, 0 ];
+    int[] b = new int[3];
+    topNCopy(a, b, Yes.sortOutput);
+    assert(b == [ 0, 1, 2 ]);
+}
+
+@system unittest
+{
+    import std.algorithm.sorting;
+
+    import std.typecons : Yes;
+
+    // Construct index to top 3 elements using numerical indices:
+    int[] a = [ 10, 2, 7, 5, 8, 1 ];
+    int[] index = new int[3];
+    topNIndex(a, index, Yes.sortOutput);
+    assert(index == [5, 1, 3]); // because a[5]==1, a[1]==2, a[3]==5
+
+    // Construct index to top 3 elements using pointer indices:
+    int*[] ptrIndex = new int*[3];
+    topNIndex(a, ptrIndex, Yes.sortOutput);
+    assert(ptrIndex == [ &a[5], &a[1], &a[3] ]);
+}
+
+@safe unittest
+{
+    import std.algorithm.sorting;
+
+    // Step through all permutations of a sorted array in lexicographic order
+    int[] a = [1,2,3];
+    assert(nextPermutation(a) == true);
+    assert(a == [1,3,2]);
+    assert(nextPermutation(a) == true);
+    assert(a == [2,1,3]);
+    assert(nextPermutation(a) == true);
+    assert(a == [2,3,1]);
+    assert(nextPermutation(a) == true);
+    assert(a == [3,1,2]);
+    assert(nextPermutation(a) == true);
+    assert(a == [3,2,1]);
+    assert(nextPermutation(a) == false);
+    assert(a == [1,2,3]);
+}
+
+@safe unittest
+{
+    import std.algorithm.sorting;
+
+    // Step through permutations of an array containing duplicate elements:
+    int[] a = [1,1,2];
+    assert(nextPermutation(a) == true);
+    assert(a == [1,2,1]);
+    assert(nextPermutation(a) == true);
+    assert(a == [2,1,1]);
+    assert(nextPermutation(a) == false);
+    assert(a == [1,1,2]);
+}
+
+@safe unittest
+{
+    import std.algorithm.sorting;
+
+    // Step through even permutations of a sorted array in lexicographic order
+    int[] a = [1,2,3];
+    assert(nextEvenPermutation(a) == true);
+    assert(a == [2,3,1]);
+    assert(nextEvenPermutation(a) == true);
+    assert(a == [3,1,2]);
+    assert(nextEvenPermutation(a) == false);
+    assert(a == [1,2,3]);
+}
+
+@safe unittest
+{
+    import std.algorithm.sorting;
+
+    import std.math.algebraic : sqrt;
+
+    // Print the 60 vertices of a uniform truncated icosahedron (soccer ball)
+    enum real Phi = (1.0 + sqrt(5.0)) / 2.0;    // Golden ratio
+    real[][] seeds = [
+        [0.0, 1.0, 3.0*Phi],
+        [1.0, 2.0+Phi, 2.0*Phi],
+        [Phi, 2.0, Phi^^3]
+    ];
+    size_t n;
+    foreach (seed; seeds)
+    {
+        // Loop over even permutations of each seed
+        do
+        {
+            // Loop over all sign changes of each permutation
+            size_t i;
+            do
+            {
+                // Generate all possible sign changes
+                for (i=0; i < seed.length; i++)
+                {
+                    if (seed[i] != 0.0)
+                    {
+                        seed[i] = -seed[i];
+                        if (seed[i] < 0.0)
+                            break;
+                    }
+                }
+                n++;
+            } while (i < seed.length);
+        } while (nextEvenPermutation(seed));
+    }
+    assert(n == 60);
+}
+
+pure @safe unittest
+{
+    import std.algorithm.sorting;
+
+    auto src = [0, 1, 2, 3, 4, 5, 6];
+    auto rslt = [4, 0, 6, 2, 1, 3, 5];
+
+    src = nthPermutation(src, 2982);
+    assert(src == rslt);
+}
+
+pure @safe unittest
+{
+    import std.algorithm.sorting;
+
+    auto src = [0, 1, 2, 3, 4, 5, 6];
+    auto rslt = [4, 0, 6, 2, 1, 3, 5];
+
+    bool worked = nthPermutationImpl(src, 2982);
+    assert(worked);
+    assert(src == rslt);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_array.d b/libphobos/testsuite/libphobos.phobos/std_array.d
new file mode 100644
index 0000000000000000000000000000000000000000..1370d089943371b2ab0aa8e326892761e1d0b4e8
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_array.d
@@ -0,0 +1,560 @@
+@safe pure nothrow unittest
+{
+    import std.array;
+
+    auto a = array([1, 2, 3, 4, 5][]);
+    assert(a == [ 1, 2, 3, 4, 5 ]);
+}
+
+@safe pure nothrow unittest
+{
+    import std.array;
+
+    import std.range.primitives : isRandomAccessRange;
+    import std.traits : isAutodecodableString;
+
+    // note that if autodecoding is turned off, `array` will not transcode these.
+    static if (isAutodecodableString!string)
+        assert("Hello D".array == "Hello D"d);
+    else
+        assert("Hello D".array == "Hello D");
+
+    static if (isAutodecodableString!wstring)
+        assert("Hello D"w.array == "Hello D"d);
+    else
+        assert("Hello D"w.array == "Hello D"w);
+
+    static assert(isRandomAccessRange!dstring == true);
+}
+
+@safe pure unittest
+{
+    import std.array;
+
+    import std.range : repeat, zip;
+    import std.typecons : tuple;
+    import std.range.primitives : autodecodeStrings;
+    auto a = assocArray(zip([0, 1, 2], ["a", "b", "c"])); // aka zipMap
+    static assert(is(typeof(a) == string[int]));
+    assert(a == [0:"a", 1:"b", 2:"c"]);
+
+    auto b = assocArray([ tuple("foo", "bar"), tuple("baz", "quux") ]);
+    static assert(is(typeof(b) == string[string]));
+    assert(b == ["foo":"bar", "baz":"quux"]);
+
+    static if (autodecodeStrings)
+        alias achar = dchar;
+    else
+        alias achar = immutable(char);
+    auto c = assocArray("ABCD", true.repeat);
+    static assert(is(typeof(c) == bool[achar]));
+    bool[achar] expected = ['D':true, 'A':true, 'B':true, 'C':true];
+    assert(c == expected);
+}
+
+@safe pure nothrow unittest
+{
+    import std.array;
+
+    import std.algorithm.sorting : sort;
+    import std.typecons : tuple, Tuple;
+
+    auto aa = ["a": 1, "b": 2, "c": 3];
+    Tuple!(string, int)[] pairs;
+
+    // Iteration over key/value pairs.
+    foreach (pair; aa.byPair)
+    {
+        if (pair.key == "b")
+            pairs ~= tuple("B", pair.value);
+        else
+            pairs ~= pair;
+    }
+
+    // Iteration order is implementation-dependent, so we should sort it to get
+    // a fixed order.
+    pairs.sort();
+    assert(pairs == [
+        tuple("B", 2),
+        tuple("a", 1),
+        tuple("c", 3)
+    ]);
+}
+
+@system nothrow pure unittest
+{
+    import std.array;
+
+    double[] arr = uninitializedArray!(double[])(100);
+    assert(arr.length == 100);
+
+    double[][] matrix = uninitializedArray!(double[][])(42, 31);
+    assert(matrix.length == 42);
+    assert(matrix[0].length == 31);
+
+    char*[] ptrs = uninitializedArray!(char*[])(100);
+    assert(ptrs.length == 100);
+}
+
+@safe pure nothrow unittest
+{
+    import std.array;
+
+    import std.algorithm.comparison : equal;
+    import std.range : repeat;
+
+    auto arr = minimallyInitializedArray!(int[])(42);
+    assert(arr.length == 42);
+
+    // Elements aren't necessarily initialized to 0, so don't do this:
+    // assert(arr.equal(0.repeat(42)));
+    // If that is needed, initialize the array normally instead:
+    auto arr2 = new int[42];
+    assert(arr2.equal(0.repeat(42)));
+}
+
+@safe pure nothrow unittest
+{
+    import std.array;
+
+    int[] a = [ 10, 11, 12, 13, 14 ];
+    int[] b = a[1 .. 3];
+    assert(overlap(a, b) == [ 11, 12 ]);
+    b = b.dup;
+    // overlap disappears even though the content is the same
+    assert(overlap(a, b).empty);
+
+    static test()() @nogc
+    {
+        auto a = "It's three o'clock"d;
+        auto b = a[5 .. 10];
+        return b.overlap(a);
+    }
+
+    //works at compile-time
+    static assert(test == "three"d);
+}
+
+@safe pure nothrow unittest
+{
+    import std.array;
+
+    import std.meta : AliasSeq;
+
+    // can be used as an alternative implementation of overlap that returns
+    // `true` or `false` instead of a slice of the overlap
+    bool isSliceOf(T)(const scope T[] part, const scope T[] whole)
+    {
+        return part.overlap(whole) is part;
+    }
+
+    auto x = [1, 2, 3, 4, 5];
+
+    assert(isSliceOf(x[3..$], x));
+    assert(isSliceOf(x[], x));
+    assert(!isSliceOf(x, x[3..$]));
+    assert(!isSliceOf([7, 8], x));
+    assert(isSliceOf(null, x));
+
+    // null is a slice of itself
+    assert(isSliceOf(null, null));
+
+    foreach (T; AliasSeq!(int[], const(int)[], immutable(int)[], const int[], immutable int[]))
+    {
+        T a = [1, 2, 3, 4, 5];
+        T b = a;
+        T c = a[1 .. $];
+        T d = a[0 .. 1];
+        T e = null;
+
+        assert(isSliceOf(a, a));
+        assert(isSliceOf(b, a));
+        assert(isSliceOf(a, b));
+
+        assert(isSliceOf(c, a));
+        assert(isSliceOf(c, b));
+        assert(!isSliceOf(a, c));
+        assert(!isSliceOf(b, c));
+
+        assert(isSliceOf(d, a));
+        assert(isSliceOf(d, b));
+        assert(!isSliceOf(a, d));
+        assert(!isSliceOf(b, d));
+
+        assert(isSliceOf(e, a));
+        assert(isSliceOf(e, b));
+        assert(isSliceOf(e, c));
+        assert(isSliceOf(e, d));
+
+        //verifies R-value compatibilty
+        assert(!isSliceOf(a[$ .. $], a));
+        assert(isSliceOf(a[0 .. 0], a));
+        assert(isSliceOf(a, a[0.. $]));
+        assert(isSliceOf(a[0 .. $], a));
+    }
+}
+
+@safe pure unittest
+{
+    import std.array;
+
+    int[] a = [ 1, 2, 3, 4 ];
+    a.insertInPlace(2, [ 1, 2 ]);
+    assert(a == [ 1, 2, 1, 2, 3, 4 ]);
+    a.insertInPlace(3, 10u, 11);
+    assert(a == [ 1, 2, 1, 10, 11, 2, 3, 4]);
+
+    union U
+    {
+        float a = 3.0;
+        int b;
+    }
+
+    U u1 = { b : 3 };
+    U u2 = { b : 4 };
+    U u3 = { b : 5 };
+    U[] unionArr = [u2, u3];
+    unionArr.insertInPlace(2, [u1]);
+    assert(unionArr == [u2, u3, u1]);
+    unionArr.insertInPlace(0, [u3, u2]);
+    assert(unionArr == [u3, u2, u2, u3, u1]);
+
+    static class C
+    {
+        int a;
+        float b;
+
+        this(int a, float b) { this.a = a; this.b = b; }
+    }
+
+    C c1 = new C(42, 1.0);
+    C c2 = new C(0, 0.0);
+    C c3 = new C(int.max, float.init);
+
+    C[] classArr = [c1, c2, c3];
+    insertInPlace(classArr, 3, [c2, c3]);
+    C[5] classArr1 = classArr;
+    assert(classArr1 == [c1, c2, c3, c2, c3]);
+    insertInPlace(classArr, 0, c3, c1);
+    C[7] classArr2 = classArr;
+    assert(classArr2 == [c3, c1, c1, c2, c3, c2, c3]);
+}
+
+@safe pure nothrow unittest
+{
+    import std.array;
+
+    auto a = [1, 2, 3, 4, 5];
+    auto b = a[0 .. 2];
+
+    assert(a.sameHead(b));
+}
+
+@safe pure nothrow unittest
+{
+    import std.array;
+
+    auto a = [1, 2, 3, 4, 5];
+    auto b = a[3..$];
+
+    assert(a.sameTail(b));
+}
+
+@safe unittest
+{
+    import std.array;
+
+    auto a = "abc";
+    auto s = replicate(a, 3);
+
+    assert(s == "abcabcabc");
+
+    auto b = [1, 2, 3];
+    auto c = replicate(b, 3);
+
+    assert(c == [1, 2, 3, 1, 2, 3, 1, 2, 3]);
+
+    auto d = replicate(b, 0);
+
+    assert(d == []);
+}
+
+@safe unittest
+{
+    import std.array;
+
+    import std.uni : isWhite;
+    assert("Learning,D,is,fun".split(",") == ["Learning", "D", "is", "fun"]);
+    assert("Learning D is fun".split!isWhite == ["Learning", "D", "is", "fun"]);
+    assert("Learning D is fun".split(" D ") == ["Learning", "is fun"]);
+}
+
+@safe unittest
+{
+    import std.array;
+
+    string str = "Hello World!";
+    assert(str.split == ["Hello", "World!"]);
+
+    string str2 = "Hello\t\tWorld\t!";
+    assert(str2.split == ["Hello", "World", "!"]);
+}
+
+@safe unittest
+{
+    import std.array;
+
+    assert(split("hello world") == ["hello","world"]);
+    assert(split("192.168.0.1", ".") == ["192", "168", "0", "1"]);
+
+    auto a = split([1, 2, 3, 4, 5, 1, 2, 3, 4, 5], [2, 3]);
+    assert(a == [[1], [4, 5, 1], [4, 5]]);
+}
+
+@safe pure nothrow unittest
+{
+    import std.array;
+
+    assert(join(["hello", "silly", "world"], " ") == "hello silly world");
+    assert(join(["hello", "silly", "world"]) == "hellosillyworld");
+
+    assert(join([[1, 2, 3], [4, 5]], [72, 73]) == [1, 2, 3, 72, 73, 4, 5]);
+    assert(join([[1, 2, 3], [4, 5]]) == [1, 2, 3, 4, 5]);
+
+    const string[] arr = ["apple", "banana"];
+    assert(arr.join(",") == "apple,banana");
+    assert(arr.join() == "applebanana");
+}
+
+@safe unittest
+{
+    import std.array;
+
+    assert("Hello Wörld".replace("o Wö", "o Wo") == "Hello World");
+    assert("Hello Wörld".replace("l", "h") == "Hehho Wörhd");
+}
+
+@safe unittest
+{
+    import std.array;
+
+    size_t changed = 0;
+    assert("Hello Wörld".replace("o Wö", "o Wo", changed) == "Hello World");
+    assert(changed == 1);
+
+    changed = 0;
+    assert("Hello Wörld".replace("l", "h", changed) == "Hehho Wörhd");
+    import std.stdio : writeln;
+    writeln(changed);
+    assert(changed == 3);
+}
+
+@safe unittest
+{
+    import std.array;
+
+    auto arr = [1, 2, 3, 4, 5];
+    auto from = [2, 3];
+    auto to = [4, 6];
+    auto sink = appender!(int[])();
+
+    replaceInto(sink, arr, from, to);
+
+    assert(sink.data == [1, 4, 6, 4, 5]);
+}
+
+@safe unittest
+{
+    import std.array;
+
+    auto arr = [1, 2, 3, 4, 5];
+    auto from = [2, 3];
+    auto to = [4, 6];
+    auto sink = appender!(int[])();
+
+    size_t changed = 0;
+    replaceInto(sink, arr, from, to, changed);
+
+    assert(sink.data == [1, 4, 6, 4, 5]);
+    assert(changed == 1);
+}
+
+@safe unittest
+{
+    import std.array;
+
+    auto a = [ 1, 2, 3, 4 ];
+    auto b = a.replace(1, 3, [ 9, 9, 9 ]);
+    assert(a == [ 1, 2, 3, 4 ]);
+    assert(b == [ 1, 9, 9, 9, 4 ]);
+}
+
+@safe unittest
+{
+    import std.array;
+
+    int[] a = [1, 4, 5];
+    replaceInPlace(a, 1u, 2u, [2, 3, 4]);
+    assert(a == [1, 2, 3, 4, 5]);
+    replaceInPlace(a, 1u, 2u, cast(int[])[]);
+    assert(a == [1, 3, 4, 5]);
+    replaceInPlace(a, 1u, 3u, a[2 .. 4]);
+    assert(a == [1, 4, 5, 5]);
+}
+
+@safe unittest
+{
+    import std.array;
+
+    auto a = [1, 2, 2, 3, 4, 5];
+    auto b = a.replaceFirst([2], [1337]);
+    assert(b == [1, 1337, 2, 3, 4, 5]);
+
+    auto s = "This is a foo foo list";
+    auto r = s.replaceFirst("foo", "silly");
+    assert(r == "This is a silly foo list");
+}
+
+@safe unittest
+{
+    import std.array;
+
+    auto a = [1, 2, 2, 3, 4, 5];
+    auto b = a.replaceLast([2], [1337]);
+    assert(b == [1, 2, 1337, 3, 4, 5]);
+
+    auto s = "This is a foo foo list";
+    auto r = s.replaceLast("foo", "silly");
+    assert(r == "This is a foo silly list", r);
+}
+
+@safe unittest
+{
+    import std.array;
+
+    auto a = [1, 2, 3, 4, 5];
+    auto b = replaceSlice(a, a[1 .. 4], [0, 0, 0]);
+
+    assert(b == [1, 0, 0, 0, 5]);
+}
+
+@safe pure nothrow unittest
+{
+    import std.array;
+
+    auto app = appender!string();
+    string b = "abcdefg";
+    foreach (char c; b)
+        app.put(c);
+    assert(app[] == "abcdefg");
+
+    int[] a = [ 1, 2 ];
+    auto app2 = appender(a);
+    app2.put(3);
+    assert(app2.length == 3);
+    app2.put([ 4, 5, 6 ]);
+    assert(app2[] == [ 1, 2, 3, 4, 5, 6 ]);
+}
+
+@safe pure nothrow unittest
+{
+    import std.array;
+
+    int[] a = [1, 2];
+    auto app2 = appender(&a);
+    assert(app2[] == [1, 2]);
+    assert(a == [1, 2]);
+    app2 ~= 3;
+    assert(app2.length == 3);
+    app2 ~= [4, 5, 6];
+    assert(app2[] == [1, 2, 3, 4, 5, 6]);
+    assert(a == [1, 2, 3, 4, 5, 6]);
+
+    app2.reserve(5);
+    assert(app2.capacity >= 5);
+}
+
+@safe pure nothrow unittest
+{
+    import std.array;
+
+    auto w = appender!string;
+    // pre-allocate space for at least 10 elements (this avoids costly reallocations)
+    w.reserve(10);
+    assert(w.capacity >= 10);
+
+    w.put('a'); // single elements
+    w.put("bc"); // multiple elements
+
+    // use the append syntax
+    w ~= 'd';
+    w ~= "ef";
+
+    assert(w[] == "abcdef");
+}
+
+@safe pure nothrow unittest
+{
+    import std.array;
+
+    int[] a = [1, 2];
+    auto app2 = appender(&a);
+    assert(app2[] == [1, 2]);
+    assert(a == [1, 2]);
+    app2 ~= 3;
+    app2 ~= [4, 5, 6];
+    assert(app2[] == [1, 2, 3, 4, 5, 6]);
+    assert(a == [1, 2, 3, 4, 5, 6]);
+
+    app2.reserve(5);
+    assert(app2.capacity >= 5);
+}
+
+nothrow pure @safe @nogc unittest
+{
+    import std.array;
+
+    auto a = [0, 1].staticArray;
+    static assert(is(typeof(a) == int[2]));
+    assert(a == [0, 1]);
+}
+
+nothrow pure @safe @nogc unittest
+{
+    import std.array;
+
+    auto b = [0, 1].staticArray!long;
+    static assert(is(typeof(b) == long[2]));
+    assert(b == [0, 1]);
+}
+
+nothrow pure @safe @nogc unittest
+{
+    import std.array;
+
+    import std.range : iota;
+
+    auto input = 3.iota;
+    auto a = input.staticArray!2;
+    static assert(is(typeof(a) == int[2]));
+    assert(a == [0, 1]);
+    auto b = input.staticArray!(long[4]);
+    static assert(is(typeof(b) == long[4]));
+    assert(b == [0, 1, 2, 0]);
+}
+
+nothrow pure @safe @nogc unittest
+{
+    import std.array;
+
+    import std.range : iota;
+
+    enum a = staticArray!(2.iota);
+    static assert(is(typeof(a) == int[2]));
+    assert(a == [0, 1]);
+
+    enum b = staticArray!(long, 2.iota);
+    static assert(is(typeof(b) == long[2]));
+    assert(b == [0, 1]);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_ascii.d b/libphobos/testsuite/libphobos.phobos/std_ascii.d
new file mode 100644
index 0000000000000000000000000000000000000000..dcdc5fd28c48857eb6a95956c72583b9ccd8576d
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_ascii.d
@@ -0,0 +1,262 @@
+@safe unittest
+{
+    import std.ascii;
+
+    import std.conv : to;
+
+    assert(42.to!string(16, LetterCase.upper) == "2A");
+    assert(42.to!string(16, LetterCase.lower) == "2a");
+}
+
+@safe unittest
+{
+    import std.ascii;
+
+    import std.digest.hmac : hmac;
+    import std.digest : toHexString;
+    import std.digest.sha : SHA1;
+    import std.string : representation;
+
+    const sha1HMAC = "A very long phrase".representation
+        .hmac!SHA1("secret".representation)
+        .toHexString!(LetterCase.lower);
+    assert(sha1HMAC == "49f2073c7bf58577e8c9ae59fe8cfd37c9ab94e5");
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.ascii;
+
+    import std.algorithm.comparison, std.algorithm.searching, std.range, std.traits;
+
+    // Because all ASCII characters fit in char, so do these
+    static assert(ControlChar.ack.sizeof == 1);
+
+    // All control characters except del are in row starting from 0
+    static assert(EnumMembers!ControlChar.only.until(ControlChar.del).equal(iota(32)));
+
+    static assert(ControlChar.nul == '\0');
+    static assert(ControlChar.bel == '\a');
+    static assert(ControlChar.bs  == '\b');
+    static assert(ControlChar.ff  == '\f');
+    static assert(ControlChar.lf  == '\n');
+    static assert(ControlChar.cr  == '\r');
+    static assert(ControlChar.tab == '\t');
+    static assert(ControlChar.vt  == '\v');
+}
+
+@safe pure nothrow unittest
+{
+    import std.ascii;
+
+    import std.conv;
+    //Control character table can be used in place of hexcodes.
+    with (ControlChar) assert(text("Phobos", us, "Deimos", us, "Tango", rs) == "Phobos\x1FDeimos\x1FTango\x1E");
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.ascii;
+
+    assert( isAlphaNum('A'));
+    assert( isAlphaNum('1'));
+    assert(!isAlphaNum('#'));
+
+    // N.B.: does not return true for non-ASCII Unicode alphanumerics:
+    assert(!isAlphaNum('á'));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.ascii;
+
+    assert( isAlpha('A'));
+    assert(!isAlpha('1'));
+    assert(!isAlpha('#'));
+
+    // N.B.: does not return true for non-ASCII Unicode alphabetic characters:
+    assert(!isAlpha('á'));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.ascii;
+
+    assert( isLower('a'));
+    assert(!isLower('A'));
+    assert(!isLower('#'));
+
+    // N.B.: does not return true for non-ASCII Unicode lowercase letters
+    assert(!isLower('á'));
+    assert(!isLower('Á'));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.ascii;
+
+    assert( isUpper('A'));
+    assert(!isUpper('a'));
+    assert(!isUpper('#'));
+
+    // N.B.: does not return true for non-ASCII Unicode uppercase letters
+    assert(!isUpper('á'));
+    assert(!isUpper('Á'));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.ascii;
+
+    assert( isDigit('3'));
+    assert( isDigit('8'));
+    assert(!isDigit('B'));
+    assert(!isDigit('#'));
+
+    // N.B.: does not return true for non-ASCII Unicode numbers
+    assert(!isDigit('0')); // full-width digit zero (U+FF10)
+    assert(!isDigit('ï¼”')); // full-width digit four (U+FF14)
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.ascii;
+
+    assert( isOctalDigit('0'));
+    assert( isOctalDigit('7'));
+    assert(!isOctalDigit('8'));
+    assert(!isOctalDigit('A'));
+    assert(!isOctalDigit('#'));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.ascii;
+
+    assert( isHexDigit('0'));
+    assert( isHexDigit('A'));
+    assert( isHexDigit('f')); // lowercase hex digits are accepted
+    assert(!isHexDigit('g'));
+    assert(!isHexDigit('G'));
+    assert(!isHexDigit('#'));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.ascii;
+
+    assert( isWhite(' '));
+    assert( isWhite('\t'));
+    assert( isWhite('\n'));
+    assert(!isWhite('1'));
+    assert(!isWhite('a'));
+    assert(!isWhite('#'));
+
+    // N.B.: Does not return true for non-ASCII Unicode whitespace characters.
+    static import std.uni;
+    assert(std.uni.isWhite('\u00A0'));
+    assert(!isWhite('\u00A0')); // std.ascii.isWhite
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.ascii;
+
+    assert( isControl('\0'));
+    assert( isControl('\022'));
+    assert( isControl('\n')); // newline is both whitespace and control
+    assert(!isControl(' '));
+    assert(!isControl('1'));
+    assert(!isControl('a'));
+    assert(!isControl('#'));
+
+    // N.B.: non-ASCII Unicode control characters are not recognized:
+    assert(!isControl('\u0080'));
+    assert(!isControl('\u2028'));
+    assert(!isControl('\u2029'));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.ascii;
+
+    assert( isPunctuation('.'));
+    assert( isPunctuation(','));
+    assert( isPunctuation(':'));
+    assert( isPunctuation('!'));
+    assert( isPunctuation('#'));
+    assert( isPunctuation('~'));
+    assert( isPunctuation('+'));
+    assert( isPunctuation('_'));
+
+    assert(!isPunctuation('1'));
+    assert(!isPunctuation('a'));
+    assert(!isPunctuation(' '));
+    assert(!isPunctuation('\n'));
+    assert(!isPunctuation('\0'));
+
+    // N.B.: Non-ASCII Unicode punctuation characters are not recognized.
+    assert(!isPunctuation('\u2012')); // (U+2012 = en-dash)
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.ascii;
+
+    assert( isGraphical('1'));
+    assert( isGraphical('a'));
+    assert( isGraphical('#'));
+    assert(!isGraphical(' ')); // whitespace is not graphical
+    assert(!isGraphical('\n'));
+    assert(!isGraphical('\0'));
+
+    // N.B.: Unicode graphical characters are not regarded as such.
+    assert(!isGraphical('á'));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.ascii;
+
+    assert( isPrintable(' '));  // whitespace is printable
+    assert( isPrintable('1'));
+    assert( isPrintable('a'));
+    assert( isPrintable('#'));
+    assert(!isPrintable('\0')); // control characters are not printable
+
+    // N.B.: Printable non-ASCII Unicode characters are not recognized.
+    assert(!isPrintable('á'));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.ascii;
+
+    assert( isASCII('a'));
+    assert(!isASCII('á'));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.ascii;
+
+    assert(toLower('a') == 'a');
+    assert(toLower('A') == 'a');
+    assert(toLower('#') == '#');
+
+    // N.B.: Non-ASCII Unicode uppercase letters are not converted.
+    assert(toLower('Á') == 'Á');
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.ascii;
+
+    assert(toUpper('a') == 'A');
+    assert(toUpper('A') == 'A');
+    assert(toUpper('#') == '#');
+
+    // N.B.: Non-ASCII Unicode lowercase letters are not converted.
+    assert(toUpper('á') == 'á');
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_base64.d b/libphobos/testsuite/libphobos.phobos/std_base64.d
new file mode 100644
index 0000000000000000000000000000000000000000..3a49eeaacaaa298fa887b4426d5dbf600bda2ae7
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_base64.d
@@ -0,0 +1,180 @@
+pure @safe unittest
+{
+    import std.base64;
+
+    ubyte[] data = [0x83, 0xd7, 0x30, 0x7a, 0x01, 0x3f];
+    assert(Base64.encode(data) == "g9cwegE/");
+    assert(Base64.decode("g9cwegE/") == data);
+}
+
+pure @safe unittest
+{
+    import std.base64;
+
+    ubyte[] data = [0x83, 0xd7, 0x30, 0x7a, 0x01, 0x3f];
+    assert(Base64URL.encode(data) == "g9cwegE_");
+    assert(Base64URL.decode("g9cwegE_") == data);
+}
+
+pure @safe unittest
+{
+    import std.base64;
+
+    ubyte[] data = [0x83, 0xd7, 0x30, 0x7b, 0xef];
+    assert(Base64URLNoPadding.encode(data) == "g9cwe-8");
+    assert(Base64URLNoPadding.decode("g9cwe-8") == data);
+}
+
+@safe unittest
+{
+    import std.base64;
+
+        ubyte[] data = [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e];
+
+        // Allocate a buffer large enough to hold the encoded string.
+        auto buf = new char[Base64.encodeLength(data.length)];
+
+        Base64.encode(data, buf);
+        assert(buf == "Gis8TV1u");
+    
+}
+
+@nogc nothrow @safe unittest
+{
+    import std.base64;
+
+        ubyte[6] data = [0x83, 0xd7, 0x30, 0x7a, 0x01, 0x3f];
+        char[32] buffer;    // much bigger than necessary
+
+        // Just to be sure...
+        auto encodedLength = Base64.encodeLength(data.length);
+        assert(buffer.length >= encodedLength);
+
+        // encode() returns a slice to the provided buffer.
+        auto encoded = Base64.encode(data[], buffer[]);
+        assert(encoded is buffer[0 .. encodedLength]);
+        assert(encoded == "g9cwegE/");
+    
+}
+
+@safe pure nothrow unittest
+{
+    import std.base64;
+
+        import std.array : appender;
+
+        auto output = appender!string();
+        ubyte[] data = [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e];
+
+        // This overload of encode() returns the number of calls to the output
+        // range's put method.
+        assert(Base64.encode(data, output) == 8);
+        assert(output.data == "Gis8TV1u");
+    
+}
+
+@safe unittest
+{
+    import std.base64;
+
+        ubyte[] data = [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e];
+        assert(Base64.encode(data) == "Gis8TV1u");
+    
+}
+
+@safe unittest
+{
+    import std.base64;
+
+        auto encoded = "Gis8TV1u";
+
+        // Allocate a sufficiently large buffer to hold to decoded result.
+        auto buffer = new ubyte[Base64.decodeLength(encoded.length)];
+
+        Base64.decode(encoded, buffer);
+        assert(buffer == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]);
+    
+}
+
+@safe unittest
+{
+    import std.base64;
+
+        auto encoded = "Gis8TV1u";
+        ubyte[32] buffer;   // much bigger than necessary
+
+        // Just to be sure...
+        auto decodedLength = Base64.decodeLength(encoded.length);
+        assert(buffer.length >= decodedLength);
+
+        // decode() returns a slice of the given buffer.
+        auto decoded = Base64.decode(encoded, buffer[]);
+        assert(decoded is buffer[0 .. decodedLength]);
+        assert(decoded == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]);
+    
+}
+
+@system unittest
+{
+    import std.base64;
+
+        struct OutputRange
+        {
+            ubyte[] result;
+            void put(ubyte b) { result ~= b; }
+        }
+        OutputRange output;
+
+        // This overload of decode() returns the number of calls to put().
+        assert(Base64.decode("Gis8TV1u", output) == 6);
+        assert(output.result == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]);
+    
+}
+
+@safe unittest
+{
+    import std.base64;
+
+        auto data = "Gis8TV1u";
+        assert(Base64.decode(data) == [0x1a, 0x2b, 0x3c, 0x4d, 0x5d, 0x6e]);
+    
+}
+
+@safe pure unittest
+{
+    import std.base64;
+
+        import std.algorithm.comparison : equal;
+        string encoded =
+            "VGhvdSBzaGFsdCBuZXZlciBjb250aW51ZSBhZnRlciBhc3NlcnRpbmcgbnVsbA==";
+
+        assert(Base64.decoder(encoded)
+            .equal("Thou shalt never continue after asserting null"));
+    
+}
+
+@safe unittest
+{
+    import std.base64;
+
+    import std.string : representation;
+
+    // pre-defined: alias Base64 = Base64Impl!('+', '/');
+    ubyte[] emptyArr;
+    assert(Base64.encode(emptyArr) == "");
+    assert(Base64.encode("f".representation) == "Zg==");
+    assert(Base64.encode("foo".representation) == "Zm9v");
+
+    alias Base64Re = Base64Impl!('!', '=', Base64.NoPadding);
+    assert(Base64Re.encode("f".representation) == "Zg");
+    assert(Base64Re.encode("foo".representation) == "Zm9v");
+}
+
+@safe unittest
+{
+    import std.base64;
+
+    import std.exception : assertThrown;
+    assertThrown!Base64Exception(Base64.decode("ab|c"));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_bigint.d b/libphobos/testsuite/libphobos.phobos/std_bigint.d
new file mode 100644
index 0000000000000000000000000000000000000000..627d15c04698d555b9f7d683461dfcf48c333caf
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_bigint.d
@@ -0,0 +1,465 @@
+pure @safe unittest
+{
+    import std.bigint;
+
+        ubyte[] magnitude = [1, 2, 3, 4, 5, 6];
+        auto b1 = BigInt(false, magnitude);
+        assert(cast(long) b1 == 0x01_02_03_04_05_06L);
+        auto b2 = BigInt(true, magnitude);
+        assert(cast(long) b2 == -0x01_02_03_04_05_06L);
+    
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+        ulong data = 1_000_000_000_000;
+        auto bigData = BigInt(data);
+        assert(bigData == BigInt("1_000_000_000_000"));
+    
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+        const(BigInt) b1 = BigInt("1_234_567_890");
+        BigInt b2 = BigInt(b1);
+        assert(b2 == BigInt("1_234_567_890"));
+    
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+        auto b = BigInt("123");
+        b = 456;
+        assert(b == BigInt("456"));
+    
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+        auto b1 = BigInt("123");
+        auto b2 = BigInt("456");
+        b2 = b1;
+        assert(b2 == BigInt("123"));
+    
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+        auto b = BigInt("1_000_000_000");
+
+        b += 12345;
+        assert(b == BigInt("1_000_012_345"));
+
+        b /= 5;
+        assert(b == BigInt("200_002_469"));
+    
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+        auto x = BigInt("123");
+        auto y = BigInt("321");
+        x += y;
+        assert(x == BigInt("444"));
+    
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+        auto x = BigInt("123");
+        auto y = BigInt("456");
+        BigInt z = x * y;
+        assert(z == BigInt("56088"));
+    
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+        auto x = BigInt("123");
+        x *= 300;
+        assert(x == BigInt("36900"));
+    
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+        auto  x  = BigInt("1_000_000_500");
+        long  l  = 1_000_000L;
+        ulong ul = 2_000_000UL;
+        int   i  = 500_000;
+        short s  = 30_000;
+
+        assert(is(typeof(x % l)  == long)   && x % l  == 500L);
+        assert(is(typeof(x % ul) == BigInt) && x % ul == BigInt(500));
+        assert(is(typeof(x % i)  == int)    && x % i  == 500);
+        assert(is(typeof(x % s)  == int)    && x % s  == 10500);
+    
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+        auto x = BigInt("100");
+        BigInt y = 123 + x;
+        assert(y == BigInt("223"));
+
+        BigInt z = 123 - x;
+        assert(z == BigInt("23"));
+
+        // Dividing a built-in integer type by BigInt always results in
+        // something that fits in a built-in type, so the built-in type is
+        // returned, not BigInt.
+        assert(is(typeof(1000 / x) == int));
+        assert(1000 / x == 10);
+    
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+        auto x = BigInt("1234");
+        assert(-x == BigInt("-1234"));
+
+        ++x;
+        assert(x == BigInt("1235"));
+    
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+        // Note that when comparing a BigInt to a float or double the
+        // full precision of the BigInt is always considered, unlike
+        // when comparing an int to a float or a long to a double.
+        assert(BigInt(123456789) != cast(float) 123456789);
+    
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+        // Non-zero values are regarded as true
+        auto x = BigInt("1");
+        auto y = BigInt("10");
+        assert(x);
+        assert(y);
+
+        // Zero value is regarded as false
+        auto z = BigInt("0");
+        assert(!z);
+    
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+        import std.conv : to, ConvOverflowException;
+        import std.exception : assertThrown;
+
+        assert(BigInt("0").to!int == 0);
+
+        assert(BigInt("0").to!ubyte == 0);
+        assert(BigInt("255").to!ubyte == 255);
+        assertThrown!ConvOverflowException(BigInt("256").to!ubyte);
+        assertThrown!ConvOverflowException(BigInt("-1").to!ubyte);
+    
+}
+
+@system unittest
+{
+    import std.bigint;
+
+        assert(cast(float)  BigInt("35540592535949172786332045140593475584")
+                == 35540592535949172786332045140593475584.0f);
+        assert(cast(double) BigInt("35540601499647381470685035515422441472")
+                == 35540601499647381470685035515422441472.0);
+        assert(cast(real)   BigInt("35540601499647381470685035515422441472")
+                == 35540601499647381470685035515422441472.0L);
+
+        assert(cast(float)  BigInt("-0x1345_6780_0000_0000_0000_0000_0000") == -0x1.3456_78p+108f       );
+        assert(cast(double) BigInt("-0x1345_678a_bcde_f000_0000_0000_0000") == -0x1.3456_78ab_cdefp+108 );
+        assert(cast(real)   BigInt("-0x1345_678a_bcde_f000_0000_0000_0000") == -0x1.3456_78ab_cdefp+108L);
+    
+}
+
+@system unittest
+{
+    import std.bigint;
+
+        // BigInts whose values cannot be exactly represented as float/double/real
+        // are rounded when cast to float/double/real. When cast to float or
+        // double or 64-bit real the rounding is strictly defined. When cast
+        // to extended-precision real the rounding rules vary by environment.
+
+        // BigInts that fall somewhere between two non-infinite floats/doubles
+        // are rounded to the closer value when cast to float/double.
+        assert(cast(float) BigInt(0x1aaa_aae7) == 0x1.aaa_aaep+28f);
+        assert(cast(float) BigInt(0x1aaa_aaff) == 0x1.aaa_ab0p+28f);
+        assert(cast(float) BigInt(-0x1aaa_aae7) == -0x1.aaaaaep+28f);
+        assert(cast(float) BigInt(-0x1aaa_aaff) == -0x1.aaaab0p+28f);
+
+        assert(cast(double) BigInt(0x1aaa_aaaa_aaaa_aa77) == 0x1.aaa_aaaa_aaaa_aa00p+60);
+        assert(cast(double) BigInt(0x1aaa_aaaa_aaaa_aaff) == 0x1.aaa_aaaa_aaaa_ab00p+60);
+        assert(cast(double) BigInt(-0x1aaa_aaaa_aaaa_aa77) == -0x1.aaa_aaaa_aaaa_aa00p+60);
+        assert(cast(double) BigInt(-0x1aaa_aaaa_aaaa_aaff) == -0x1.aaa_aaaa_aaaa_ab00p+60);
+
+        // BigInts that fall exactly between two non-infinite floats/doubles
+        // are rounded away from zero when cast to float/double. (Note that
+        // in most environments this is NOT the same rounding rule rule used
+        // when casting int/long to float/double.)
+        assert(cast(float) BigInt(0x1aaa_aaf0) == 0x1.aaa_ab0p+28f);
+        assert(cast(float) BigInt(-0x1aaa_aaf0) == -0x1.aaaab0p+28f);
+
+        assert(cast(double) BigInt(0x1aaa_aaaa_aaaa_aa80) == 0x1.aaa_aaaa_aaaa_ab00p+60);
+        assert(cast(double) BigInt(-0x1aaa_aaaa_aaaa_aa80) == -0x1.aaa_aaaa_aaaa_ab00p+60);
+
+        // BigInts that are bounded on one side by the largest positive or
+        // most negative finite float/double and on the other side by infinity
+        // or -infinity are rounded as if in place of infinity was the value
+        // `2^^(T.max_exp)` when cast to float/double.
+        assert(cast(float) BigInt("999_999_999_999_999_999_999_999_999_999_999_999_999") == float.infinity);
+        assert(cast(float) BigInt("-999_999_999_999_999_999_999_999_999_999_999_999_999") == -float.infinity);
+
+        assert(cast(double) BigInt("999_999_999_999_999_999_999_999_999_999_999_999_999") < double.infinity);
+        assert(cast(real) BigInt("999_999_999_999_999_999_999_999_999_999_999_999_999") < real.infinity);
+    
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+        const(BigInt) x = BigInt("123");
+        BigInt y = cast() x;    // cast away const
+        assert(y == x);
+    
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+        auto x = BigInt("100");
+        auto y = BigInt("10");
+        int z = 50;
+        const int w = 200;
+
+        assert(y < x);
+        assert(x > z);
+        assert(z > y);
+        assert(x < w);
+    
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+        auto x = BigInt("0x1abc_de80_0000_0000_0000_0000_0000_0000");
+        BigInt y = x - 1;
+        BigInt z = x + 1;
+
+        double d = 0x1.abcde8p124;
+        assert(y < d);
+        assert(z > d);
+        assert(x >= d && x <= d);
+
+        // Note that when comparing a BigInt to a float or double the
+        // full precision of the BigInt is always considered, unlike
+        // when comparing an int to a float or a long to a double.
+        assert(BigInt(123456789) < cast(float) 123456789);
+    
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+        auto b = BigInt("12345");
+        long l = b.toLong();
+        assert(l == 12345);
+    
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+        auto big = BigInt("5_000_000");
+        auto i = big.toInt();
+        assert(i == 5_000_000);
+
+        // Numbers that are too big to fit into an int will be clamped to int.max.
+        auto tooBig = BigInt("5_000_000_000");
+        i = tooBig.toInt();
+        assert(i == int.max);
+    
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+        import std.format : format;
+
+        auto x = BigInt("1_000_000");
+        x *= 12345;
+
+        assert(format("%d", x) == "12345000000");
+        assert(format("%x", x) == "2_dfd1c040");
+        assert(format("%X", x) == "2_DFD1C040");
+        assert(format("%o", x) == "133764340100");
+    
+}
+
+@safe pure unittest
+{
+    import std.bigint;
+
+        string[BigInt] aa;
+        aa[BigInt(123)] = "abc";
+        aa[BigInt(456)] = "def";
+
+        assert(aa[BigInt(123)] == "abc");
+        assert(aa[BigInt(456)] == "def");
+    
+}
+
+@safe pure unittest
+{
+    import std.bigint;
+
+        auto a = BigInt("1000");
+        assert(a.ulongLength() == 1);
+        assert(a.getDigit(0) == 1000);
+
+        assert(a.uintLength() == 1);
+        assert(a.getDigit!uint(0) == 1000);
+
+        auto b = BigInt("2_000_000_000_000_000_000_000_000_000");
+        assert(b.ulongLength() == 2);
+        assert(b.getDigit(0) == 4584946418820579328);
+        assert(b.getDigit(1) == 108420217);
+
+        assert(b.uintLength() == 3);
+        assert(b.getDigit!uint(0) == 3489660928);
+        assert(b.getDigit!uint(1) == 1067516025);
+        assert(b.getDigit!uint(2) == 108420217);
+    
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+    BigInt a = "9588669891916142";
+    BigInt b = "7452469135154800";
+    auto c = a * b;
+    assert(c == BigInt("71459266416693160362545788781600"));
+    auto d = b * a;
+    assert(d == BigInt("71459266416693160362545788781600"));
+    assert(d == c);
+    d = c * BigInt("794628672112");
+    assert(d == BigInt("56783581982794522489042432639320434378739200"));
+    auto e = c + d;
+    assert(e == BigInt("56783581982865981755459125799682980167520800"));
+    auto f = d + c;
+    assert(f == e);
+    auto g = f - c;
+    assert(g == d);
+    g = f - d;
+    assert(g == c);
+    e = 12345678;
+    g = c + e;
+    auto h = g / b;
+    auto i = g % b;
+    assert(h == a);
+    assert(i == e);
+    BigInt j = "-0x9A56_57f4_7B83_AB78";
+    BigInt k = j;
+    j ^^= 11;
+    assert(k ^^ 11 == j);
+}
+
+@safe pure unittest
+{
+    import std.bigint;
+
+    auto x = BigInt("123");
+    x *= 1000;
+    x += 456;
+
+    auto xstr = x.toDecimalString();
+    assert(xstr == "123456");
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+    auto x = BigInt("123");
+    x *= 1000;
+    x += 456;
+
+    auto xstr = x.toHex();
+    assert(xstr == "1E240");
+}
+
+nothrow pure @safe unittest
+{
+    import std.bigint;
+
+    assert((-1).absUnsign == 1);
+    assert(1.absUnsign == 1);
+}
+
+@safe pure nothrow unittest
+{
+    import std.bigint;
+
+    auto a = BigInt(123);
+    auto b = BigInt(25);
+    BigInt q, r;
+
+    divMod(a, b, q, r);
+
+    assert(q == 4);
+    assert(r == 23);
+    assert(q * b + r == a);
+}
+
+@safe unittest
+{
+    import std.bigint;
+
+    BigInt base = BigInt("123456789012345678901234567890");
+    BigInt exponent = BigInt("1234567890123456789012345678901234567");
+    BigInt modulus = BigInt("1234567");
+
+    BigInt result = powmod(base, exponent, modulus);
+    assert(result == 359079);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_bitmanip.d b/libphobos/testsuite/libphobos.phobos/std_bitmanip.d
new file mode 100644
index 0000000000000000000000000000000000000000..6bd658d011868229278e6046a13385befe843100
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_bitmanip.d
@@ -0,0 +1,1628 @@
+@safe unittest
+{
+    import std.bitmanip;
+
+    struct A
+    {
+        int a;
+        mixin(bitfields!(
+            uint, "x",    2,
+            int,  "y",    3,
+            uint, "z",    2,
+            bool, "flag", 1));
+    }
+
+    A obj;
+    obj.x = 2;
+    obj.z = obj.x;
+
+    assert(obj.x == 2);
+    assert(obj.y == 0);
+    assert(obj.z == 2);
+    assert(obj.flag == false);
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    struct A
+    {
+        mixin(bitfields!(
+            bool, "flag1",    1,
+            bool, "flag2",    1,
+            uint, "",         6));
+    }
+
+    A a;
+    assert(a.flag1 == 0);
+    a.flag1 = 1;
+    assert(a.flag1 == 1);
+    a.flag1 = 0;
+    assert(a.flag1 == 0);
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    enum ABC { A, B, C }
+    struct EnumTest
+    {
+        mixin(bitfields!(
+                  ABC, "x", 2,
+                  bool, "y", 1,
+                  ubyte, "z", 5));
+    }
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    struct A
+    {
+        int a;
+        mixin(taggedPointer!(
+            uint*, "x",
+            bool, "b1", 1,
+            bool, "b2", 1));
+    }
+    A obj;
+    obj.x = new uint;
+    obj.b1 = true;
+    obj.b2 = false;
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    struct A
+    {
+        int a;
+        mixin(taggedClassRef!(
+            Object, "o",
+            uint, "i", 2));
+    }
+    A obj;
+    obj.o = new Object();
+    obj.i = 3;
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    FloatRep rep = {value: 0};
+    assert(rep.fraction == 0);
+    assert(rep.exponent == 0);
+    assert(!rep.sign);
+
+    rep.value = 42;
+    assert(rep.fraction == 2621440);
+    assert(rep.exponent == 132);
+    assert(!rep.sign);
+
+    rep.value = 10;
+    assert(rep.fraction == 2097152);
+    assert(rep.exponent == 130);
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    FloatRep rep = {value: 1};
+    assert(rep.fraction == 0);
+    assert(rep.exponent == 127);
+    assert(!rep.sign);
+
+    rep.exponent = 126;
+    assert(rep.value == 0.5);
+
+    rep.exponent = 130;
+    assert(rep.value == 8);
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    FloatRep rep = {value: 1};
+    rep.value = -0.5;
+    assert(rep.fraction == 0);
+    assert(rep.exponent == 126);
+    assert(rep.sign);
+
+    rep.value = -1. / 3;
+    assert(rep.fraction == 2796203);
+    assert(rep.exponent == 125);
+    assert(rep.sign);
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    DoubleRep rep = {value: 0};
+    assert(rep.fraction == 0);
+    assert(rep.exponent == 0);
+    assert(!rep.sign);
+
+    rep.value = 42;
+    assert(rep.fraction == 1407374883553280);
+    assert(rep.exponent == 1028);
+    assert(!rep.sign);
+
+    rep.value = 10;
+    assert(rep.fraction == 1125899906842624);
+    assert(rep.exponent == 1026);
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    DoubleRep rep = {value: 1};
+    assert(rep.fraction == 0);
+    assert(rep.exponent == 1023);
+    assert(!rep.sign);
+
+    rep.exponent = 1022;
+    assert(rep.value == 0.5);
+
+    rep.exponent = 1026;
+    assert(rep.value == 8);
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    DoubleRep rep = {value: 1};
+    rep.value = -0.5;
+    assert(rep.fraction == 0);
+    assert(rep.exponent == 1022);
+    assert(rep.sign);
+
+    rep.value = -1. / 3;
+    assert(rep.fraction == 1501199875790165);
+    assert(rep.exponent == 1021);
+    assert(rep.sign);
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    DoubleRep x;
+    x.value = 1.0;
+    assert(x.fraction == 0 && x.exponent == 1023 && !x.sign);
+    x.value = -0.5;
+    assert(x.fraction == 0 && x.exponent == 1022 && x.sign);
+    x.value = 0.5;
+    assert(x.fraction == 0 && x.exponent == 1022 && !x.sign);
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    DoubleRep x;
+    x.fraction = 1125899906842624;
+    x.exponent = 1025;
+    x.sign = true;
+    assert(x.value == -5.0);
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        import std.algorithm.comparison : equal;
+
+        bool[] input = [true, false, false, true, true];
+        auto a = BitArray(input);
+        assert(a.length == 5);
+        assert(a.bitsSet.equal([0, 3, 4]));
+
+        // This also works because an implicit cast to bool[] occurs for this array.
+        auto b = BitArray([0, 0, 1]);
+        assert(b.length == 3);
+        assert(b.bitsSet.equal([2]));
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        import std.algorithm.comparison : equal;
+        import std.array : array;
+        import std.range : iota, repeat;
+
+        BitArray a = true.repeat(70).array;
+        assert(a.length == 70);
+        assert(a.bitsSet.equal(iota(0, 70)));
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        import std.algorithm.comparison : equal;
+
+        auto a = BitArray([1, 0, 0, 1, 1]);
+
+        // Inverse of the cast.
+        auto v = cast(void[]) a;
+        auto b = BitArray(v, a.length);
+
+        assert(b.length == 5);
+        assert(b.bitsSet.equal([0, 3, 4]));
+
+        // a and b share the underlying data.
+        a[0] = 0;
+        assert(b[0] == 0);
+        assert(a == b);
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        import std.algorithm.comparison : equal;
+
+        size_t[] source = [0b1100, 0b0011];
+        enum sbits = size_t.sizeof * 8;
+        auto ba = BitArray(source, source.length * sbits);
+        // The least significant bit in each unit is this unit's starting bit.
+        assert(ba.bitsSet.equal([2, 3, sbits, sbits + 1]));
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        // Example from the doc for this constructor.
+        static immutable size_t[] sourceData = [1, 0b101, 3, 3424234, 724398, 230947, 389492];
+        size_t[] source = sourceData.dup;
+        enum sbits = size_t.sizeof * 8;
+        auto ba = BitArray(source, source.length * sbits);
+        foreach (n; 0 .. source.length * sbits)
+        {
+            auto nth_bit = cast(bool) (source[n / sbits] & (1L << (n % sbits)));
+            assert(ba[n] == nth_bit);
+        }
+
+        // Example of mapping only part of the array.
+        import std.algorithm.comparison : equal;
+
+        auto bc = BitArray(source, sbits + 1);
+        assert(bc.bitsSet.equal([0, sbits]));
+        // Source array has not been modified.
+        assert(source == sourceData);
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        static void fun(const BitArray arr)
+        {
+            auto x = arr[0];
+            assert(x == 1);
+        }
+        BitArray a;
+        a.length = 3;
+        a[0] = 1;
+        fun(a);
+    
+}
+
+@system pure nothrow unittest
+{
+    import std.bitmanip;
+
+        import std.algorithm.comparison : equal;
+
+        auto b = BitArray([1, 0, 1, 0, 1, 1]);
+
+        b[] = true;
+        // all bits are set
+        assert(b.bitsSet.equal([0, 1, 2, 3, 4, 5]));
+
+        b[] = false;
+        // none of the bits are set
+        assert(b.bitsSet.empty);
+    
+}
+
+@system pure nothrow unittest
+{
+    import std.bitmanip;
+
+        import std.algorithm.comparison : equal;
+        import std.range : iota;
+        import std.stdio;
+
+        auto b = BitArray([1, 0, 0, 0, 1, 1, 0]);
+        b[1 .. 3] = true;
+        assert(b.bitsSet.equal([0, 1, 2, 4, 5]));
+
+        bool[72] bitArray;
+        auto b1 = BitArray(bitArray);
+        b1[63 .. 67] = true;
+        assert(b1.bitsSet.equal([63, 64, 65, 66]));
+        b1[63 .. 67] = false;
+        assert(b1.bitsSet.empty);
+        b1[0 .. 64] = true;
+        assert(b1.bitsSet.equal(iota(0, 64)));
+        b1[0 .. 64] = false;
+        assert(b1.bitsSet.empty);
+
+        bool[256] bitArray2;
+        auto b2 = BitArray(bitArray2);
+        b2[3 .. 245] = true;
+        assert(b2.bitsSet.equal(iota(3, 245)));
+        b2[3 .. 245] = false;
+        assert(b2.bitsSet.empty);
+    
+}
+
+@system pure nothrow unittest
+{
+    import std.bitmanip;
+
+        import std.algorithm.comparison : equal;
+        import std.range : iota;
+
+        // positions 0, 2, 4 are set
+        auto b = BitArray([1, 0, 1, 0, 1, 0]);
+        b.flip();
+        // after flipping, positions 1, 3, 5 are set
+        assert(b.bitsSet.equal([1, 3, 5]));
+
+        bool[270] bits;
+        auto b1 = BitArray(bits);
+        b1.flip();
+        assert(b1.bitsSet.equal(iota(0, 270)));
+    
+}
+
+@system pure nothrow unittest
+{
+    import std.bitmanip;
+
+        auto ax = BitArray([1, 0, 0, 1]);
+        ax.flip(0);
+        assert(ax[0] == 0);
+
+        bool[200] y;
+        y[90 .. 130] = true;
+        auto ay = BitArray(y);
+        ay.flip(100);
+        assert(ay[100] == 0);
+    
+}
+
+@system pure nothrow unittest
+{
+    import std.bitmanip;
+
+        auto a = BitArray([0, 1, 1, 0, 0, 1, 1]);
+        assert(a.count == 4);
+
+        BitArray b;
+        assert(b.count == 0);
+
+        bool[200] boolArray;
+        boolArray[45 .. 130] = true;
+        auto c = BitArray(boolArray);
+        assert(c.count == 85);
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        BitArray a;
+        BitArray b;
+
+        a.length = 3;
+        a[0] = 1; a[1] = 0; a[2] = 1;
+        b = a.dup;
+        assert(b.length == 3);
+        foreach (i; 0 .. 3)
+            assert(b[i] == (((i ^ 1) & 1) ? true : false));
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        bool[] ba = [1,0,1];
+
+        auto a = BitArray(ba);
+
+        int i;
+        foreach (b;a)
+        {
+            switch (i)
+            {
+                case 0: assert(b == true); break;
+                case 1: assert(b == false); break;
+                case 2: assert(b == true); break;
+                default: assert(0);
+            }
+            i++;
+        }
+
+        foreach (j,b;a)
+        {
+            switch (j)
+            {
+                case 0: assert(b == true); break;
+                case 1: assert(b == false); break;
+                case 2: assert(b == true); break;
+                default: assert(0);
+            }
+        }
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        BitArray b;
+        bool[5] data = [1,0,1,1,0];
+
+        b = BitArray(data);
+        b.reverse;
+        foreach (i; 0 .. data.length)
+            assert(b[i] == data[4 - i]);
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        size_t x = 0b1100011000;
+        auto ba = BitArray(10, &x);
+        ba.sort;
+        foreach (i; 0 .. 6)
+            assert(ba[i] == false);
+        foreach (i; 6 .. 10)
+            assert(ba[i] == true);
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        bool[] ba = [1,0,1,0,1];
+        bool[] bb = [1,0,1];
+        bool[] bc = [1,0,1,0,1,0,1];
+        bool[] bd = [1,0,1,1,1];
+        bool[] be = [1,0,1,0,1];
+        bool[] bf = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
+        bool[] bg = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1];
+
+        auto a = BitArray(ba);
+        auto b = BitArray(bb);
+        auto c = BitArray(bc);
+        auto d = BitArray(bd);
+        auto e = BitArray(be);
+        auto f = BitArray(bf);
+        auto g = BitArray(bg);
+
+        assert(a != b);
+        assert(a != c);
+        assert(a != d);
+        assert(a == e);
+        assert(f != g);
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        bool[] ba = [1,0,1,0,1];
+        bool[] bb = [1,0,1];
+        bool[] bc = [1,0,1,0,1,0,1];
+        bool[] bd = [1,0,1,1,1];
+        bool[] be = [1,0,1,0,1];
+        bool[] bf = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1];
+        bool[] bg = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0];
+
+        auto a = BitArray(ba);
+        auto b = BitArray(bb);
+        auto c = BitArray(bc);
+        auto d = BitArray(bd);
+        auto e = BitArray(be);
+        auto f = BitArray(bf);
+        auto g = BitArray(bg);
+
+        assert(a >  b);
+        assert(a >= b);
+        assert(a <  c);
+        assert(a <= c);
+        assert(a <  d);
+        assert(a <= d);
+        assert(a == e);
+        assert(a <= e);
+        assert(a >= e);
+        assert(f <  g);
+        assert(g <= g);
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        import std.array : array;
+        import std.range : repeat, take;
+
+        // bit array with 300 elements
+        auto a = BitArray(true.repeat.take(300).array);
+        size_t[] v = cast(size_t[]) a;
+        const blockSize = size_t.sizeof * 8;
+        assert(v.length == (a.length + blockSize - 1) / blockSize);
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        bool[] ba = [1,0,1,0,1];
+
+        auto a = BitArray(ba);
+        BitArray b = ~a;
+
+        assert(b[0] == 0);
+        assert(b[1] == 1);
+        assert(b[2] == 0);
+        assert(b[3] == 1);
+        assert(b[4] == 0);
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        static bool[] ba = [1,0,1,0,1];
+        static bool[] bb = [1,0,1,1,0];
+
+        auto a = BitArray(ba);
+        auto b = BitArray(bb);
+
+        BitArray c = a & b;
+
+        assert(c[0] == 1);
+        assert(c[1] == 0);
+        assert(c[2] == 1);
+        assert(c[3] == 0);
+        assert(c[4] == 0);
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        bool[] ba = [1,0,1,0,1];
+        bool[] bb = [1,0,1,1,0];
+
+        auto a = BitArray(ba);
+        auto b = BitArray(bb);
+
+        BitArray c = a | b;
+
+        assert(c[0] == 1);
+        assert(c[1] == 0);
+        assert(c[2] == 1);
+        assert(c[3] == 1);
+        assert(c[4] == 1);
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        bool[] ba = [1,0,1,0,1];
+        bool[] bb = [1,0,1,1,0];
+
+        auto a = BitArray(ba);
+        auto b = BitArray(bb);
+
+        BitArray c = a ^ b;
+
+        assert(c[0] == 0);
+        assert(c[1] == 0);
+        assert(c[2] == 0);
+        assert(c[3] == 1);
+        assert(c[4] == 1);
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        bool[] ba = [1,0,1,0,1];
+        bool[] bb = [1,0,1,1,0];
+
+        auto a = BitArray(ba);
+        auto b = BitArray(bb);
+
+        BitArray c = a - b;
+
+        assert(c[0] == 0);
+        assert(c[1] == 0);
+        assert(c[2] == 0);
+        assert(c[3] == 0);
+        assert(c[4] == 1);
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        bool[] ba = [1,0,1,0,1,1,0,1,0,1];
+        bool[] bb = [1,0,1,1,0];
+        auto a = BitArray(ba);
+        auto b = BitArray(bb);
+        BitArray c = a;
+        c.length = 5;
+        c &= b;
+        assert(a[5] == 1);
+        assert(a[6] == 0);
+        assert(a[7] == 1);
+        assert(a[8] == 0);
+        assert(a[9] == 1);
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        bool[] ba = [1,0,1,0,1];
+        bool[] bb = [1,0,1,1,0];
+
+        auto a = BitArray(ba);
+        auto b = BitArray(bb);
+
+        a &= b;
+        assert(a[0] == 1);
+        assert(a[1] == 0);
+        assert(a[2] == 1);
+        assert(a[3] == 0);
+        assert(a[4] == 0);
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        bool[] ba = [1,0,1,0,1];
+        bool[] bb = [1,0,1,1,0];
+
+        auto a = BitArray(ba);
+        auto b = BitArray(bb);
+
+        a |= b;
+        assert(a[0] == 1);
+        assert(a[1] == 0);
+        assert(a[2] == 1);
+        assert(a[3] == 1);
+        assert(a[4] == 1);
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        bool[] ba = [1,0,1,0,1];
+        bool[] bb = [1,0,1,1,0];
+
+        auto a = BitArray(ba);
+        auto b = BitArray(bb);
+
+        a ^= b;
+        assert(a[0] == 0);
+        assert(a[1] == 0);
+        assert(a[2] == 0);
+        assert(a[3] == 1);
+        assert(a[4] == 1);
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        bool[] ba = [1,0,1,0,1];
+        bool[] bb = [1,0,1,1,0];
+
+        auto a = BitArray(ba);
+        auto b = BitArray(bb);
+
+        a -= b;
+        assert(a[0] == 0);
+        assert(a[1] == 0);
+        assert(a[2] == 0);
+        assert(a[3] == 0);
+        assert(a[4] == 1);
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        bool[] ba = [1,0,1,0,1];
+
+        auto a = BitArray(ba);
+        BitArray b;
+
+        b = (a ~= true);
+        assert(a[0] == 1);
+        assert(a[1] == 0);
+        assert(a[2] == 1);
+        assert(a[3] == 0);
+        assert(a[4] == 1);
+        assert(a[5] == 1);
+
+        assert(b == a);
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        bool[] ba = [1,0];
+        bool[] bb = [0,1,0];
+
+        auto a = BitArray(ba);
+        auto b = BitArray(bb);
+        BitArray c;
+
+        c = (a ~= b);
+        assert(a.length == 5);
+        assert(a[0] == 1);
+        assert(a[1] == 0);
+        assert(a[2] == 0);
+        assert(a[3] == 1);
+        assert(a[4] == 0);
+
+        assert(c == a);
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        bool[] ba = [1,0];
+        bool[] bb = [0,1,0];
+
+        auto a = BitArray(ba);
+        auto b = BitArray(bb);
+        BitArray c;
+
+        c = (a ~ b);
+        assert(c.length == 5);
+        assert(c[0] == 1);
+        assert(c[1] == 0);
+        assert(c[2] == 0);
+        assert(c[3] == 1);
+        assert(c[4] == 0);
+
+        c = (a ~ true);
+        assert(c.length == 3);
+        assert(c[0] == 1);
+        assert(c[1] == 0);
+        assert(c[2] == 1);
+
+        c = (false ~ a);
+        assert(c.length == 3);
+        assert(c[0] == 0);
+        assert(c[1] == 1);
+        assert(c[2] == 0);
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        import std.format : format;
+
+        auto b = BitArray([1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1]);
+
+        b <<= 1;
+        assert(format("%b", b) == "01100_10101101");
+
+        b >>= 1;
+        assert(format("%b", b) == "11001_01011010");
+
+        b <<= 4;
+        assert(format("%b", b) == "00001_10010101");
+
+        b >>= 5;
+        assert(format("%b", b) == "10010_10100000");
+
+        b <<= 13;
+        assert(format("%b", b) == "00000_00000000");
+
+        b = BitArray([1, 0, 1, 1, 0, 1, 1, 1]);
+        b >>= 8;
+        assert(format("%b", b) == "00000000");
+
+    
+}
+
+@system pure unittest
+{
+    import std.bitmanip;
+
+        import std.format : format;
+
+        auto b = BitArray([0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]);
+
+        auto s1 = format("%s", b);
+        assert(s1 == "[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]");
+
+        auto s2 = format("%b", b);
+        assert(s2 == "00001111_00001111");
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+        import std.algorithm.comparison : equal;
+
+        auto b1 = BitArray([0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]);
+        assert(b1.bitsSet.equal([4, 5, 6, 7, 12, 13, 14, 15]));
+
+        BitArray b2;
+        b2.length = 1000;
+        b2[333] = true;
+        b2[666] = true;
+        b2[999] = true;
+        assert(b2.bitsSet.equal([333, 666, 999]));
+    
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+    import std.algorithm.comparison : equal;
+    import std.range : iota;
+
+    bool[] buf = new bool[64 * 3];
+    buf[0 .. 64] = true;
+    BitArray b = BitArray(buf);
+    assert(b.bitsSet.equal(iota(0, 64)));
+    b <<= 64;
+    assert(b.bitsSet.equal(iota(64, 128)));
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+    import std.algorithm.comparison : equal;
+
+    auto b = BitArray([1, 0]);
+    b ~= true;
+    assert(b[2] == 1);
+    b ~= BitArray([0, 1]);
+    auto c = BitArray([1, 0, 1, 0, 1]);
+    assert(b == c);
+    assert(b.bitsSet.equal([0, 2, 4]));
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+    import std.algorithm.comparison : equal;
+
+    auto b = BitArray([1, 1, 0, 1]);
+    b &= BitArray([0, 1, 1, 0]);
+    assert(b.bitsSet.equal([1]));
+    b.flip;
+    assert(b.bitsSet.equal([0, 2, 3]));
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+    import std.format : format;
+    auto b = BitArray([1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]);
+    assert(format("%b", b) == "1_00001111_00001111");
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+    import std.format : format;
+
+    BitArray b;
+
+    b = BitArray([]);
+    assert(format("%s", b) == "[]");
+    assert(format("%b", b) is null);
+
+    b = BitArray([1]);
+    assert(format("%s", b) == "[1]");
+    assert(format("%b", b) == "1");
+
+    b = BitArray([0, 0, 0, 0]);
+    assert(format("%b", b) == "0000");
+
+    b = BitArray([0, 0, 0, 0, 1, 1, 1, 1]);
+    assert(format("%s", b) == "[0, 0, 0, 0, 1, 1, 1, 1]");
+    assert(format("%b", b) == "00001111");
+
+    b = BitArray([0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]);
+    assert(format("%s", b) == "[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]");
+    assert(format("%b", b) == "00001111_00001111");
+
+    b = BitArray([1, 0, 0, 0, 0, 1, 1, 1, 1]);
+    assert(format("%b", b) == "1_00001111");
+
+    b = BitArray([1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]);
+    assert(format("%b", b) == "1_00001111_00001111");
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    assert(42.swapEndian == 704643072);
+    assert(42.swapEndian.swapEndian == 42); // reflexive
+    assert(1.swapEndian == 16777216);
+
+    assert(true.swapEndian == true);
+    assert(byte(10).swapEndian == 10);
+    assert(char(10).swapEndian == 10);
+
+    assert(ushort(10).swapEndian == 2560);
+    assert(long(10).swapEndian == 720575940379279360);
+    assert(ulong(10).swapEndian == 720575940379279360);
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    int i = 12345;
+    ubyte[4] swappedI = nativeToBigEndian(i);
+    assert(i == bigEndianToNative!int(swappedI));
+
+    float f = 123.45f;
+    ubyte[4] swappedF = nativeToBigEndian(f);
+    assert(f == bigEndianToNative!float(swappedF));
+
+    const float cf = 123.45f;
+    ubyte[4] swappedCF = nativeToBigEndian(cf);
+    assert(cf == bigEndianToNative!float(swappedCF));
+
+    double d = 123.45;
+    ubyte[8] swappedD = nativeToBigEndian(d);
+    assert(d == bigEndianToNative!double(swappedD));
+
+    const double cd = 123.45;
+    ubyte[8] swappedCD = nativeToBigEndian(cd);
+    assert(cd == bigEndianToNative!double(swappedCD));
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    ushort i = 12345;
+    ubyte[2] swappedI = nativeToBigEndian(i);
+    assert(i == bigEndianToNative!ushort(swappedI));
+
+    dchar c = 'D';
+    ubyte[4] swappedC = nativeToBigEndian(c);
+    assert(c == bigEndianToNative!dchar(swappedC));
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    int i = 12345;
+    ubyte[4] swappedI = nativeToLittleEndian(i);
+    assert(i == littleEndianToNative!int(swappedI));
+
+    float f = 123.45f;
+    ubyte[4] swappedF = nativeToLittleEndian(f);
+    assert(f == littleEndianToNative!float(swappedF));
+
+    const float cf = 123.45f;
+    ubyte[4] swappedCF = nativeToLittleEndian(cf);
+    assert(cf == littleEndianToNative!float(swappedCF));
+
+    double d = 123.45;
+    ubyte[8] swappedD = nativeToLittleEndian(d);
+    assert(d == littleEndianToNative!double(swappedD));
+
+    const double cd = 123.45;
+    ubyte[8] swappedCD = nativeToLittleEndian(cd);
+    assert(cd == littleEndianToNative!double(swappedCD));
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    ushort i = 12345;
+    ubyte[2] swappedI = nativeToLittleEndian(i);
+    assert(i == littleEndianToNative!ushort(swappedI));
+
+    dchar c = 'D';
+    ubyte[4] swappedC = nativeToLittleEndian(c);
+    assert(c == littleEndianToNative!dchar(swappedC));
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+    ubyte[] buffer = [1, 5, 22, 9, 44, 255, 8];
+    assert(buffer.peek!uint() == 17110537);
+    assert(buffer.peek!ushort() == 261);
+    assert(buffer.peek!ubyte() == 1);
+
+    assert(buffer.peek!uint(2) == 369700095);
+    assert(buffer.peek!ushort(2) == 5641);
+    assert(buffer.peek!ubyte(2) == 22);
+
+    size_t index = 0;
+    assert(buffer.peek!ushort(&index) == 261);
+    assert(index == 2);
+
+    assert(buffer.peek!uint(&index) == 369700095);
+    assert(index == 6);
+
+    assert(buffer.peek!ubyte(&index) == 8);
+    assert(index == 7);
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    import std.algorithm.iteration : filter;
+    ubyte[] buffer = [1, 5, 22, 9, 44, 255, 7];
+    auto range = filter!"true"(buffer);
+    assert(range.peek!uint() == 17110537);
+    assert(range.peek!ushort() == 261);
+    assert(range.peek!ubyte() == 1);
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    import std.range.primitives : empty;
+    ubyte[] buffer = [1, 5, 22, 9, 44, 255, 8];
+    assert(buffer.length == 7);
+
+    assert(buffer.read!ushort() == 261);
+    assert(buffer.length == 5);
+
+    assert(buffer.read!uint() == 369700095);
+    assert(buffer.length == 1);
+
+    assert(buffer.read!ubyte() == 8);
+    assert(buffer.empty);
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+    ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
+    buffer.write!uint(29110231u, 0);
+    assert(buffer == [1, 188, 47, 215, 0, 0, 0, 0]);
+
+    buffer.write!ushort(927, 0);
+    assert(buffer == [3, 159, 47, 215, 0, 0, 0, 0]);
+
+    buffer.write!ubyte(42, 0);
+    assert(buffer == [42, 159, 47, 215, 0, 0, 0, 0]);
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+    ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0];
+    buffer.write!uint(142700095u, 2);
+    assert(buffer == [0, 0, 8, 129, 110, 63, 0, 0, 0]);
+
+    buffer.write!ushort(19839, 2);
+    assert(buffer == [0, 0, 77, 127, 110, 63, 0, 0, 0]);
+
+    buffer.write!ubyte(132, 2);
+    assert(buffer == [0, 0, 132, 127, 110, 63, 0, 0, 0]);
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+    ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
+    size_t index = 0;
+    buffer.write!ushort(261, &index);
+    assert(buffer == [1, 5, 0, 0, 0, 0, 0, 0]);
+    assert(index == 2);
+
+    buffer.write!uint(369700095u, &index);
+    assert(buffer == [1, 5, 22, 9, 44, 255, 0, 0]);
+    assert(index == 6);
+
+    buffer.write!ubyte(8, &index);
+    assert(buffer == [1, 5, 22, 9, 44, 255, 8, 0]);
+    assert(index == 7);
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+    ubyte[] buffer = [0, 0];
+    buffer.write!bool(false, 0);
+    assert(buffer == [0, 0]);
+
+    buffer.write!bool(true, 0);
+    assert(buffer == [1, 0]);
+
+    buffer.write!bool(true, 1);
+    assert(buffer == [1, 1]);
+
+    buffer.write!bool(false, 1);
+    assert(buffer == [1, 0]);
+
+    size_t index = 0;
+    buffer.write!bool(false, &index);
+    assert(buffer == [0, 0]);
+    assert(index == 1);
+
+    buffer.write!bool(true, &index);
+    assert(buffer == [0, 1]);
+    assert(index == 2);
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+    ubyte[] buffer = [0, 0, 0];
+
+    buffer.write!char('a', 0);
+    assert(buffer == [97, 0, 0]);
+
+    buffer.write!char('b', 1);
+    assert(buffer == [97, 98, 0]);
+
+    size_t index = 0;
+    buffer.write!char('a', &index);
+    assert(buffer == [97, 98, 0]);
+    assert(index == 1);
+
+    buffer.write!char('b', &index);
+    assert(buffer == [97, 98, 0]);
+    assert(index == 2);
+
+    buffer.write!char('c', &index);
+    assert(buffer == [97, 98, 99]);
+    assert(index == 3);
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+    ubyte[] buffer = [0, 0, 0, 0];
+
+    buffer.write!wchar('Ä…', 0);
+    assert(buffer == [1, 5, 0, 0]);
+
+    buffer.write!wchar('”', 2);
+    assert(buffer == [1, 5, 32, 29]);
+
+    size_t index = 0;
+    buffer.write!wchar('ć', &index);
+    assert(buffer == [1, 7, 32, 29]);
+    assert(index == 2);
+
+    buffer.write!wchar('Ä…', &index);
+    assert(buffer == [1, 7, 1, 5]);
+    assert(index == 4);
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+    ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
+
+    buffer.write!dchar('Ä…', 0);
+    assert(buffer == [0, 0, 1, 5, 0, 0, 0, 0]);
+
+    buffer.write!dchar('”', 4);
+    assert(buffer == [0, 0, 1, 5, 0, 0, 32, 29]);
+
+    size_t index = 0;
+    buffer.write!dchar('ć', &index);
+    assert(buffer == [0, 0, 1, 7, 0, 0, 32, 29]);
+    assert(index == 4);
+
+    buffer.write!dchar('Ä…', &index);
+    assert(buffer == [0, 0, 1, 7, 0, 0, 1, 5]);
+    assert(index == 8);
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+    ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
+
+    buffer.write!float(32.0f, 0);
+    assert(buffer == [66, 0, 0, 0, 0, 0, 0, 0]);
+
+    buffer.write!float(25.0f, 4);
+    assert(buffer == [66, 0, 0, 0, 65, 200, 0, 0]);
+
+    size_t index = 0;
+    buffer.write!float(25.0f, &index);
+    assert(buffer == [65, 200, 0, 0, 65, 200, 0, 0]);
+    assert(index == 4);
+
+    buffer.write!float(32.0f, &index);
+    assert(buffer == [65, 200, 0, 0, 66, 0, 0, 0]);
+    assert(index == 8);
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+    ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+
+    buffer.write!double(32.0, 0);
+    assert(buffer == [64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+
+    buffer.write!double(25.0, 8);
+    assert(buffer == [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
+
+    size_t index = 0;
+    buffer.write!double(25.0, &index);
+    assert(buffer == [64, 57, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
+    assert(index == 8);
+
+    buffer.write!double(32.0, &index);
+    assert(buffer == [64, 57, 0, 0, 0, 0, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0]);
+    assert(index == 16);
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+    ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+
+    enum Foo
+    {
+        one = 10,
+        two = 20,
+        three = 30
+    }
+
+    buffer.write!Foo(Foo.one, 0);
+    assert(buffer == [0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0]);
+
+    buffer.write!Foo(Foo.two, 4);
+    assert(buffer == [0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 0]);
+
+    buffer.write!Foo(Foo.three, 8);
+    assert(buffer == [0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 30]);
+
+    size_t index = 0;
+    buffer.write!Foo(Foo.three, &index);
+    assert(buffer == [0, 0, 0, 30, 0, 0, 0, 20, 0, 0, 0, 30]);
+    assert(index == 4);
+
+    buffer.write!Foo(Foo.one, &index);
+    assert(buffer == [0, 0, 0, 30, 0, 0, 0, 10, 0, 0, 0, 30]);
+    assert(index == 8);
+
+    buffer.write!Foo(Foo.two, &index);
+    assert(buffer == [0, 0, 0, 30, 0, 0, 0, 10, 0, 0, 0, 20]);
+    assert(index == 12);
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+    ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
+
+    enum Float: float
+    {
+        one = 32.0f,
+        two = 25.0f
+    }
+
+    buffer.write!Float(Float.one, 0);
+    assert(buffer == [66, 0, 0, 0, 0, 0, 0, 0]);
+
+    buffer.write!Float(Float.two, 4);
+    assert(buffer == [66, 0, 0, 0, 65, 200, 0, 0]);
+
+    size_t index = 0;
+    buffer.write!Float(Float.two, &index);
+    assert(buffer == [65, 200, 0, 0, 65, 200, 0, 0]);
+    assert(index == 4);
+
+    buffer.write!Float(Float.one, &index);
+    assert(buffer == [65, 200, 0, 0, 66, 0, 0, 0]);
+    assert(index == 8);
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+    ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+
+    enum Double: double
+    {
+        one = 32.0,
+        two = 25.0
+    }
+
+    buffer.write!Double(Double.one, 0);
+    assert(buffer == [64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+
+    buffer.write!Double(Double.two, 8);
+    assert(buffer == [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
+
+    size_t index = 0;
+    buffer.write!Double(Double.two, &index);
+    assert(buffer == [64, 57, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
+    assert(index == 8);
+
+    buffer.write!Double(Double.one, &index);
+    assert(buffer == [64, 57, 0, 0, 0, 0, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0]);
+    assert(index == 16);
+}
+
+@system unittest
+{
+    import std.bitmanip;
+
+    ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+
+    enum Real: real
+    {
+        one = 32.0,
+        two = 25.0
+    }
+
+    static assert(!__traits(compiles, buffer.write!Real(Real.one)));
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    import std.array;
+    auto buffer = appender!(const ubyte[])();
+    buffer.append!ushort(261);
+    assert(buffer.data == [1, 5]);
+
+    buffer.append!uint(369700095u);
+    assert(buffer.data == [1, 5, 22, 9, 44, 255]);
+
+    buffer.append!ubyte(8);
+    assert(buffer.data == [1, 5, 22, 9, 44, 255, 8]);
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    import std.array : appender;
+    auto buffer = appender!(const ubyte[])();
+
+    buffer.append!bool(true);
+    assert(buffer.data == [1]);
+
+    buffer.append!bool(false);
+    assert(buffer.data == [1, 0]);
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    import std.array : appender;
+    auto buffer = appender!(const ubyte[])();
+
+    buffer.append!char('a');
+    assert(buffer.data == [97]);
+
+    buffer.append!char('b');
+    assert(buffer.data == [97, 98]);
+
+    buffer.append!wchar('Ä…');
+    assert(buffer.data == [97, 98, 1, 5]);
+
+    buffer.append!dchar('Ä…');
+        assert(buffer.data == [97, 98, 1, 5, 0, 0, 1, 5]);
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    import std.array : appender;
+    auto buffer = appender!(const ubyte[])();
+
+    buffer.append!float(32.0f);
+    assert(buffer.data == [66, 0, 0, 0]);
+
+    buffer.append!double(32.0);
+    assert(buffer.data == [66, 0, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0]);
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    import std.array : appender;
+    auto buffer = appender!(const ubyte[])();
+
+    enum Foo
+    {
+        one = 10,
+        two = 20,
+        three = 30
+    }
+
+    buffer.append!Foo(Foo.one);
+    assert(buffer.data == [0, 0, 0, 10]);
+
+    buffer.append!Foo(Foo.two);
+    assert(buffer.data == [0, 0, 0, 10, 0, 0, 0, 20]);
+
+    buffer.append!Foo(Foo.three);
+    assert(buffer.data == [0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 30]);
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    import std.array : appender;
+    auto buffer = appender!(const ubyte[])();
+
+    enum Bool: bool
+    {
+        bfalse = false,
+        btrue = true,
+    }
+
+    buffer.append!Bool(Bool.btrue);
+    assert(buffer.data == [1]);
+
+    buffer.append!Bool(Bool.bfalse);
+    assert(buffer.data == [1, 0]);
+
+    buffer.append!Bool(Bool.btrue);
+    assert(buffer.data == [1, 0, 1]);
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    import std.array : appender;
+    auto buffer = appender!(const ubyte[])();
+
+    enum Float: float
+    {
+        one = 32.0f,
+        two = 25.0f
+    }
+
+    buffer.append!Float(Float.one);
+    assert(buffer.data == [66, 0, 0, 0]);
+
+    buffer.append!Float(Float.two);
+    assert(buffer.data == [66, 0, 0, 0, 65, 200, 0, 0]);
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    import std.array : appender;
+    auto buffer = appender!(const ubyte[])();
+
+    enum Double: double
+    {
+        one = 32.0,
+        two = 25.0
+    }
+
+    buffer.append!Double(Double.one);
+    assert(buffer.data == [64, 64, 0, 0, 0, 0, 0, 0]);
+
+    buffer.append!Double(Double.two);
+    assert(buffer.data == [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    import std.array : appender;
+    auto buffer = appender!(const ubyte[])();
+
+    enum Real: real
+    {
+        one = 32.0,
+        two = 25.0
+    }
+
+    static assert(!__traits(compiles, buffer.append!Real(Real.one)));
+}
+
+@safe unittest
+{
+    import std.bitmanip;
+
+    import std.algorithm.comparison : equal;
+    import std.range : iota;
+
+    assert(bitsSet(1).equal([0]));
+    assert(bitsSet(5).equal([0, 2]));
+    assert(bitsSet(-1).equal(iota(32)));
+    assert(bitsSet(int.min).equal([31]));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_checkedint.d b/libphobos/testsuite/libphobos.phobos/std_checkedint.d
new file mode 100644
index 0000000000000000000000000000000000000000..589d8d3a716cfae47e2a93cd7d2e1e60bedbbfe9
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_checkedint.d
@@ -0,0 +1,628 @@
+@safe unittest
+{
+    import std.checkedint;
+
+    int[] concatAndAdd(int[] a, int[] b, int offset)
+    {
+        // Aborts on overflow on size computation
+        auto r = new int[(checked(a.length) + b.length).get];
+        // Aborts on overflow on element computation
+        foreach (i; 0 .. a.length)
+            r[i] = (a[i] + checked(offset)).get;
+        foreach (i; 0 .. b.length)
+            r[i + a.length] = (b[i] + checked(offset)).get;
+        return r;
+    }
+    assert(concatAndAdd([1, 2, 3], [4, 5], -1) == [0, 1, 2, 3, 4]);
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+    auto x = (cast(byte) 127).checked!Saturate;
+    assert(x == 127);
+    x++;
+    assert(x == 127);
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+    auto x = 100.checked!WithNaN;
+    assert(x == 100);
+    x /= 0;
+    assert(x.isNaN);
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+    uint x = 1;
+    auto y = x.checked!ProperCompare;
+    assert(x < -1); // built-in comparison
+    assert(y > -1); // ProperCompare
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+    import std.exception : assertThrown;
+    auto x = -1.checked!Throw;
+    assertThrown(x / 0);
+    assertThrown(x + int.min);
+    assertThrown(x == uint.max);
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+        auto x = checked(ubyte(42));
+        static assert(is(typeof(x.get()) == ubyte));
+        assert(x.get == 42);
+        const y = checked(ubyte(42));
+        static assert(is(typeof(y.get()) == const ubyte));
+        assert(y.get == 42);
+    
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+            assert(Checked!short.min == -32768);
+            assert(Checked!(short, WithNaN).min == -32767);
+            assert(Checked!(uint, WithNaN).max == uint.max - 1);
+        
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+        auto a = checked(42L);
+        assert(a == 42);
+        auto b = Checked!long(4242); // convert 4242 to long
+        assert(b == 4242);
+    
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+        Checked!long a;
+        a = 42L;
+        assert(a == 42);
+        a = 4242;
+        assert(a == 4242);
+    
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+        Checked!long a, b;
+        a = b = 3;
+        assert(a == 3 && b == 3);
+    
+}
+
+@system unittest
+{
+    import std.checkedint;
+
+        import std.conv : to;
+
+        const a = to!long("1234");
+        const b = to!(Checked!long)("1234");
+        assert(a == b);
+    
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+        assert(cast(uint) checked(42) == 42);
+        assert(cast(uint) checked!WithNaN(-42) == uint.max);
+    
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+        import std.traits : isUnsigned;
+
+        static struct MyHook
+        {
+            static bool thereWereErrors;
+            static bool hookOpEquals(L, R)(L lhs, R rhs)
+            {
+                if (lhs != rhs) return false;
+                static if (isUnsigned!L && !isUnsigned!R)
+                {
+                    if (lhs > 0 && rhs < 0) thereWereErrors = true;
+                }
+                else static if (isUnsigned!R && !isUnsigned!L)
+                    if (lhs < 0 && rhs > 0) thereWereErrors = true;
+                // Preserve built-in behavior.
+                return true;
+            }
+        }
+        auto a = checked!MyHook(-42);
+        assert(a == uint(-42));
+        assert(MyHook.thereWereErrors);
+        MyHook.thereWereErrors = false;
+        assert(checked!MyHook(uint(-42)) == -42);
+        assert(MyHook.thereWereErrors);
+        static struct MyHook2
+        {
+            static bool hookOpEquals(L, R)(L lhs, R rhs)
+            {
+                return lhs == rhs;
+            }
+        }
+        MyHook.thereWereErrors = false;
+        assert(checked!MyHook2(uint(-42)) == a);
+        // Hook on left hand side takes precedence, so no errors
+        assert(!MyHook.thereWereErrors);
+    
+}
+
+@system unittest
+{
+    import std.checkedint;
+
+        import std.format;
+
+        assert(format("%04d", checked(15)) == "0015");
+        assert(format("0x%02x", checked(15)) == "0x0f");
+    
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+        import std.traits : isUnsigned;
+
+        static struct MyHook
+        {
+            static bool thereWereErrors;
+            static int hookOpCmp(L, R)(L lhs, R rhs)
+            {
+                static if (isUnsigned!L && !isUnsigned!R)
+                {
+                    if (rhs < 0 && rhs >= lhs)
+                        thereWereErrors = true;
+                }
+                else static if (isUnsigned!R && !isUnsigned!L)
+                {
+                    if (lhs < 0 && lhs >= rhs)
+                        thereWereErrors = true;
+                }
+                // Preserve built-in behavior.
+                return lhs < rhs ? -1 : lhs > rhs;
+            }
+        }
+        auto a = checked!MyHook(-42);
+        assert(a > uint(42));
+        assert(MyHook.thereWereErrors);
+        static struct MyHook2
+        {
+            static int hookOpCmp(L, R)(L lhs, R rhs)
+            {
+                // Default behavior
+                return lhs < rhs ? -1 : lhs > rhs;
+            }
+        }
+        MyHook.thereWereErrors = false;
+        assert(Checked!(uint, MyHook2)(uint(-42)) <= a);
+        //assert(Checked!(uint, MyHook2)(uint(-42)) >= a);
+        // Hook on left hand side takes precedence, so no errors
+        assert(!MyHook.thereWereErrors);
+        assert(a <= Checked!(uint, MyHook2)(uint(-42)));
+        assert(MyHook.thereWereErrors);
+    
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+        static struct MyHook
+        {
+            static bool thereWereErrors;
+            static L hookOpUnary(string x, L)(L lhs)
+            {
+                if (x == "-" && lhs == -lhs) thereWereErrors = true;
+                return -lhs;
+            }
+        }
+        auto a = checked!MyHook(long.min);
+        assert(a == -a);
+        assert(MyHook.thereWereErrors);
+        auto b = checked!void(42);
+        assert(++b == 43);
+    
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+        static struct MyHook
+        {
+            static bool thereWereErrors;
+            static T onLowerBound(Rhs, T)(Rhs rhs, T bound)
+            {
+                thereWereErrors = true;
+                return bound;
+            }
+            static T onUpperBound(Rhs, T)(Rhs rhs, T bound)
+            {
+                thereWereErrors = true;
+                return bound;
+            }
+        }
+        auto x = checked!MyHook(byte.min);
+        x -= 1;
+        assert(MyHook.thereWereErrors);
+        MyHook.thereWereErrors = false;
+        x = byte.max;
+        x += 1;
+        assert(MyHook.thereWereErrors);
+    
+}
+
+@safe @nogc pure nothrow unittest
+{
+    import std.checkedint;
+
+    // Hook that ignores all problems.
+    static struct Ignore
+    {
+        @nogc nothrow pure @safe static:
+        Dst onBadCast(Dst, Src)(Src src) { return cast(Dst) src; }
+        Lhs onLowerBound(Rhs, T)(Rhs rhs, T bound) { return cast(T) rhs; }
+        T onUpperBound(Rhs, T)(Rhs rhs, T bound) { return cast(T) rhs; }
+        bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs) { return lhs == rhs; }
+        int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs) { return (lhs > rhs) - (lhs < rhs); }
+        typeof(~Lhs()) onOverflow(string x, Lhs)(ref Lhs lhs) { return mixin(x ~ "lhs"); }
+        typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs)
+        {
+            static if (x == "/")
+                return typeof(lhs / rhs).min;
+            else
+                return mixin("lhs" ~ x ~ "rhs");
+        }
+    }
+
+    auto x = Checked!(int, Ignore)(5) + 7;
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+    static assert(is(typeof(checked(42)) == Checked!int));
+    assert(checked(42) == Checked!int(42));
+    static assert(is(typeof(checked!WithNaN(42)) == Checked!(int, WithNaN)));
+    assert(checked!WithNaN(42) == Checked!(int, WithNaN)(42));
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+    void test(T)()
+    {
+        Checked!(int, Abort) x;
+        x = 42;
+        auto x1 = cast(T) x;
+        assert(x1 == 42);
+        //x1 += long(int.max);
+    }
+    test!short;
+    test!(const short);
+    test!(immutable short);
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+    void test(T)()
+    {
+        Checked!(int, Throw) x;
+        x = 42;
+        auto x1 = cast(T) x;
+        assert(x1 == 42);
+        x = T.max + 1;
+        import std.exception : assertThrown, assertNotThrown;
+        assertThrown(cast(T) x);
+        x = x.max;
+        assertThrown(x += 42);
+        assertThrown(x += 42L);
+        x = x.min;
+        assertThrown(-x);
+        assertThrown(x -= 42);
+        assertThrown(x -= 42L);
+        x = -1;
+        assertNotThrown(x == -1);
+        assertThrown(x == uint(-1));
+        assertNotThrown(x <= -1);
+        assertThrown(x <= uint(-1));
+    }
+    test!short;
+    test!(const short);
+    test!(immutable short);
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+        auto x = checked!Warn(-42);
+        // Passes
+        assert(x == -42);
+        // Passes but prints a warning
+        // assert(x == uint(-42));
+    
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+        auto x = checked!Warn(-42);
+        // Passes
+        assert(x <= -42);
+        // Passes but prints a warning
+        // assert(x <= uint(-42));
+    
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+    auto x = checked!Warn(42);
+    short x1 = cast(short) x;
+    //x += long(int.max);
+    auto y = checked!Warn(cast(const int) 42);
+    short y1 = cast(const byte) y;
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+    alias opEqualsProper = ProperCompare.hookOpEquals;
+    assert(opEqualsProper(42, 42));
+    assert(opEqualsProper(42.0, 42.0));
+    assert(opEqualsProper(42u, 42));
+    assert(opEqualsProper(42, 42u));
+    assert(-1 == 4294967295u);
+    assert(!opEqualsProper(-1, 4294967295u));
+    assert(!opEqualsProper(const uint(-1), -1));
+    assert(!opEqualsProper(uint(-1), -1.0));
+    assert(3_000_000_000U == -1_294_967_296);
+    assert(!opEqualsProper(3_000_000_000U, -1_294_967_296));
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+        auto x = checked!WithNaN(422);
+        assert((cast(ubyte) x) == 255);
+        x = checked!WithNaN(-422);
+        assert((cast(byte) x) == -128);
+        assert(cast(short) x == -422);
+        assert(cast(bool) x);
+        x = x.init; // set back to NaN
+        assert(x != true);
+        assert(x != false);
+    
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+        Checked!(int, WithNaN) x;
+        assert(!(x < 0) && !(x > 0) && !(x == 0));
+        x = 1;
+        assert(x > 0 && !(x < 0) && !(x == 0));
+    
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+        Checked!(int, WithNaN) x;
+        ++x;
+        assert(x.isNaN);
+        x = 1;
+        assert(!x.isNaN);
+        x = -x;
+        ++x;
+        assert(!x.isNaN);
+    
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+        Checked!(int, WithNaN) x;
+        assert((x + 1).isNaN);
+        x = 100;
+        assert(!(x + 1).isNaN);
+    
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+        Checked!(int, WithNaN) x;
+        assert((1 + x).isNaN);
+        x = 100;
+        assert(!(1 + x).isNaN);
+    
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+        Checked!(int, WithNaN) x;
+        x += 4;
+        assert(x.isNaN);
+        x = 0;
+        x += 4;
+        assert(!x.isNaN);
+        x += int.max;
+        assert(x.isNaN);
+    
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+    auto x1 = Checked!(int, WithNaN)();
+    assert(x1.isNaN);
+    assert(x1.get == int.min);
+    assert(x1 != x1);
+    assert(!(x1 < x1));
+    assert(!(x1 > x1));
+    assert(!(x1 == x1));
+    ++x1;
+    assert(x1.isNaN);
+    assert(x1.get == int.min);
+    --x1;
+    assert(x1.isNaN);
+    assert(x1.get == int.min);
+    x1 = 42;
+    assert(!x1.isNaN);
+    assert(x1 == x1);
+    assert(x1 <= x1);
+    assert(x1 >= x1);
+    static assert(x1.min == int.min + 1);
+    x1 += long(int.max);
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+    auto x1 = Checked!(int, WithNaN)();
+    assert(x1.isNaN);
+    x1 = 1;
+    assert(!x1.isNaN);
+    x1 = x1.init;
+    assert(x1.isNaN);
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+        auto x = checked!Saturate(short(100));
+        x += 33000;
+        assert(x == short.max);
+        x -= 70000;
+        assert(x == short.min);
+    
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+        assert(checked!Saturate(int.max) + 1 == int.max);
+        assert(checked!Saturate(100) ^^ 10 == int.max);
+        assert(checked!Saturate(-100) ^^ 10 == int.max);
+        assert(checked!Saturate(100) / 0 == int.max);
+        assert(checked!Saturate(100) << -1 == 0);
+        assert(checked!Saturate(100) << 33 == int.max);
+        assert(checked!Saturate(100) >> -1 == int.max);
+        assert(checked!Saturate(100) >> 33 == 0);
+    
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+    auto x = checked!Saturate(int.max);
+    ++x;
+    assert(x == int.max);
+    --x;
+    assert(x == int.max - 1);
+    x = int.min;
+    assert(-x == int.max);
+    x -= 42;
+    assert(x == int.min);
+    assert(x * -2 == int.max);
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+    bool overflow;
+    assert(opChecked!"+"(const short(1), short(1), overflow) == 2 && !overflow);
+    assert(opChecked!"+"(1, 1, overflow) == 2 && !overflow);
+    assert(opChecked!"+"(1, 1u, overflow) == 2 && !overflow);
+    assert(opChecked!"+"(-1, 1u, overflow) == 0 && !overflow);
+    assert(opChecked!"+"(1u, -1, overflow) == 0 && !overflow);
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+    bool overflow;
+    assert(opChecked!"-"(1, 1, overflow) == 0 && !overflow);
+    assert(opChecked!"-"(1, 1u, overflow) == 0 && !overflow);
+    assert(opChecked!"-"(1u, -1, overflow) == 2 && !overflow);
+    assert(opChecked!"-"(-1, 1u, overflow) == 0 && overflow);
+}
+
+@safe unittest
+{
+    import std.checkedint;
+
+    struct MyHook
+    {
+        static size_t hookToHash(T)(const T payload) nothrow @trusted
+        {
+            return .hashOf(payload);
+        }
+    }
+
+    int[Checked!(int, MyHook)] aa;
+    Checked!(int, MyHook) var = 42;
+    aa[var] = 100;
+
+    assert(aa[var] == 100);
+
+    int[Checked!(int, Abort)] bb;
+    Checked!(int, Abort) var2 = 42;
+    bb[var2] = 100;
+
+    assert(bb[var2] == 100);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_complex.d b/libphobos/testsuite/libphobos.phobos/std_complex.d
new file mode 100644
index 0000000000000000000000000000000000000000..44ac5d7fdf2a6e6b06c34ade00fc126439a0cd31
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_complex.d
@@ -0,0 +1,403 @@
+@safe pure nothrow unittest
+{
+    import std.complex;
+
+    auto a = complex(1.0);
+    static assert(is(typeof(a) == Complex!double));
+    assert(a.re == 1.0);
+    assert(a.im == 0.0);
+
+    auto b = complex(2.0L);
+    static assert(is(typeof(b) == Complex!real));
+    assert(b.re == 2.0L);
+    assert(b.im == 0.0L);
+
+    auto c = complex(1.0, 2.0);
+    static assert(is(typeof(c) == Complex!double));
+    assert(c.re == 1.0);
+    assert(c.im == 2.0);
+
+    auto d = complex(3.0, 4.0L);
+    static assert(is(typeof(d) == Complex!real));
+    assert(d.re == 3.0);
+    assert(d.im == 4.0L);
+
+    auto e = complex(1);
+    static assert(is(typeof(e) == Complex!double));
+    assert(e.re == 1);
+    assert(e.im == 0);
+
+    auto f = complex(1L, 2);
+    static assert(is(typeof(f) == Complex!double));
+    assert(f.re == 1L);
+    assert(f.im == 2);
+
+    auto g = complex(3, 4.0L);
+    static assert(is(typeof(g) == Complex!real));
+    assert(g.re == 3);
+    assert(g.im == 4.0L);
+}
+
+@safe unittest
+{
+    import std.complex;
+
+        auto c = complex(1.2, 3.4);
+
+        // Vanilla toString formatting:
+        assert(c.toString() == "1.2+3.4i");
+
+        // Formatting with std.string.format specs: the precision and width
+        // specifiers apply to both the real and imaginary parts of the
+        // complex number.
+        import std.format : format;
+        assert(format("%.2f", c)  == "1.20+3.40i");
+        assert(format("%4.1f", c) == " 1.2+ 3.4i");
+    
+}
+
+@safe pure nothrow unittest
+{
+    import std.complex;
+
+    static import core.math;
+    assert(abs(complex(1.0)) == 1.0);
+    assert(abs(complex(0.0, 1.0)) == 1.0);
+    assert(abs(complex(1.0L, -2.0L)) == core.math.sqrt(5.0L));
+}
+
+@safe pure nothrow unittest
+{
+    import std.complex;
+
+    import std.math.operations : isClose;
+    assert(sqAbs(complex(0.0)) == 0.0);
+    assert(sqAbs(complex(1.0)) == 1.0);
+    assert(sqAbs(complex(0.0, 1.0)) == 1.0);
+    assert(isClose(sqAbs(complex(1.0L, -2.0L)), 5.0L));
+    assert(isClose(sqAbs(complex(-3.0L, 1.0L)), 10.0L));
+    assert(isClose(sqAbs(complex(1.0f,-1.0f)), 2.0f));
+}
+
+@safe pure nothrow unittest
+{
+    import std.complex;
+
+    import std.math.constants : PI_2, PI_4;
+    assert(arg(complex(1.0)) == 0.0);
+    assert(arg(complex(0.0L, 1.0L)) == PI_2);
+    assert(arg(complex(1.0L, 1.0L)) == PI_4);
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.complex;
+
+    import std.math.operations : isClose;
+    import std.math.constants : PI;
+    assert(norm(complex(3.0, 4.0)) == 25.0);
+    assert(norm(fromPolar(5.0, 0.0)) == 25.0);
+    assert(isClose(norm(fromPolar(5.0L, PI / 6)), 25.0L));
+    assert(isClose(norm(fromPolar(5.0L, 13 * PI / 6)), 25.0L));
+}
+
+@safe pure nothrow unittest
+{
+    import std.complex;
+
+    assert(conj(complex(1.0)) == complex(1.0));
+    assert(conj(complex(1.0, 2.0)) == complex(1.0, -2.0));
+}
+
+@safe pure nothrow unittest
+{
+    import std.complex;
+
+    assert(proj(complex(1.0)) == complex(1.0));
+    assert(proj(complex(double.infinity, 5.0)) == complex(double.infinity, 0.0));
+    assert(proj(complex(5.0, -double.infinity)) == complex(double.infinity, -0.0));
+}
+
+@safe pure nothrow unittest
+{
+    import std.complex;
+
+    import core.math;
+    import std.math.operations : isClose;
+    import std.math.algebraic : sqrt;
+    import std.math.constants : PI_4;
+    auto z = fromPolar(core.math.sqrt(2.0L), PI_4);
+    assert(isClose(z.re, 1.0L));
+    assert(isClose(z.im, 1.0L));
+}
+
+@safe pure nothrow unittest
+{
+    import std.complex;
+
+    static import core.math;
+    assert(sin(complex(0.0)) == 0.0);
+    assert(sin(complex(2.0, 0)) == core.math.sin(2.0));
+}
+
+@safe pure nothrow unittest
+{
+    import std.complex;
+
+    static import core.math;
+    static import std.math;
+    assert(cos(complex(0.0)) == 1.0);
+    assert(cos(complex(1.3, 0.0)) == core.math.cos(1.3));
+    assert(cos(complex(0.0, 5.2)) == std.math.cosh(5.2));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.complex;
+
+    static import std.math;
+
+    int ceqrel(T)(const Complex!T x, const Complex!T y) @safe pure nothrow @nogc
+    {
+        import std.math.operations : feqrel;
+        const r = feqrel(x.re, y.re);
+        const i = feqrel(x.im, y.im);
+        return r < i ? r : i;
+    }
+    assert(ceqrel(tan(complex(1.0, 0.0)), complex(std.math.tan(1.0), 0.0)) >= double.mant_dig - 2);
+    assert(ceqrel(tan(complex(0.0, 1.0)), complex(0.0, std.math.tanh(1.0))) >= double.mant_dig - 2);
+}
+
+@safe pure nothrow unittest
+{
+    import std.complex;
+
+    import std.math.operations : isClose;
+    import std.math.constants : PI;
+    assert(asin(complex(0.0)) == 0.0);
+    assert(isClose(asin(complex(0.5L)), PI / 6));
+}
+
+@safe pure nothrow unittest
+{
+    import std.complex;
+
+    import std.math.operations : isClose;
+    import std.math.constants : PI;
+    import std.math.trigonometry : std_math_acos = acos;
+    assert(acos(complex(0.0)) == std_math_acos(0.0));
+    assert(isClose(acos(complex(0.5L)), PI / 3));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.complex;
+
+    import std.math.operations : isClose;
+    import std.math.constants : PI;
+    assert(atan(complex(0.0)) == 0.0);
+    assert(isClose(atan(sqrt(complex(3.0L))), PI / 3));
+    assert(isClose(atan(sqrt(complex(3.0f))), float(PI) / 3));
+}
+
+@safe pure nothrow unittest
+{
+    import std.complex;
+
+    static import std.math;
+    assert(sinh(complex(0.0)) == 0.0);
+    assert(sinh(complex(1.0L)) == std.math.sinh(1.0L));
+    assert(sinh(complex(1.0f)) == std.math.sinh(1.0f));
+}
+
+@safe pure nothrow unittest
+{
+    import std.complex;
+
+    static import std.math;
+    assert(cosh(complex(0.0)) == 1.0);
+    assert(cosh(complex(1.0L)) == std.math.cosh(1.0L));
+    assert(cosh(complex(1.0f)) == std.math.cosh(1.0f));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.complex;
+
+    import std.math.operations : isClose;
+    import std.math.trigonometry : std_math_tanh = tanh;
+    assert(tanh(complex(0.0)) == 0.0);
+    assert(isClose(tanh(complex(1.0L)), std_math_tanh(1.0L)));
+    assert(isClose(tanh(complex(1.0f)), std_math_tanh(1.0f)));
+}
+
+@safe pure nothrow unittest
+{
+    import std.complex;
+
+    import std.math.operations : isClose;
+    import std.math.trigonometry : std_math_asinh = asinh;
+    assert(asinh(complex(0.0)) == 0.0);
+    assert(isClose(asinh(complex(1.0L)), std_math_asinh(1.0L)));
+    assert(isClose(asinh(complex(1.0f)), std_math_asinh(1.0f)));
+}
+
+@safe pure nothrow unittest
+{
+    import std.complex;
+
+    import std.math.operations : isClose;
+    import std.math.trigonometry : std_math_acosh = acosh;
+    assert(acosh(complex(1.0)) == 0.0);
+    assert(isClose(acosh(complex(3.0L)), std_math_acosh(3.0L)));
+    assert(isClose(acosh(complex(3.0f)), std_math_acosh(3.0f)));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.complex;
+
+    import std.math.operations : isClose;
+    import std.math.trigonometry : std_math_atanh = atanh;
+    assert(atanh(complex(0.0)) == 0.0);
+    assert(isClose(atanh(complex(0.5L)), std_math_atanh(0.5L)));
+    assert(isClose(atanh(complex(0.5f)), std_math_atanh(0.5f)));
+}
+
+@safe pure nothrow unittest
+{
+    import std.complex;
+
+    import core.math : cos, sin;
+    assert(expi(0.0L) == 1.0L);
+    assert(expi(1.3e5L) == complex(cos(1.3e5L), sin(1.3e5L)));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.complex;
+
+    import std.math.trigonometry : cosh, sinh;
+    assert(coshisinh(3.0L) == complex(cosh(3.0L), sinh(3.0L)));
+}
+
+@safe pure nothrow unittest
+{
+    import std.complex;
+
+    static import core.math;
+    assert(sqrt(complex(0.0)) == 0.0);
+    assert(sqrt(complex(1.0L, 0)) == core.math.sqrt(1.0L));
+    assert(sqrt(complex(-1.0L, 0)) == complex(0, 1.0L));
+    assert(sqrt(complex(-8.0, -6.0)) == complex(1.0, -3.0));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.complex;
+
+    import std.math.operations : isClose;
+    import std.math.constants : PI;
+
+    assert(exp(complex(0.0, 0.0)) == complex(1.0, 0.0));
+
+    auto a = complex(2.0, 1.0);
+    assert(exp(conj(a)) == conj(exp(a)));
+
+    auto b = exp(complex(0.0L, 1.0L) * PI);
+    assert(isClose(b, -1.0L, 0.0, 1e-15));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.complex;
+
+    import core.math : sqrt;
+    import std.math.constants : PI;
+    import std.math.operations : isClose;
+
+    auto a = complex(2.0, 1.0);
+    assert(log(conj(a)) == conj(log(a)));
+
+    auto b = 2.0 * log10(complex(0.0, 1.0));
+    auto c = 4.0 * log10(complex(sqrt(2.0) / 2, sqrt(2.0) / 2));
+    assert(isClose(b, c, 0.0, 1e-15));
+
+    assert(log(complex(-1.0L, 0.0L)) == complex(0.0L, PI));
+    assert(log(complex(-1.0L, -0.0L)) == complex(0.0L, -PI));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.complex;
+
+    import core.math : sqrt;
+    import std.math.constants : LN10, PI;
+    import std.math.operations : isClose;
+
+    auto a = complex(2.0, 1.0);
+    assert(log10(a) == log(a) / log(complex(10.0)));
+
+    auto b = log10(complex(0.0, 1.0)) * 2.0;
+    auto c = log10(complex(sqrt(2.0) / 2, sqrt(2.0) / 2)) * 4.0;
+    assert(isClose(b, c, 0.0, 1e-15));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.complex;
+
+    import std.math.operations : isClose;
+
+    auto a = complex(1.0, 2.0);
+    assert(pow(a, 2) == a * a);
+    assert(pow(a, 3) == a * a * a);
+    assert(pow(a, -2) == 1.0 / (a * a));
+    assert(isClose(pow(a, -3), 1.0 / (a * a * a)));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.complex;
+
+    import std.math.operations : isClose;
+    assert(pow(complex(0.0), 2.0) == complex(0.0));
+    assert(pow(complex(5.0), 2.0) == complex(25.0));
+
+    auto a = pow(complex(-1.0, 0.0), 0.5);
+    assert(isClose(a, complex(0.0, +1.0), 0.0, 1e-16));
+
+    auto b = pow(complex(-1.0, -0.0), 0.5);
+    assert(isClose(b, complex(0.0, -1.0), 0.0, 1e-16));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.complex;
+
+    import std.math.operations : isClose;
+    import std.math.exponential : exp;
+    import std.math.constants : PI;
+    auto a = complex(0.0);
+    auto b = complex(2.0);
+    assert(pow(a, b) == complex(0.0));
+
+    auto c = complex(0.0L, 1.0L);
+    assert(isClose(pow(c, c), exp((-PI) / 2)));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.complex;
+
+    import std.math.operations : isClose;
+    assert(pow(2.0, complex(0.0)) == complex(1.0));
+    assert(pow(2.0, complex(5.0)) == complex(32.0));
+
+    auto a = pow(-2.0, complex(-1.0));
+    assert(isClose(a, complex(-0.5), 0.0, 1e-16));
+
+    auto b = pow(-0.5, complex(-1.0));
+    assert(isClose(b, complex(-2.0), 0.0, 1e-15));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_concurrency.d b/libphobos/testsuite/libphobos.phobos/std_concurrency.d
new file mode 100644
index 0000000000000000000000000000000000000000..46da87d47af6ba6c0a370d31082f9dd040f2d23e
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_concurrency.d
@@ -0,0 +1,235 @@
+@system unittest
+{
+    import std.concurrency;
+
+    __gshared string received;
+    static void spawnedFunc(Tid ownerTid)
+    {
+        import std.conv : text;
+        // Receive a message from the owner thread.
+        receive((int i){
+            received = text("Received the number ", i);
+
+            // Send a message back to the owner thread
+            // indicating success.
+            send(ownerTid, true);
+        });
+    }
+
+    // Start spawnedFunc in a new thread.
+    auto childTid = spawn(&spawnedFunc, thisTid);
+
+    // Send the number 42 to this new thread.
+    send(childTid, 42);
+
+    // Receive the result code.
+    auto wasSuccessful = receiveOnly!(bool);
+    assert(wasSuccessful);
+    assert(received == "Received the number 42");
+}
+
+@system unittest
+{
+    import std.concurrency;
+
+    static void f(string msg)
+    {
+        assert(msg == "Hello World");
+    }
+
+    auto tid = spawn(&f, "Hello World");
+}
+
+@system unittest
+{
+    import std.concurrency;
+
+    string msg = "Hello, World!";
+
+    static void f1(string msg) {}
+    static assert(!__traits(compiles, spawn(&f1, msg.dup)));
+    static assert( __traits(compiles, spawn(&f1, msg.idup)));
+
+    static void f2(char[] msg) {}
+    static assert(!__traits(compiles, spawn(&f2, msg.dup)));
+    static assert(!__traits(compiles, spawn(&f2, msg.idup)));
+}
+
+@system unittest
+{
+    import std.concurrency;
+
+    spawn({
+        ownerTid.send("This is so great!");
+    });
+    assert(receiveOnly!string == "This is so great!");
+}
+
+@system unittest
+{
+    import std.concurrency;
+
+    import std.variant : Variant;
+
+    auto process = ()
+    {
+        receive(
+            (int i) { ownerTid.send(1); },
+            (double f) { ownerTid.send(2); },
+            (Variant v) { ownerTid.send(3); }
+        );
+    };
+
+    {
+        auto tid = spawn(process);
+        send(tid, 42);
+        assert(receiveOnly!int == 1);
+    }
+
+    {
+        auto tid = spawn(process);
+        send(tid, 3.14);
+        assert(receiveOnly!int == 2);
+    }
+
+    {
+        auto tid = spawn(process);
+        send(tid, "something else");
+        assert(receiveOnly!int == 3);
+    }
+}
+
+@system unittest
+{
+    import std.concurrency;
+
+    auto tid = spawn(
+    {
+        assert(receiveOnly!int == 42);
+    });
+    send(tid, 42);
+}
+
+@system unittest
+{
+    import std.concurrency;
+
+    auto tid = spawn(
+    {
+        assert(receiveOnly!string == "text");
+    });
+    send(tid, "text");
+}
+
+@system unittest
+{
+    import std.concurrency;
+
+    struct Record { string name; int age; }
+
+    auto tid = spawn(
+    {
+        auto msg = receiveOnly!(double, Record);
+        assert(msg[0] == 0.5);
+        assert(msg[1].name == "Alice");
+        assert(msg[1].age == 31);
+    });
+
+    send(tid, 0.5, Record("Alice", 31));
+}
+
+@system unittest
+{
+    import std.concurrency;
+
+    auto tid = spawn({
+        int i;
+        while (i < 9)
+            i = receiveOnly!int;
+
+        ownerTid.send(i * 2);
+    });
+
+    auto r = new Generator!int({
+        foreach (i; 1 .. 10)
+            yield(i);
+    });
+
+    foreach (e; r)
+        tid.send(e);
+
+    assert(receiveOnly!int == 18);
+}
+
+@system unittest
+{
+    import std.concurrency;
+
+    import std.range;
+
+    InputRange!int myIota = iota(10).inputRangeObject;
+
+    myIota.popFront();
+    myIota.popFront();
+    assert(myIota.moveFront == 2);
+    assert(myIota.front == 2);
+    myIota.popFront();
+    assert(myIota.front == 3);
+
+    //can be assigned to std.range.interfaces.InputRange directly
+    myIota = new Generator!int(
+    {
+        foreach (i; 0 .. 10) yield(i);
+    });
+
+    myIota.popFront();
+    myIota.popFront();
+    assert(myIota.moveFront == 2);
+    assert(myIota.front == 2);
+    myIota.popFront();
+    assert(myIota.front == 3);
+
+    size_t[2] counter = [0, 0];
+    foreach (i, unused; myIota) counter[] += [1, i];
+
+    assert(myIota.empty);
+    assert(counter == [7, 21]);
+}
+
+@system unittest
+{
+    import std.concurrency;
+
+    static class MySingleton
+    {
+        static MySingleton instance()
+        {
+            __gshared MySingleton inst;
+            return initOnce!inst(new MySingleton);
+        }
+    }
+
+    assert(MySingleton.instance !is null);
+}
+
+@system unittest
+{
+    import std.concurrency;
+
+    import core.sync.mutex : Mutex;
+
+    static shared bool varA, varB;
+    static shared Mutex m;
+    m = new shared Mutex;
+
+    spawn({
+        // use a different mutex for varB to avoid a dead-lock
+        initOnce!varB(true, m);
+        ownerTid.send(true);
+    });
+    // init depends on the result of the spawned thread
+    initOnce!varA(receiveOnly!bool);
+    assert(varA == true);
+    assert(varB == true);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_container_array.d b/libphobos/testsuite/libphobos.phobos/std_container_array.d
new file mode 100644
index 0000000000000000000000000000000000000000..abf8dd57ceb95adafb5c96b98bceef7e62055a6c
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_container_array.d
@@ -0,0 +1,56 @@
+pure @system unittest
+{
+    import std.container.array;
+
+    auto arr = Array!int(0, 2, 3);
+    assert(arr[0] == 0);
+    assert(arr.front == 0);
+    assert(arr.back == 3);
+
+    // reserve space
+    arr.reserve(1000);
+    assert(arr.length == 3);
+    assert(arr.capacity >= 1000);
+
+    // insertion
+    arr.insertBefore(arr[1..$], 1);
+    assert(arr.front == 0);
+    assert(arr.length == 4);
+
+    arr.insertBack(4);
+    assert(arr.back == 4);
+    assert(arr.length == 5);
+
+    // set elements
+    arr[1] *= 42;
+    assert(arr[1] == 42);
+}
+
+pure @system unittest
+{
+    import std.container.array;
+
+    import std.algorithm.comparison : equal;
+    auto arr = Array!int(1, 2, 3);
+
+    // concat
+    auto b = Array!int(11, 12, 13);
+    arr ~= b;
+    assert(arr.length == 6);
+
+    // slicing
+    assert(arr[1 .. 3].equal([2, 3]));
+
+    // remove
+    arr.linearRemove(arr[1 .. 3]);
+    assert(arr[0 .. 2].equal([1, 11]));
+}
+
+pure @system unittest
+{
+    import std.container.array;
+
+    auto arr = Array!bool([true, true, false, true, false]);
+    assert(arr.length == 5);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_container_binaryheap.d b/libphobos/testsuite/libphobos.phobos/std_container_binaryheap.d
new file mode 100644
index 0000000000000000000000000000000000000000..1ebcc6738feca377eb4636c1d11ec1b1546840f5
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_container_binaryheap.d
@@ -0,0 +1,102 @@
+@system unittest
+{
+    import std.container.binaryheap;
+
+    import std.algorithm.comparison : equal;
+    import std.range : take;
+    auto maxHeap = heapify([4, 7, 3, 1, 5]);
+    assert(maxHeap.take(3).equal([7, 5, 4]));
+
+    auto minHeap = heapify!"a > b"([4, 7, 3, 1, 5]);
+    assert(minHeap.take(3).equal([1, 3, 4]));
+}
+
+@system unittest
+{
+    import std.container.binaryheap;
+
+    import std.algorithm.comparison : equal;
+    int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ];
+    auto h = heapify(a);
+    // largest element
+    assert(h.front == 16);
+    // a has the heap property
+    assert(equal(a, [ 16, 14, 10, 8, 7, 9, 3, 2, 4, 1 ]));
+}
+
+@system unittest
+{
+    import std.container.binaryheap;
+
+    import std.algorithm.comparison : equal;
+    import std.range : take;
+    int[] a = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7];
+    auto top5 = heapify(a).take(5);
+    assert(top5.equal([16, 14, 10, 9, 8]));
+}
+
+@system unittest
+{
+    import std.container.binaryheap;
+
+    import std.conv : to;
+    import std.range.primitives;
+    {
+        // example from "Introduction to Algorithms" Cormen et al., p 146
+        int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ];
+        auto h = heapify(a);
+        h = heapify!"a < b"(a);
+        assert(h.front == 16);
+        assert(a == [ 16, 14, 10, 8, 7, 9, 3, 2, 4, 1 ]);
+        auto witness = [ 16, 14, 10, 9, 8, 7, 4, 3, 2, 1 ];
+        for (; !h.empty; h.removeFront(), witness.popFront())
+        {
+            assert(!witness.empty);
+            assert(witness.front == h.front);
+        }
+        assert(witness.empty);
+    }
+    {
+        int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ];
+        int[] b = new int[a.length];
+        BinaryHeap!(int[]) h = BinaryHeap!(int[])(b, 0);
+        foreach (e; a)
+        {
+            h.insert(e);
+        }
+        assert(b == [ 16, 14, 10, 8, 7, 3, 9, 1, 4, 2 ], to!string(b));
+    }
+}
+
+@system unittest
+{
+    import std.container.binaryheap;
+
+    import std.stdio;
+    import std.algorithm.comparison : equal;
+    import std.container.binaryheap;
+
+    int[] a = [ 4, 1, 3, 2, 16, 9, 10, 14, 8, 7 ];
+    auto h = heapify(a);
+
+    // Internal representation of Binary Heap tree
+    assert(a.equal([16, 14, 10, 8, 7, 9, 3, 2, 4, 1]));
+
+    h.replaceFront(30);
+    // Value 16 was replaced by 30
+    assert(a.equal([30, 14, 10, 8, 7, 9, 3, 2, 4, 1]));
+
+    // Making changes to the Store will be seen in the Heap
+    a[0] = 40;
+    assert(h.front() == 40);
+
+    // Inserting a new element will reallocate the Store, leaving
+    // the original Store unchanged.
+    h.insert(20);
+    assert(a.equal([40, 14, 10, 8, 7, 9, 3, 2, 4, 1]));
+
+    // Making changes to the original Store will not affect the Heap anymore
+    a[0] = 60;
+    assert(h.front() == 40);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_container_dlist.d b/libphobos/testsuite/libphobos.phobos/std_container_dlist.d
new file mode 100644
index 0000000000000000000000000000000000000000..f9510485a08e32b4f1385f281a23c9c7d04d59d1
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_container_dlist.d
@@ -0,0 +1,47 @@
+@safe unittest
+{
+    import std.container.dlist;
+
+    import std.algorithm.comparison : equal;
+    import std.container : DList;
+
+    auto s = DList!int(1, 2, 3);
+    assert(equal(s[], [1, 2, 3]));
+
+    s.removeFront();
+    assert(equal(s[], [2, 3]));
+    s.removeBack();
+    assert(equal(s[], [2]));
+
+    s.insertFront([4, 5]);
+    assert(equal(s[], [4, 5, 2]));
+    s.insertBack([6, 7]);
+    assert(equal(s[], [4, 5, 2, 6, 7]));
+
+    // If you want to apply range operations, simply slice it.
+    import std.algorithm.searching : countUntil;
+    import std.range : popFrontN, popBackN, walkLength;
+
+    auto sl = DList!int([1, 2, 3, 4, 5]);
+    assert(countUntil(sl[], 2) == 1);
+
+    auto r = sl[];
+    popFrontN(r, 2);
+    popBackN(r, 2);
+    assert(r.equal([3]));
+    assert(walkLength(r) == 1);
+
+    // DList.Range can be used to remove elements from the list it spans
+    auto nl = DList!int([1, 2, 3, 4, 5]);
+    for (auto rn = nl[]; !rn.empty;)
+        if (rn.front % 2 == 0)
+            nl.popFirstOf(rn);
+        else
+            rn.popFront();
+    assert(equal(nl[], [1, 3, 5]));
+    auto rs = nl[];
+    rs.popFront();
+    nl.remove(rs);
+    assert(equal(nl[], [1]));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_container_rbtree.d b/libphobos/testsuite/libphobos.phobos/std_container_rbtree.d
new file mode 100644
index 0000000000000000000000000000000000000000..a9d333550dc78fca3c093d7c32ea422e640051f5
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_container_rbtree.d
@@ -0,0 +1,60 @@
+@safe pure unittest
+{
+    import std.container.rbtree;
+
+    import std.algorithm.comparison : equal;
+    import std.container.rbtree;
+
+    auto rbt = redBlackTree(3, 1, 4, 2, 5);
+    assert(rbt.front == 1);
+    assert(equal(rbt[], [1, 2, 3, 4, 5]));
+
+    rbt.removeKey(1, 4);
+    assert(equal(rbt[], [2, 3, 5]));
+
+    rbt.removeFront();
+    assert(equal(rbt[], [3, 5]));
+
+    rbt.insert([1, 2, 4]);
+    assert(equal(rbt[], [1, 2, 3, 4, 5]));
+
+    // Query bounds in O(log(n))
+    assert(rbt.lowerBound(3).equal([1, 2]));
+    assert(rbt.equalRange(3).equal([3]));
+    assert(rbt.upperBound(3).equal([4, 5]));
+
+    // A Red Black tree with the highest element at front:
+    import std.range : iota;
+    auto maxTree = redBlackTree!"a > b"(iota(5));
+    assert(equal(maxTree[], [4, 3, 2, 1, 0]));
+
+    // adding duplicates will not add them, but return 0
+    auto rbt2 = redBlackTree(1, 3);
+    assert(rbt2.insert(1) == 0);
+    assert(equal(rbt2[], [1, 3]));
+    assert(rbt2.insert(2) == 1);
+
+    // however you can allow duplicates
+    auto ubt = redBlackTree!true([0, 1, 0, 1]);
+    assert(equal(ubt[], [0, 0, 1, 1]));
+}
+
+@safe pure unittest
+{
+    import std.container.rbtree;
+
+    import std.range : iota;
+
+    auto rbt1 = redBlackTree(0, 1, 5, 7);
+    auto rbt2 = redBlackTree!string("hello", "world");
+    auto rbt3 = redBlackTree!true(0, 1, 5, 7, 5);
+    auto rbt4 = redBlackTree!"a > b"(0, 1, 5, 7);
+    auto rbt5 = redBlackTree!("a > b", true)(0.1, 1.3, 5.9, 7.2, 5.9);
+
+    // also works with ranges
+    auto rbt6 = redBlackTree(iota(3));
+    auto rbt7 = redBlackTree!true(iota(3));
+    auto rbt8 = redBlackTree!"a > b"(iota(3));
+    auto rbt9 = redBlackTree!("a > b", true)(iota(3));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_container_slist.d b/libphobos/testsuite/libphobos.phobos/std_container_slist.d
new file mode 100644
index 0000000000000000000000000000000000000000..7c9a2b66ac1c90b5705e6a66cdfbb99a5244da21
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_container_slist.d
@@ -0,0 +1,28 @@
+@safe unittest
+{
+    import std.container.slist;
+
+    import std.algorithm.comparison : equal;
+    import std.container : SList;
+
+    auto s = SList!int(1, 2, 3);
+    assert(equal(s[], [1, 2, 3]));
+
+    s.removeFront();
+    assert(equal(s[], [2, 3]));
+
+    s.insertFront([5, 6]);
+    assert(equal(s[], [5, 6, 2, 3]));
+
+    // If you want to apply range operations, simply slice it.
+    import std.algorithm.searching : countUntil;
+    import std.range : popFrontN, walkLength;
+
+    auto sl = SList!int(1, 2, 3, 4, 5);
+    assert(countUntil(sl[], 2) == 1);
+
+    auto r = sl[];
+    popFrontN(r, 2);
+    assert(walkLength(r) == 3);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_container_util.d b/libphobos/testsuite/libphobos.phobos/std_container_util.d
new file mode 100644
index 0000000000000000000000000000000000000000..07d8831464b89cbbb27b27e5d893cb5cd41a4292
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_container_util.d
@@ -0,0 +1,51 @@
+@system unittest
+{
+    import std.container.util;
+
+    import std.algorithm.comparison : equal;
+    import std.container;
+
+    auto arr = make!(Array!int)([4, 2, 3, 1]);
+    assert(equal(arr[], [4, 2, 3, 1]));
+
+    auto rbt = make!(RedBlackTree!(int, "a > b"))([4, 2, 3, 1]);
+    assert(equal(rbt[], [4, 3, 2, 1]));
+
+    alias makeList = make!(SList!int);
+    auto slist = makeList(1, 2, 3);
+    assert(equal(slist[], [1, 2, 3]));
+}
+
+@safe unittest
+{
+    import std.container.util;
+
+    import std.container.array : Array;
+    import std.range : only, repeat;
+    import std.range.primitives : isInfinite;
+    static assert(__traits(compiles, { auto arr = make!Array(only(5)); }));
+    static assert(!__traits(compiles, { auto arr = make!Array(repeat(5)); }));
+}
+
+@system unittest
+{
+    import std.container.util;
+
+    import std.algorithm.comparison : equal;
+    import std.container.array, std.container.rbtree, std.container.slist;
+    import std.range : iota;
+
+    auto arr = make!Array(iota(5));
+    assert(equal(arr[], [0, 1, 2, 3, 4]));
+
+    auto rbtmax = make!(RedBlackTree, "a > b")(iota(5));
+    assert(equal(rbtmax[], [4, 3, 2, 1, 0]));
+
+    auto rbtmin = make!RedBlackTree(4, 1, 3, 2);
+    assert(equal(rbtmin[], [1, 2, 3, 4]));
+
+    alias makeList = make!SList;
+    auto list = makeList(1, 7, 42);
+    assert(equal(list[], [1, 7, 42]));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_conv.d b/libphobos/testsuite/libphobos.phobos/std_conv.d
new file mode 100644
index 0000000000000000000000000000000000000000..01f6fb7e2569cfb4ad2a450d355d0f0193e6ddf5
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_conv.d
@@ -0,0 +1,511 @@
+@safe unittest
+{
+    import std.conv;
+
+    import std.exception : assertThrown;
+    assertThrown!ConvException(to!int("abc"));
+}
+
+@safe unittest
+{
+    import std.conv;
+
+    import std.exception : assertThrown;
+    assertThrown!ConvOverflowException(to!ubyte(1_000_000));
+}
+
+@safe pure unittest
+{
+    import std.conv;
+
+    int a = 42;
+    int b = to!int(a);
+    double c = to!double(3.14); // c is double with value 3.14
+}
+
+@safe pure unittest
+{
+    import std.conv;
+
+    import std.exception : assertThrown;
+
+    int a = 420;
+    assert(to!long(a) == a);
+    assertThrown!ConvOverflowException(to!byte(a));
+
+    assert(to!int(4.2e6) == 4200000);
+    assertThrown!ConvOverflowException(to!uint(-3.14));
+    assert(to!uint(3.14) == 3);
+    assert(to!uint(3.99) == 3);
+    assert(to!int(-3.99) == -3);
+}
+
+@safe pure unittest
+{
+    import std.conv;
+
+    auto str = to!string(42, 16);
+    assert(str == "2A");
+    auto i = to!int(str, 16);
+    assert(i == 42);
+}
+
+@safe pure unittest
+{
+    import std.conv;
+
+    // 2^24 - 1, largest proper integer representable as float
+    int a = 16_777_215;
+    assert(to!int(to!float(a)) == a);
+    assert(to!int(to!float(-a)) == -a);
+}
+
+@safe pure unittest
+{
+    import std.conv;
+
+    import std.exception : assertThrown;
+
+    assert(to!char("a") == 'a');
+    assertThrown(to!char("ñ")); // 'ñ' does not fit into a char
+    assert(to!wchar("ñ") == 'ñ');
+    assertThrown(to!wchar("😃")); // '😃' does not fit into a wchar
+    assert(to!dchar("😃") == '😃');
+
+    // Using wstring or dstring as source type does not affect the result
+    assert(to!char("a"w) == 'a');
+    assert(to!char("a"d) == 'a');
+
+    // Two code points cannot be converted to a single one
+    assertThrown(to!char("ab"));
+}
+
+@safe pure unittest
+{
+    import std.conv;
+
+    import std.string : split;
+
+    int[] a = [1, 2, 3];
+    auto b = to!(float[])(a);
+    assert(b == [1.0f, 2, 3]);
+    string str = "1 2 3 4 5 6";
+    auto numbers = to!(double[])(split(str));
+    assert(numbers == [1.0, 2, 3, 4, 5, 6]);
+    int[string] c;
+    c["a"] = 1;
+    c["b"] = 2;
+    auto d = to!(double[wstring])(c);
+    assert(d["a"w] == 1 && d["b"w] == 2);
+}
+
+@safe unittest
+{
+    import std.conv;
+
+    int[string][double[int[]]] a;
+    auto b = to!(short[wstring][string[double[]]])(a);
+}
+
+@safe pure unittest
+{
+    import std.conv;
+
+    import std.exception : assertThrown;
+    // Testing object conversions
+    class A {}
+    class B : A {}
+    class C : A {}
+    A a1 = new A, a2 = new B, a3 = new C;
+    assert(to!B(a2) is a2);
+    assert(to!C(a3) is a3);
+    assertThrown!ConvException(to!B(a3));
+}
+
+@system pure unittest
+{
+    import std.conv;
+
+    // Conversion representing dynamic/static array with string
+    long[] a = [ 1, 3, 5 ];
+    assert(to!string(a) == "[1, 3, 5]");
+
+    // Conversion representing associative array with string
+    int[string] associativeArray = ["0":1, "1":2];
+    assert(to!string(associativeArray) == `["0":1, "1":2]` ||
+           to!string(associativeArray) == `["1":2, "0":1]`);
+
+    // char* to string conversion
+    assert(to!string(cast(char*) null) == "");
+    assert(to!string("foo\0".ptr) == "foo");
+
+    // Conversion reinterpreting void array to string
+    auto w = "abcx"w;
+    const(void)[] b = w;
+    assert(b.length == 8);
+
+    auto c = to!(wchar[])(b);
+    assert(c == "abcx");
+}
+
+@safe pure unittest
+{
+    import std.conv;
+
+    import std.exception : assertThrown;
+
+    enum E { a, b, c }
+    assert(to!E("a") == E.a);
+    assert(to!E("b") == E.b);
+    assertThrown!ConvException(to!E("A"));
+}
+
+@safe unittest
+{
+    import std.conv;
+
+    assert(roundTo!int(3.14) == 3);
+    assert(roundTo!int(3.49) == 3);
+    assert(roundTo!int(3.5) == 4);
+    assert(roundTo!int(3.999) == 4);
+    assert(roundTo!int(-3.14) == -3);
+    assert(roundTo!int(-3.49) == -3);
+    assert(roundTo!int(-3.5) == -4);
+    assert(roundTo!int(-3.999) == -4);
+    assert(roundTo!(const int)(to!(const double)(-3.999)) == -4);
+}
+
+@safe unittest
+{
+    import std.conv;
+
+    import std.typecons : Flag, Yes, No;
+    auto s = "true";
+    bool b = parse!bool(s);
+    assert(b);
+    auto s2 = "true";
+    bool b2 = parse!(bool, string, No.doCount)(s2);
+    assert(b2);
+    auto s3 = "true";
+    auto b3 = parse!(bool, string, Yes.doCount)(s3);
+    assert(b3.data && b3.count == 4);
+    auto s4 = "falSE";
+    auto b4 = parse!(bool, string, Yes.doCount)(s4);
+    assert(!b4.data && b4.count == 5);
+}
+
+@safe pure unittest
+{
+    import std.conv;
+
+    import std.typecons : Flag, Yes, No;
+    string s = "123";
+    auto a = parse!int(s);
+    assert(a == 123);
+
+    string s1 = "123";
+    auto a1 = parse!(int, string, Yes.doCount)(s1);
+    assert(a1.data == 123 && a1.count == 3);
+}
+
+@safe pure unittest
+{
+    import std.conv;
+
+    import std.string : tr;
+    import std.typecons : Flag, Yes, No;
+    string test = "123 \t  76.14";
+    auto a = parse!uint(test);
+    assert(a == 123);
+    assert(test == " \t  76.14"); // parse bumps string
+    test = tr(test, " \t\n\r", "", "d"); // skip ws
+    assert(test == "76.14");
+    auto b = parse!double(test);
+    assert(b == 76.14);
+    assert(test == "");
+
+    string test2 = "123 \t  76.14";
+    auto a2 = parse!(uint, string, Yes.doCount)(test2);
+    assert(a2.data == 123 && a2.count == 3);
+    assert(test2 == " \t  76.14");// parse bumps string
+    test2 = tr(test2, " \t\n\r", "", "d"); // skip ws
+    assert(test2 == "76.14");
+    auto b2 = parse!(double, string, Yes.doCount)(test2);
+    assert(b2.data == 76.14 && b2.count == 5);
+    assert(test2 == "");
+
+}
+
+@safe unittest
+{
+    import std.conv;
+
+    import std.typecons : Flag, Yes, No, tuple;
+    enum EnumType : bool { a = true, b = false, c = a }
+
+    auto str = "a";
+    assert(parse!EnumType(str) == EnumType.a);
+    auto str2 = "a";
+    assert(parse!(EnumType, string, No.doCount)(str2) == EnumType.a);
+    auto str3 = "a";
+    assert(parse!(EnumType, string, Yes.doCount)(str3) == tuple(EnumType.a, 1));
+
+}
+
+@safe unittest
+{
+    import std.conv;
+
+    import std.math.operations : isClose;
+    import std.math.traits : isNaN, isInfinity;
+    import std.typecons : Flag, Yes, No;
+    auto str = "123.456";
+    assert(parse!double(str).isClose(123.456));
+    auto str2 = "123.456";
+    assert(parse!(double, string, No.doCount)(str2).isClose(123.456));
+    auto str3 = "123.456";
+    auto r = parse!(double, string, Yes.doCount)(str3);
+    assert(r.data.isClose(123.456));
+    assert(r.count == 7);
+    auto str4 = "-123.456";
+    r = parse!(double, string, Yes.doCount)(str4);
+    assert(r.data.isClose(-123.456));
+    assert(r.count == 8);
+    auto str5 = "+123.456";
+    r = parse!(double, string, Yes.doCount)(str5);
+    assert(r.data.isClose(123.456));
+    assert(r.count == 8);
+    auto str6 = "inf0";
+    r = parse!(double, string, Yes.doCount)(str6);
+    assert(isInfinity(r.data) && r.count == 3 && str6 == "0");
+    auto str7 = "-0";
+    auto r2 = parse!(float, string, Yes.doCount)(str7);
+    assert(r2.data.isClose(0.0) && r2.count == 2);
+    auto str8 = "nan";
+    auto r3 = parse!(real, string, Yes.doCount)(str8);
+    assert(isNaN(r3.data) && r3.count == 3);
+}
+
+@safe pure unittest
+{
+    import std.conv;
+
+    import std.typecons : Flag, Yes, No;
+    auto s = "Hello, World!";
+    char first = parse!char(s);
+    assert(first == 'H');
+    assert(s == "ello, World!");
+    char second = parse!(char, string, No.doCount)(s);
+    assert(second == 'e');
+    assert(s == "llo, World!");
+    auto third = parse!(char, string, Yes.doCount)(s);
+    assert(third.data == 'l' && third.count == 1);
+    assert(s == "lo, World!");
+}
+
+@safe pure unittest
+{
+    import std.conv;
+
+    import std.exception : assertThrown;
+    import std.typecons : Flag, Yes, No;
+
+    alias NullType = typeof(null);
+    auto s1 = "null";
+    assert(parse!NullType(s1) is null);
+    assert(s1 == "");
+
+    auto s2 = "NUll"d;
+    assert(parse!NullType(s2) is null);
+    assert(s2 == "");
+
+    auto s3 = "nuLlNULl";
+    assert(parse!(NullType, string, No.doCount)(s3) is null);
+    auto r = parse!(NullType, string, Yes.doCount)(s3);
+    assert(r.data is null && r.count == 4);
+
+    auto m = "maybe";
+    assertThrown!ConvException(parse!NullType(m));
+    assertThrown!ConvException(parse!(NullType, string, Yes.doCount)(m));
+    assert(m == "maybe");  // m shouldn't change on failure
+
+    auto s = "NULL";
+    assert(parse!(const NullType)(s) is null);
+}
+
+@safe pure unittest
+{
+    import std.conv;
+
+    import std.typecons : Flag, Yes, No;
+    auto s1 = `[['h', 'e', 'l', 'l', 'o'], "world"]`;
+    auto a1 = parse!(string[])(s1);
+    assert(a1 == ["hello", "world"]);
+
+    auto s2 = `["aaa", "bbb", "ccc"]`;
+    auto a2 = parse!(string[])(s2);
+    assert(a2 == ["aaa", "bbb", "ccc"]);
+
+    auto s3 = `[['h', 'e', 'l', 'l', 'o'], "world"]`;
+    auto len3 = s3.length;
+    auto a3 = parse!(string[], string, Yes.doCount)(s3);
+    assert(a3.data == ["hello", "world"]);
+    assert(a3.count == len3);
+}
+
+@safe pure unittest
+{
+    import std.conv;
+
+    import std.typecons : Flag, Yes, No, tuple;
+    import std.range.primitives : save;
+    import std.array : assocArray;
+    auto s1 = "[1:10, 2:20, 3:30]";
+    auto copyS1 = s1.save;
+    auto aa1 = parse!(int[int])(s1);
+    assert(aa1 == [1:10, 2:20, 3:30]);
+    assert(tuple([1:10, 2:20, 3:30], copyS1.length) == parse!(int[int], string, Yes.doCount)(copyS1));
+
+    auto s2 = `["aaa":10, "bbb":20, "ccc":30]`;
+    auto copyS2 = s2.save;
+    auto aa2 = parse!(int[string])(s2);
+    assert(aa2 == ["aaa":10, "bbb":20, "ccc":30]);
+    assert(tuple(["aaa":10, "bbb":20, "ccc":30], copyS2.length) ==
+        parse!(int[string], string, Yes.doCount)(copyS2));
+
+    auto s3 = `["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]]`;
+    auto copyS3 = s3.save;
+    auto aa3 = parse!(int[][string])(s3);
+    assert(aa3 == ["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]]);
+    assert(tuple(["aaa":[1], "bbb":[2,3], "ccc":[4,5,6]], copyS3.length) ==
+        parse!(int[][string], string, Yes.doCount)(copyS3));
+
+    auto s4 = `[]`;
+    int[int] emptyAA;
+    assert(tuple(emptyAA, s4.length) == parse!(int[int], string, Yes.doCount)(s4));
+}
+
+@safe unittest
+{
+    import std.conv;
+
+    assert( text(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"c);
+    assert(wtext(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"w);
+    assert(dtext(42, ' ', 1.5, ": xyz") == "42 1.5: xyz"d);
+}
+
+@safe unittest
+{
+    import std.conv;
+
+    // Same as 0177
+    auto a = octal!177;
+    // octal is a compile-time device
+    enum b = octal!160;
+    // Create an unsigned octal
+    auto c = octal!"1_000_000u";
+    // Leading zeros are allowed when converting from a string
+    auto d = octal!"0001_200_000";
+}
+
+@safe unittest
+{
+    import std.conv;
+
+    import std.traits : Unsigned;
+    immutable int s = 42;
+    auto u1 = unsigned(s); //not qualified
+    static assert(is(typeof(u1) == uint));
+    Unsigned!(typeof(s)) u2 = unsigned(s); //same qualification
+    static assert(is(typeof(u2) == immutable uint));
+    immutable u3 = unsigned(s); //explicitly qualified
+}
+
+@safe unittest
+{
+    import std.conv;
+
+    import std.traits : Signed;
+
+    immutable uint u = 42;
+    auto s1 = signed(u); //not qualified
+    static assert(is(typeof(s1) == int));
+    Signed!(typeof(u)) s2 = signed(u); //same qualification
+    static assert(is(typeof(s2) == immutable int));
+    immutable s3 = signed(u); //explicitly qualified
+}
+
+@safe unittest
+{
+    import std.conv;
+
+    enum A { a = 42 }
+    static assert(is(typeof(A.a.asOriginalType) == int));
+    assert(A.a.asOriginalType == 42);
+    enum B : double { a = 43 }
+    static assert(is(typeof(B.a.asOriginalType) == double));
+    assert(B.a.asOriginalType == 43);
+}
+
+@system unittest
+{
+    import std.conv;
+
+    // Regular cast, which has been verified to be legal by the programmer:
+    {
+        long x;
+        auto y = cast(int) x;
+    }
+
+    // However this will still compile if 'x' is changed to be a pointer:
+    {
+        long* x;
+        auto y = cast(int) x;
+    }
+
+    // castFrom provides a more reliable alternative to casting:
+    {
+        long x;
+        auto y = castFrom!long.to!int(x);
+    }
+
+    // Changing the type of 'x' will now issue a compiler error,
+    // allowing bad casts to be caught before it's too late:
+    {
+        long* x;
+        static assert(
+            !__traits(compiles, castFrom!long.to!int(x))
+        );
+
+        // if cast is still needed, must be changed to:
+        auto y = castFrom!(long*).to!int(x);
+    }
+}
+
+@safe unittest
+{
+    import std.conv;
+
+    // conversion at compile time
+    auto string1 = hexString!"304A314B";
+    assert(string1 == "0J1K");
+    auto string2 = hexString!"304A314B"w;
+    assert(string2 == "0J1K"w);
+    auto string3 = hexString!"304A314B"d;
+    assert(string3 == "0J1K"d);
+}
+
+@safe unittest
+{
+    import std.conv;
+
+    import std.algorithm.comparison : equal;
+
+    assert(toChars(1).equal("1"));
+    assert(toChars(1_000_000).equal("1000000"));
+
+    assert(toChars!(2)(2U).equal("10"));
+    assert(toChars!(16)(255U).equal("ff"));
+    assert(toChars!(16, char, LetterCase.upper)(255U).equal("FF"));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_csv.d b/libphobos/testsuite/libphobos.phobos/std_csv.d
new file mode 100644
index 0000000000000000000000000000000000000000..e35df43da688388792c693acb284931233418599
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_csv.d
@@ -0,0 +1,228 @@
+@safe unittest
+{
+    import std.csv;
+
+    import std.exception : collectException;
+    import std.algorithm.searching : count;
+    string text = "a,b,c\nHello,65";
+    auto ex = collectException!CSVException(csvReader(text).count);
+    assert(ex.toString == "(Row: 0, Col: 0) Row 2's length 2 does not match previous length of 3.");
+}
+
+@safe unittest
+{
+    import std.csv;
+
+    import std.exception : collectException;
+    import std.algorithm.searching : count;
+    import std.typecons : Tuple;
+    string text = "a,b\nHello,65";
+    auto ex = collectException!CSVException(csvReader!(Tuple!(string,int))(text).count);
+    assert(ex.toString == "(Row: 1, Col: 2) Unexpected 'b' when converting from type string to type int");
+}
+
+@safe unittest
+{
+    import std.csv;
+
+    import std.exception : assertThrown;
+    string text = "a,\"b,c\nHello,65,2.5";
+    assertThrown!IncompleteCellException(text.csvReader(["a","b","c"]));
+}
+
+@safe unittest
+{
+    import std.csv;
+
+    import std.exception : assertThrown;
+    string text = "a,b,c\nHello,65,2.5";
+    assertThrown!HeaderMismatchException(text.csvReader(["b","c","invalid"]));
+}
+
+@safe unittest
+{
+    import std.csv;
+
+    import std.algorithm.comparison : equal;
+    import std.algorithm.searching : count;
+    import std.exception : assertThrown;
+
+    string text = "a,b,c\nHello,65,\"2.5";
+    assertThrown!IncompleteCellException(text.csvReader.count);
+
+    // ignore the exceptions and try to handle invalid CSV
+    auto firstLine = text.csvReader!(string, Malformed.ignore)(null).front;
+    assert(firstLine.equal(["Hello", "65", "2.5"]));
+}
+
+@safe unittest
+{
+    import std.csv;
+
+    import std.algorithm.comparison : equal;
+    string text = "76,26,22";
+    auto records = text.csvReader!int;
+    assert(records.equal!equal([
+        [76, 26, 22],
+    ]));
+}
+
+@safe unittest
+{
+    import std.csv;
+
+    import std.algorithm.comparison : equal;
+    string text = "Hello;65;2.5\nWorld;123;7.5";
+    struct Layout
+    {
+        string name;
+        int value;
+        double other;
+    }
+
+    auto records = text.csvReader!Layout(';');
+    assert(records.equal([
+        Layout("Hello", 65, 2.5),
+        Layout("World", 123, 7.5),
+    ]));
+}
+
+@safe unittest
+{
+    import std.csv;
+
+    string text = "A \" is now part of the data";
+    auto records = text.csvReader!(string, Malformed.ignore);
+    auto record = records.front;
+
+    assert(record.front == text);
+}
+
+@safe unittest
+{
+    import std.csv;
+
+    import std.algorithm.comparison : equal;
+    string text = "a,b,c\nHello,65,63.63\nWorld,123,3673.562";
+    auto records = text.csvReader!int(["b"]);
+
+    assert(records.equal!equal([
+        [65],
+        [123],
+    ]));
+}
+
+@safe unittest
+{
+    import std.csv;
+
+    import std.algorithm.comparison : equal;
+    string text = "a,b,c\nHello,65,2.5\nWorld,123,7.5";
+    struct Layout
+    {
+        int value;
+        double other;
+        string name;
+    }
+
+    auto records = text.csvReader!Layout(["b","c","a"]);
+    assert(records.equal([
+        Layout(65, 2.5, "Hello"),
+        Layout(123, 7.5, "World")
+    ]));
+}
+
+@safe unittest
+{
+    import std.csv;
+
+    string text = "a,b,c\nHello,65,63.63";
+    auto records = text.csvReader(null);
+
+    assert(records.header == ["a","b","c"]);
+}
+
+@safe unittest
+{
+    import std.csv;
+
+    import std.algorithm.comparison : equal;
+
+    string text = "76,26,22\n1,2\n3,4,5,6";
+    auto records = text.csvReader!int(',', '"', true);
+
+    assert(records.equal!equal([
+        [76, 26, 22],
+        [1, 2],
+        [3, 4, 5, 6]
+    ]));
+}
+
+@safe unittest
+{
+    import std.csv;
+
+    import std.algorithm.comparison : equal;
+
+    static struct Three
+    {
+        int a;
+        int b;
+        int c;
+    }
+
+    string text = "76,26,22\n1,2\n3,4,5,6";
+    auto records = text.csvReader!Three(',', '"', true);
+
+    assert(records.equal([
+        Three(76, 26, 22),
+        Three(1, 2, 0),
+        Three(3, 4, 5)
+    ]));
+}
+
+@safe unittest
+{
+    import std.csv;
+
+    import std.algorithm.comparison : equal;
+
+    auto text = "Name,Occupation,Salary\r" ~
+        "Joe,Carpenter,300000\nFred,Blacksmith\r\n";
+
+    auto r = csvReader!(string[string])(text, null, ',', '"', true);
+
+    assert(r.equal([
+        [ "Name" : "Joe", "Occupation" : "Carpenter", "Salary" : "300000" ],
+        [ "Name" : "Fred", "Occupation" : "Blacksmith" ]
+    ]));
+}
+
+@safe unittest
+{
+    import std.csv;
+
+    import std.array : appender;
+    import std.range.primitives : popFront;
+
+    string str = "65,63\n123,3673";
+
+    auto a = appender!(char[])();
+
+    csvNextToken(str,a,',','"');
+    assert(a.data == "65");
+    assert(str == ",63\n123,3673");
+
+    str.popFront();
+    a.shrinkTo(0);
+    csvNextToken(str,a,',','"');
+    assert(a.data == "63");
+    assert(str == "\n123,3673");
+
+    str.popFront();
+    a.shrinkTo(0);
+    csvNextToken(str,a,',','"');
+    assert(a.data == "123");
+    assert(str == ",3673");
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_datetime_date.d b/libphobos/testsuite/libphobos.phobos/std_datetime_date.d
new file mode 100644
index 0000000000000000000000000000000000000000..0ecf625562af9c17ba0708f457cca518ff7f43bf
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_datetime_date.d
@@ -0,0 +1,992 @@
+@safe pure unittest
+{
+    import std.datetime.date;
+
+    assert(Date(2018, 10, 1).month == Month.oct);
+    assert(DateTime(1, 1, 1).month == Month.jan);
+}
+
+@safe pure unittest
+{
+    import std.datetime.date;
+
+    assert(Date(2018, 10, 1).dayOfWeek == DayOfWeek.mon);
+    assert(DateTime(5, 5, 5).dayOfWeek == DayOfWeek.thu);
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).year == 1999);
+        assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).year == 2010);
+        assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).year == -7);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(DateTime(Date(0, 1, 1), TimeOfDay(12, 30, 33)).yearBC == 1);
+        assert(DateTime(Date(-1, 1, 1), TimeOfDay(10, 7, 2)).yearBC == 2);
+        assert(DateTime(Date(-100, 1, 1), TimeOfDay(4, 59, 0)).yearBC == 101);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        auto dt = DateTime(Date(2010, 1, 1), TimeOfDay(7, 30, 0));
+        dt.yearBC = 1;
+        assert(dt == DateTime(Date(0, 1, 1), TimeOfDay(7, 30, 0)));
+
+        dt.yearBC = 10;
+        assert(dt == DateTime(Date(-9, 1, 1), TimeOfDay(7, 30, 0)));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).month == 7);
+        assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).month == 10);
+        assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).month == 4);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(DateTime(Date(1999, 7, 6), TimeOfDay(9, 7, 5)).day == 6);
+        assert(DateTime(Date(2010, 10, 4), TimeOfDay(0, 0, 30)).day == 4);
+        assert(DateTime(Date(-7, 4, 5), TimeOfDay(7, 45, 2)).day == 5);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        auto dt1 = DateTime(2010, 1, 1, 12, 30, 33);
+        dt1.add!"months"(11);
+        assert(dt1 == DateTime(2010, 12, 1, 12, 30, 33));
+
+        auto dt2 = DateTime(2010, 1, 1, 12, 30, 33);
+        dt2.add!"months"(-11);
+        assert(dt2 == DateTime(2009, 2, 1, 12, 30, 33));
+
+        auto dt3 = DateTime(2000, 2, 29, 12, 30, 33);
+        dt3.add!"years"(1);
+        assert(dt3 == DateTime(2001, 3, 1, 12, 30, 33));
+
+        auto dt4 = DateTime(2000, 2, 29, 12, 30, 33);
+        dt4.add!"years"(1, AllowDayOverflow.no);
+        assert(dt4 == DateTime(2001, 2, 28, 12, 30, 33));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        auto dt1 = DateTime(2010, 1, 1, 12, 33, 33);
+        dt1.roll!"months"(1);
+        assert(dt1 == DateTime(2010, 2, 1, 12, 33, 33));
+
+        auto dt2 = DateTime(2010, 1, 1, 12, 33, 33);
+        dt2.roll!"months"(-1);
+        assert(dt2 == DateTime(2010, 12, 1, 12, 33, 33));
+
+        auto dt3 = DateTime(1999, 1, 29, 12, 33, 33);
+        dt3.roll!"months"(1);
+        assert(dt3 == DateTime(1999, 3, 1, 12, 33, 33));
+
+        auto dt4 = DateTime(1999, 1, 29, 12, 33, 33);
+        dt4.roll!"months"(1, AllowDayOverflow.no);
+        assert(dt4 == DateTime(1999, 2, 28, 12, 33, 33));
+
+        auto dt5 = DateTime(2000, 2, 29, 12, 30, 33);
+        dt5.roll!"years"(1);
+        assert(dt5 == DateTime(2001, 3, 1, 12, 30, 33));
+
+        auto dt6 = DateTime(2000, 2, 29, 12, 30, 33);
+        dt6.roll!"years"(1, AllowDayOverflow.no);
+        assert(dt6 == DateTime(2001, 2, 28, 12, 30, 33));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        auto dt1 = DateTime(2010, 1, 1, 11, 23, 12);
+        dt1.roll!"days"(1);
+        assert(dt1 == DateTime(2010, 1, 2, 11, 23, 12));
+        dt1.roll!"days"(365);
+        assert(dt1 == DateTime(2010, 1, 26, 11, 23, 12));
+        dt1.roll!"days"(-32);
+        assert(dt1 == DateTime(2010, 1, 25, 11, 23, 12));
+
+        auto dt2 = DateTime(2010, 7, 4, 12, 0, 0);
+        dt2.roll!"hours"(1);
+        assert(dt2 == DateTime(2010, 7, 4, 13, 0, 0));
+
+        auto dt3 = DateTime(2010, 1, 1, 0, 0, 0);
+        dt3.roll!"seconds"(-1);
+        assert(dt3 == DateTime(2010, 1, 1, 0, 0, 59));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        import core.time : hours, seconds;
+
+        assert(DateTime(2015, 12, 31, 23, 59, 59) + seconds(1) ==
+               DateTime(2016, 1, 1, 0, 0, 0));
+
+        assert(DateTime(2015, 12, 31, 23, 59, 59) + hours(1) ==
+               DateTime(2016, 1, 1, 0, 59, 59));
+
+        assert(DateTime(2016, 1, 1, 0, 0, 0) - seconds(1) ==
+               DateTime(2015, 12, 31, 23, 59, 59));
+
+        assert(DateTime(2016, 1, 1, 0, 59, 59) - hours(1) ==
+               DateTime(2015, 12, 31, 23, 59, 59));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(DateTime(1999, 2, 1, 12, 2, 3).diffMonths(
+                   DateTime(1999, 1, 31, 23, 59, 59)) == 1);
+
+        assert(DateTime(1999, 1, 31, 0, 0, 0).diffMonths(
+                   DateTime(1999, 2, 1, 12, 3, 42)) == -1);
+
+        assert(DateTime(1999, 3, 1, 5, 30, 0).diffMonths(
+                   DateTime(1999, 1, 1, 2, 4, 7)) == 2);
+
+        assert(DateTime(1999, 1, 1, 7, 2, 4).diffMonths(
+                   DateTime(1999, 3, 31, 0, 30, 58)) == -2);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(DateTime(Date(1999, 1, 1), TimeOfDay(12, 22, 7)).dayOfYear == 1);
+        assert(DateTime(Date(1999, 12, 31), TimeOfDay(7, 2, 59)).dayOfYear == 365);
+        assert(DateTime(Date(2000, 12, 31), TimeOfDay(21, 20, 0)).dayOfYear == 366);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(DateTime(Date(1, 1, 1), TimeOfDay(0, 0, 0)).dayOfGregorianCal == 1);
+        assert(DateTime(Date(1, 12, 31), TimeOfDay(23, 59, 59)).dayOfGregorianCal == 365);
+        assert(DateTime(Date(2, 1, 1), TimeOfDay(2, 2, 2)).dayOfGregorianCal == 366);
+
+        assert(DateTime(Date(0, 12, 31), TimeOfDay(7, 7, 7)).dayOfGregorianCal == 0);
+        assert(DateTime(Date(0, 1, 1), TimeOfDay(19, 30, 0)).dayOfGregorianCal == -365);
+        assert(DateTime(Date(-1, 12, 31), TimeOfDay(4, 7, 0)).dayOfGregorianCal == -366);
+
+        assert(DateTime(Date(2000, 1, 1), TimeOfDay(9, 30, 20)).dayOfGregorianCal == 730_120);
+        assert(DateTime(Date(2010, 12, 31), TimeOfDay(15, 45, 50)).dayOfGregorianCal == 734_137);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        auto dt = DateTime(Date.init, TimeOfDay(12, 0, 0));
+        dt.dayOfGregorianCal = 1;
+        assert(dt == DateTime(Date(1, 1, 1), TimeOfDay(12, 0, 0)));
+
+        dt.dayOfGregorianCal = 365;
+        assert(dt == DateTime(Date(1, 12, 31), TimeOfDay(12, 0, 0)));
+
+        dt.dayOfGregorianCal = 366;
+        assert(dt == DateTime(Date(2, 1, 1), TimeOfDay(12, 0, 0)));
+
+        dt.dayOfGregorianCal = 0;
+        assert(dt == DateTime(Date(0, 12, 31), TimeOfDay(12, 0, 0)));
+
+        dt.dayOfGregorianCal = -365;
+        assert(dt == DateTime(Date(-0, 1, 1), TimeOfDay(12, 0, 0)));
+
+        dt.dayOfGregorianCal = -366;
+        assert(dt == DateTime(Date(-1, 12, 31), TimeOfDay(12, 0, 0)));
+
+        dt.dayOfGregorianCal = 730_120;
+        assert(dt == DateTime(Date(2000, 1, 1), TimeOfDay(12, 0, 0)));
+
+        dt.dayOfGregorianCal = 734_137;
+        assert(dt == DateTime(Date(2010, 12, 31), TimeOfDay(12, 0, 0)));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).endOfMonth ==
+               DateTime(Date(1999, 1, 31), TimeOfDay(23, 59, 59)));
+
+        assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).endOfMonth ==
+               DateTime(Date(1999, 2, 28), TimeOfDay(23, 59, 59)));
+
+        assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).endOfMonth ==
+               DateTime(Date(2000, 2, 29), TimeOfDay(23, 59, 59)));
+
+        assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).endOfMonth ==
+               DateTime(Date(2000, 6, 30), TimeOfDay(23, 59, 59)));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(DateTime(Date(1999, 1, 6), TimeOfDay(0, 0, 0)).daysInMonth == 31);
+        assert(DateTime(Date(1999, 2, 7), TimeOfDay(19, 30, 0)).daysInMonth == 28);
+        assert(DateTime(Date(2000, 2, 7), TimeOfDay(5, 12, 27)).daysInMonth == 29);
+        assert(DateTime(Date(2000, 6, 4), TimeOfDay(12, 22, 9)).daysInMonth == 30);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(DateTime(Date(1, 1, 1), TimeOfDay(12, 7, 0)).isAD);
+        assert(DateTime(Date(2010, 12, 31), TimeOfDay(0, 0, 0)).isAD);
+        assert(!DateTime(Date(0, 12, 31), TimeOfDay(23, 59, 59)).isAD);
+        assert(!DateTime(Date(-2010, 1, 1), TimeOfDay(2, 2, 2)).isAD);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOString() ==
+               "20100704T070612");
+
+        assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOString() ==
+               "19981225T021500");
+
+        assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOString() ==
+               "00000105T230959");
+
+        assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOString() ==
+               "-00040105T000002");
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toISOExtString() ==
+               "2010-07-04T07:06:12");
+
+        assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toISOExtString() ==
+               "1998-12-25T02:15:00");
+
+        assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toISOExtString() ==
+               "0000-01-05T23:09:59");
+
+        assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toISOExtString() ==
+               "-0004-01-05T00:00:02");
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)).toSimpleString() ==
+               "2010-Jul-04 07:06:12");
+
+        assert(DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)).toSimpleString() ==
+               "1998-Dec-25 02:15:00");
+
+        assert(DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)).toSimpleString() ==
+               "0000-Jan-05 23:09:59");
+
+        assert(DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)).toSimpleString() ==
+               "-0004-Jan-05 00:00:02");
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(DateTime.fromISOString("20100704T070612") ==
+               DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
+
+        assert(DateTime.fromISOString("19981225T021500") ==
+               DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
+
+        assert(DateTime.fromISOString("00000105T230959") ==
+               DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
+
+        assert(DateTime.fromISOString("-00040105T000002") ==
+               DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
+
+        assert(DateTime.fromISOString(" 20100704T070612 ") ==
+               DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(DateTime.fromISOExtString("2010-07-04T07:06:12") ==
+               DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
+
+        assert(DateTime.fromISOExtString("1998-12-25T02:15:00") ==
+               DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
+
+        assert(DateTime.fromISOExtString("0000-01-05T23:09:59") ==
+               DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
+
+        assert(DateTime.fromISOExtString("-0004-01-05T00:00:02") ==
+               DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
+
+        assert(DateTime.fromISOExtString(" 2010-07-04T07:06:12 ") ==
+               DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(DateTime.fromSimpleString("2010-Jul-04 07:06:12") ==
+               DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
+        assert(DateTime.fromSimpleString("1998-Dec-25 02:15:00") ==
+               DateTime(Date(1998, 12, 25), TimeOfDay(2, 15, 0)));
+        assert(DateTime.fromSimpleString("0000-Jan-05 23:09:59") ==
+               DateTime(Date(0, 1, 5), TimeOfDay(23, 9, 59)));
+        assert(DateTime.fromSimpleString("-0004-Jan-05 00:00:02") ==
+               DateTime(Date(-4, 1, 5), TimeOfDay(0, 0, 2)));
+        assert(DateTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") ==
+               DateTime(Date(2010, 7, 4), TimeOfDay(7, 6, 12)));
+    
+}
+
+@safe pure unittest
+{
+    import std.datetime.date;
+
+    import core.time : days, seconds;
+
+    auto dt = DateTime(2000, 6, 1, 10, 30, 0);
+
+    assert(dt.date == Date(2000, 6, 1));
+    assert(dt.timeOfDay == TimeOfDay(10, 30, 0));
+    assert(dt.dayOfYear == 153);
+    assert(dt.dayOfWeek == DayOfWeek.thu);
+
+    dt += 10.days + 100.seconds;
+    assert(dt == DateTime(2000, 6, 11, 10, 31, 40));
+
+    assert(dt.toISOExtString() == "2000-06-11T10:31:40");
+    assert(dt.toISOString() == "20000611T103140");
+    assert(dt.toSimpleString() == "2000-Jun-11 10:31:40");
+
+    assert(DateTime.fromISOExtString("2018-01-01T12:00:00") == DateTime(2018, 1, 1, 12, 0, 0));
+    assert(DateTime.fromISOString("20180101T120000") == DateTime(2018, 1, 1, 12, 0, 0));
+    assert(DateTime.fromSimpleString("2018-Jan-01 12:00:00") == DateTime(2018, 1, 1, 12, 0, 0));
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(Date(1999, 7, 6).year == 1999);
+        assert(Date(2010, 10, 4).year == 2010);
+        assert(Date(-7, 4, 5).year == -7);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(Date(1999, 7, 6).year == 1999);
+        assert(Date(2010, 10, 4).year == 2010);
+        assert(Date(-7, 4, 5).year == -7);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(Date(0, 1, 1).yearBC == 1);
+        assert(Date(-1, 1, 1).yearBC == 2);
+        assert(Date(-100, 1, 1).yearBC == 101);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        auto date = Date(2010, 1, 1);
+        date.yearBC = 1;
+        assert(date == Date(0, 1, 1));
+
+        date.yearBC = 10;
+        assert(date == Date(-9, 1, 1));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(Date(1999, 7, 6).month == 7);
+        assert(Date(2010, 10, 4).month == 10);
+        assert(Date(-7, 4, 5).month == 4);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(Date(1999, 7, 6).day == 6);
+        assert(Date(2010, 10, 4).day == 4);
+        assert(Date(-7, 4, 5).day == 5);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        auto d1 = Date(2010, 1, 1);
+        d1.add!"months"(11);
+        assert(d1 == Date(2010, 12, 1));
+
+        auto d2 = Date(2010, 1, 1);
+        d2.add!"months"(-11);
+        assert(d2 == Date(2009, 2, 1));
+
+        auto d3 = Date(2000, 2, 29);
+        d3.add!"years"(1);
+        assert(d3 == Date(2001, 3, 1));
+
+        auto d4 = Date(2000, 2, 29);
+        d4.add!"years"(1, AllowDayOverflow.no);
+        assert(d4 == Date(2001, 2, 28));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        auto d1 = Date(2010, 1, 1);
+        d1.roll!"months"(1);
+        assert(d1 == Date(2010, 2, 1));
+
+        auto d2 = Date(2010, 1, 1);
+        d2.roll!"months"(-1);
+        assert(d2 == Date(2010, 12, 1));
+
+        auto d3 = Date(1999, 1, 29);
+        d3.roll!"months"(1);
+        assert(d3 == Date(1999, 3, 1));
+
+        auto d4 = Date(1999, 1, 29);
+        d4.roll!"months"(1, AllowDayOverflow.no);
+        assert(d4 == Date(1999, 2, 28));
+
+        auto d5 = Date(2000, 2, 29);
+        d5.roll!"years"(1);
+        assert(d5 == Date(2001, 3, 1));
+
+        auto d6 = Date(2000, 2, 29);
+        d6.roll!"years"(1, AllowDayOverflow.no);
+        assert(d6 == Date(2001, 2, 28));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        auto d = Date(2010, 1, 1);
+        d.roll!"days"(1);
+        assert(d == Date(2010, 1, 2));
+        d.roll!"days"(365);
+        assert(d == Date(2010, 1, 26));
+        d.roll!"days"(-32);
+        assert(d == Date(2010, 1, 25));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        import core.time : days;
+
+        assert(Date(2015, 12, 31) + days(1) == Date(2016, 1, 1));
+        assert(Date(2004, 2, 26) + days(4) == Date(2004, 3, 1));
+
+        assert(Date(2016, 1, 1) - days(1) == Date(2015, 12, 31));
+        assert(Date(2004, 3, 1) - days(4) == Date(2004, 2, 26));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(Date(1999, 2, 1).diffMonths(Date(1999, 1, 31)) == 1);
+        assert(Date(1999, 1, 31).diffMonths(Date(1999, 2, 1)) == -1);
+        assert(Date(1999, 3, 1).diffMonths(Date(1999, 1, 1)) == 2);
+        assert(Date(1999, 1, 1).diffMonths(Date(1999, 3, 31)) == -2);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(Date(1999, 1, 1).dayOfYear == 1);
+        assert(Date(1999, 12, 31).dayOfYear == 365);
+        assert(Date(2000, 12, 31).dayOfYear == 366);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(Date(1, 1, 1).dayOfGregorianCal == 1);
+        assert(Date(1, 12, 31).dayOfGregorianCal == 365);
+        assert(Date(2, 1, 1).dayOfGregorianCal == 366);
+
+        assert(Date(0, 12, 31).dayOfGregorianCal == 0);
+        assert(Date(0, 1, 1).dayOfGregorianCal == -365);
+        assert(Date(-1, 12, 31).dayOfGregorianCal == -366);
+
+        assert(Date(2000, 1, 1).dayOfGregorianCal == 730_120);
+        assert(Date(2010, 12, 31).dayOfGregorianCal == 734_137);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        auto date = Date.init;
+        date.dayOfGregorianCal = 1;
+        assert(date == Date(1, 1, 1));
+
+        date.dayOfGregorianCal = 365;
+        assert(date == Date(1, 12, 31));
+
+        date.dayOfGregorianCal = 366;
+        assert(date == Date(2, 1, 1));
+
+        date.dayOfGregorianCal = 0;
+        assert(date == Date(0, 12, 31));
+
+        date.dayOfGregorianCal = -365;
+        assert(date == Date(-0, 1, 1));
+
+        date.dayOfGregorianCal = -366;
+        assert(date == Date(-1, 12, 31));
+
+        date.dayOfGregorianCal = 730_120;
+        assert(date == Date(2000, 1, 1));
+
+        date.dayOfGregorianCal = 734_137;
+        assert(date == Date(2010, 12, 31));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(Date(1999, 1, 6).endOfMonth == Date(1999, 1, 31));
+        assert(Date(1999, 2, 7).endOfMonth == Date(1999, 2, 28));
+        assert(Date(2000, 2, 7).endOfMonth == Date(2000, 2, 29));
+        assert(Date(2000, 6, 4).endOfMonth == Date(2000, 6, 30));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(Date(1999, 1, 6).daysInMonth == 31);
+        assert(Date(1999, 2, 7).daysInMonth == 28);
+        assert(Date(2000, 2, 7).daysInMonth == 29);
+        assert(Date(2000, 6, 4).daysInMonth == 30);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(Date(1, 1, 1).isAD);
+        assert(Date(2010, 12, 31).isAD);
+        assert(!Date(0, 12, 31).isAD);
+        assert(!Date(-2010, 1, 1).isAD);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(Date(2010, 7, 4).toISOString() == "20100704");
+        assert(Date(1998, 12, 25).toISOString() == "19981225");
+        assert(Date(0, 1, 5).toISOString() == "00000105");
+        assert(Date(-4, 1, 5).toISOString() == "-00040105");
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(Date(2010, 7, 4).toISOExtString() == "2010-07-04");
+        assert(Date(1998, 12, 25).toISOExtString() == "1998-12-25");
+        assert(Date(0, 1, 5).toISOExtString() == "0000-01-05");
+        assert(Date(-4, 1, 5).toISOExtString() == "-0004-01-05");
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(Date(2010, 7, 4).toSimpleString() == "2010-Jul-04");
+        assert(Date(1998, 12, 25).toSimpleString() == "1998-Dec-25");
+        assert(Date(0, 1, 5).toSimpleString() == "0000-Jan-05");
+        assert(Date(-4, 1, 5).toSimpleString() == "-0004-Jan-05");
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(Date.fromISOString("20100704") == Date(2010, 7, 4));
+        assert(Date.fromISOString("19981225") == Date(1998, 12, 25));
+        assert(Date.fromISOString("00000105") == Date(0, 1, 5));
+        assert(Date.fromISOString("-00040105") == Date(-4, 1, 5));
+        assert(Date.fromISOString(" 20100704 ") == Date(2010, 7, 4));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(Date.fromISOExtString("2010-07-04") == Date(2010, 7, 4));
+        assert(Date.fromISOExtString("1998-12-25") == Date(1998, 12, 25));
+        assert(Date.fromISOExtString("0000-01-05") == Date(0, 1, 5));
+        assert(Date.fromISOExtString("-0004-01-05") == Date(-4, 1, 5));
+        assert(Date.fromISOExtString(" 2010-07-04 ") == Date(2010, 7, 4));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(Date.fromSimpleString("2010-Jul-04") == Date(2010, 7, 4));
+        assert(Date.fromSimpleString("1998-Dec-25") == Date(1998, 12, 25));
+        assert(Date.fromSimpleString("0000-Jan-05") == Date(0, 1, 5));
+        assert(Date.fromSimpleString("-0004-Jan-05") == Date(-4, 1, 5));
+        assert(Date.fromSimpleString(" 2010-Jul-04 ") == Date(2010, 7, 4));
+    
+}
+
+@safe pure unittest
+{
+    import std.datetime.date;
+
+    import core.time : days;
+
+    auto d = Date(2000, 6, 1);
+
+    assert(d.dayOfYear == 153);
+    assert(d.dayOfWeek == DayOfWeek.thu);
+
+    d += 10.days;
+    assert(d == Date(2000, 6, 11));
+
+    assert(d.toISOExtString() == "2000-06-11");
+    assert(d.toISOString() == "20000611");
+    assert(d.toSimpleString() == "2000-Jun-11");
+
+    assert(Date.fromISOExtString("2018-01-01") == Date(2018, 1, 1));
+    assert(Date.fromISOString("20180101") == Date(2018, 1, 1));
+    assert(Date.fromSimpleString("2018-Jan-01") == Date(2018, 1, 1));
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        auto tod1 = TimeOfDay(7, 12, 0);
+        tod1.roll!"hours"(1);
+        assert(tod1 == TimeOfDay(8, 12, 0));
+
+        auto tod2 = TimeOfDay(7, 12, 0);
+        tod2.roll!"hours"(-1);
+        assert(tod2 == TimeOfDay(6, 12, 0));
+
+        auto tod3 = TimeOfDay(23, 59, 0);
+        tod3.roll!"minutes"(1);
+        assert(tod3 == TimeOfDay(23, 0, 0));
+
+        auto tod4 = TimeOfDay(0, 0, 0);
+        tod4.roll!"minutes"(-1);
+        assert(tod4 == TimeOfDay(0, 59, 0));
+
+        auto tod5 = TimeOfDay(23, 59, 59);
+        tod5.roll!"seconds"(1);
+        assert(tod5 == TimeOfDay(23, 59, 0));
+
+        auto tod6 = TimeOfDay(0, 0, 0);
+        tod6.roll!"seconds"(-1);
+        assert(tod6 == TimeOfDay(0, 0, 59));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        import core.time : hours, minutes, seconds;
+
+        assert(TimeOfDay(12, 12, 12) + seconds(1) == TimeOfDay(12, 12, 13));
+        assert(TimeOfDay(12, 12, 12) + minutes(1) == TimeOfDay(12, 13, 12));
+        assert(TimeOfDay(12, 12, 12) + hours(1) == TimeOfDay(13, 12, 12));
+        assert(TimeOfDay(23, 59, 59) + seconds(1) == TimeOfDay(0, 0, 0));
+
+        assert(TimeOfDay(12, 12, 12) - seconds(1) == TimeOfDay(12, 12, 11));
+        assert(TimeOfDay(12, 12, 12) - minutes(1) == TimeOfDay(12, 11, 12));
+        assert(TimeOfDay(12, 12, 12) - hours(1) == TimeOfDay(11, 12, 12));
+        assert(TimeOfDay(0, 0, 0) - seconds(1) == TimeOfDay(23, 59, 59));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(TimeOfDay(0, 0, 0).toISOString() == "000000");
+        assert(TimeOfDay(12, 30, 33).toISOString() == "123033");
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(TimeOfDay(0, 0, 0).toISOExtString() == "00:00:00");
+        assert(TimeOfDay(12, 30, 33).toISOExtString() == "12:30:33");
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(TimeOfDay.fromISOString("000000") == TimeOfDay(0, 0, 0));
+        assert(TimeOfDay.fromISOString("123033") == TimeOfDay(12, 30, 33));
+        assert(TimeOfDay.fromISOString(" 123033 ") == TimeOfDay(12, 30, 33));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+        assert(TimeOfDay.fromISOExtString("00:00:00") == TimeOfDay(0, 0, 0));
+        assert(TimeOfDay.fromISOExtString("12:30:33") == TimeOfDay(12, 30, 33));
+        assert(TimeOfDay.fromISOExtString(" 12:30:33 ") == TimeOfDay(12, 30, 33));
+    
+}
+
+@safe pure unittest
+{
+    import std.datetime.date;
+
+    import core.time : minutes, seconds;
+
+    auto t = TimeOfDay(12, 30, 0);
+
+    t += 10.minutes + 100.seconds;
+    assert(t == TimeOfDay(12, 41, 40));
+
+    assert(t.toISOExtString() == "12:41:40");
+    assert(t.toISOString() == "124140");
+
+    assert(TimeOfDay.fromISOExtString("15:00:00") == TimeOfDay(15, 0, 0));
+    assert(TimeOfDay.fromISOString("015000") == TimeOfDay(1, 50, 0));
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+    assert(valid!"hours"(12));
+    assert(!valid!"hours"(32));
+    assert(valid!"months"(12));
+    assert(!valid!"months"(13));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.datetime.date;
+
+    assert(valid!"days"(2016, 2, 29));
+    assert(!valid!"days"(2016, 2, 30));
+    assert(valid!"days"(2017, 2, 20));
+    assert(!valid!"days"(2017, 2, 29));
+}
+
+@safe pure unittest
+{
+    import std.datetime.date;
+
+    import std.exception : assertThrown, assertNotThrown;
+
+    assertNotThrown(enforceValid!"months"(10));
+    assertNotThrown(enforceValid!"seconds"(40));
+
+    assertThrown!DateTimeException(enforceValid!"months"(0));
+    assertThrown!DateTimeException(enforceValid!"hours"(24));
+    assertThrown!DateTimeException(enforceValid!"minutes"(60));
+    assertThrown!DateTimeException(enforceValid!"seconds"(60));
+}
+
+@safe pure unittest
+{
+    import std.datetime.date;
+
+    import std.exception : assertThrown, assertNotThrown;
+
+    assertNotThrown(enforceValid!"days"(2000, Month.jan, 1));
+    // leap year
+    assertNotThrown(enforceValid!"days"(2000, Month.feb, 29));
+
+    assertThrown!DateTimeException(enforceValid!"days"(2001, Month.feb, 29));
+    assertThrown!DateTimeException(enforceValid!"days"(2000, Month.jan, 32));
+    assertThrown!DateTimeException(enforceValid!"days"(2000, Month.apr, 31));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.datetime.date;
+
+    assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.mon) == 0);
+    assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.sun) == 6);
+    assert(daysToDayOfWeek(DayOfWeek.mon, DayOfWeek.wed) == 2);
+}
+
+@safe pure unittest
+{
+    import std.datetime.date;
+
+    assert(monthsToMonth(Month.jan, Month.jan) == 0);
+    assert(monthsToMonth(Month.jan, Month.dec) == 11);
+    assert(monthsToMonth(Month.jul, Month.oct) == 3);
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+    foreach (year; [1, 2, 100, 2001, 2002, 2003, 2005, 2006, 2007, 2009, 2010])
+    {
+        assert(!yearIsLeapYear(year));
+        assert(!yearIsLeapYear(-year));
+    }
+
+    foreach (year; [0, 4, 8, 400, 800, 1600, 1996, 2000, 2004, 2008, 2012])
+    {
+        assert(yearIsLeapYear(year));
+        assert(yearIsLeapYear(-year));
+    }
+}
+
+@safe unittest
+{
+    import std.datetime.date;
+
+    import core.time : Duration;
+    import std.datetime.interval : Interval;
+    import std.datetime.systime : SysTime;
+
+    static assert(isTimePoint!Date);
+    static assert(isTimePoint!DateTime);
+    static assert(isTimePoint!SysTime);
+    static assert(isTimePoint!TimeOfDay);
+
+    static assert(!isTimePoint!int);
+    static assert(!isTimePoint!Duration);
+    static assert(!isTimePoint!(Interval!SysTime));
+}
+
+@safe @nogc nothrow unittest
+{
+    import std.datetime.date;
+
+    assert(validTimeUnits("msecs", "seconds", "minutes"));
+    assert(validTimeUnits("days", "weeks", "months"));
+    assert(!validTimeUnits("ms", "seconds", "minutes"));
+}
+
+@safe pure unittest
+{
+    import std.datetime.date;
+
+    import std.exception : assertThrown;
+
+    assert(cmpTimeUnits("hours", "hours") == 0);
+    assert(cmpTimeUnits("hours", "weeks") < 0);
+    assert(cmpTimeUnits("months", "seconds") > 0);
+
+    assertThrown!DateTimeException(cmpTimeUnits("month", "second"));
+}
+
+@safe pure unittest
+{
+    import std.datetime.date;
+
+    static assert(CmpTimeUnits!("years", "weeks") > 0);
+    static assert(CmpTimeUnits!("days", "days") == 0);
+    static assert(CmpTimeUnits!("seconds", "hours") < 0);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_datetime_interval.d b/libphobos/testsuite/libphobos.phobos/std_datetime_interval.d
new file mode 100644
index 0000000000000000000000000000000000000000..e741f3aeed16f3d580e2e558593ed199ef3b7cb6
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_datetime_interval.d
@@ -0,0 +1,112 @@
+@system unittest
+{
+    import std.datetime.interval;
+
+    import std.datetime.date : Date, DayOfWeek;
+
+    auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27));
+    auto func = everyDayOfWeek!Date(DayOfWeek.mon);
+    auto range = interval.fwdRange(func);
+
+    // A Thursday. Using PopFirst.yes would have made this Date(2010, 9, 6).
+    assert(range.front == Date(2010, 9, 2));
+
+    range.popFront();
+    assert(range.front == Date(2010, 9, 6));
+
+    range.popFront();
+    assert(range.front == Date(2010, 9, 13));
+
+    range.popFront();
+    assert(range.front == Date(2010, 9, 20));
+
+    range.popFront();
+    assert(range.empty);
+}
+
+@system unittest
+{
+    import std.datetime.interval;
+
+    import std.datetime.date : Date, Month;
+
+    auto interval = Interval!Date(Date(2000, 1, 30), Date(2004, 8, 5));
+    auto func = everyMonth!Date(Month.feb);
+    auto range = interval.fwdRange(func);
+
+    // Using PopFirst.yes would have made this Date(2010, 2, 29).
+    assert(range.front == Date(2000, 1, 30));
+
+    range.popFront();
+    assert(range.front == Date(2000, 2, 29));
+
+    range.popFront();
+    assert(range.front == Date(2001, 2, 28));
+
+    range.popFront();
+    assert(range.front == Date(2002, 2, 28));
+
+    range.popFront();
+    assert(range.front == Date(2003, 2, 28));
+
+    range.popFront();
+    assert(range.front == Date(2004, 2, 28));
+
+    range.popFront();
+    assert(range.empty);
+}
+
+@system unittest
+{
+    import std.datetime.interval;
+
+    import core.time : dur;
+    import std.datetime.date : Date;
+
+    auto interval = Interval!Date(Date(2010, 9, 2), Date(2010, 9, 27));
+    auto func = everyDuration!Date(dur!"days"(8));
+    auto range = interval.fwdRange(func);
+
+    // Using PopFirst.yes would have made this Date(2010, 9, 10).
+    assert(range.front == Date(2010, 9, 2));
+
+    range.popFront();
+    assert(range.front == Date(2010, 9, 10));
+
+    range.popFront();
+    assert(range.front == Date(2010, 9, 18));
+
+    range.popFront();
+    assert(range.front == Date(2010, 9, 26));
+
+    range.popFront();
+    assert(range.empty);
+}
+
+@system unittest
+{
+    import std.datetime.interval;
+
+    import core.time : dur;
+    import std.datetime.date : AllowDayOverflow, Date;
+
+    auto interval = Interval!Date(Date(2010, 9, 2), Date(2025, 9, 27));
+    auto func = everyDuration!Date(4, 1, AllowDayOverflow.yes, dur!"days"(2));
+    auto range = interval.fwdRange(func);
+
+    // Using PopFirst.yes would have made this Date(2014, 10, 12).
+    assert(range.front == Date(2010, 9, 2));
+
+    range.popFront();
+    assert(range.front == Date(2014, 10, 4));
+
+    range.popFront();
+    assert(range.front == Date(2018, 11, 6));
+
+    range.popFront();
+    assert(range.front == Date(2022, 12, 8));
+
+    range.popFront();
+    assert(range.empty);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_datetime_package.d b/libphobos/testsuite/libphobos.phobos/std_datetime_package.d
new file mode 100644
index 0000000000000000000000000000000000000000..d40ad44f080e43bbe7ac5ccad3a68e55e4cf144b
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_datetime_package.d
@@ -0,0 +1,34 @@
+@safe unittest
+{
+    import std.datetime;
+
+    import std.datetime.systime : SysTime, Clock;
+
+    SysTime currentTime = Clock.currTime();
+}
+
+@safe unittest
+{
+    import std.datetime;
+
+    import std.datetime.date : DateTime;
+
+    auto dt = DateTime(2018, 1, 1, 12, 30, 10);
+    assert(dt.toISOString() == "20180101T123010");
+    assert(dt.toISOExtString() == "2018-01-01T12:30:10");
+}
+
+@safe unittest
+{
+    import std.datetime;
+
+    import std.datetime.systime : SysTime;
+    import std.datetime.timezone : UTC;
+    import core.time : days;
+
+    auto st = SysTime(DateTime(2018, 1, 1, 12, 30, 10), UTC());
+    assert(st.toISOExtString() == "2018-01-01T12:30:10Z");
+    st += 2.days;
+    assert(st.toISOExtString() == "2018-01-03T12:30:10Z");
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_datetime_stopwatch.d b/libphobos/testsuite/libphobos.phobos/std_datetime_stopwatch.d
new file mode 100644
index 0000000000000000000000000000000000000000..df58cb1c9413ef94f8697a41d1172225f0425758
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_datetime_stopwatch.d
@@ -0,0 +1,210 @@
+@system nothrow @nogc unittest
+{
+    import std.datetime.stopwatch;
+
+        import core.thread : Thread;
+
+        {
+            auto sw = StopWatch(AutoStart.yes);
+            assert(sw.running);
+            Thread.sleep(usecs(1));
+            assert(sw.peek() > Duration.zero);
+        }
+        {
+            auto sw = StopWatch(AutoStart.no);
+            assert(!sw.running);
+            Thread.sleep(usecs(1));
+            assert(sw.peek() == Duration.zero);
+        }
+        {
+            StopWatch sw;
+            assert(!sw.running);
+            Thread.sleep(usecs(1));
+            assert(sw.peek() == Duration.zero);
+        }
+
+        assert(StopWatch.init == StopWatch(AutoStart.no));
+        assert(StopWatch.init != StopWatch(AutoStart.yes));
+    
+}
+
+@system nothrow @nogc unittest
+{
+    import std.datetime.stopwatch;
+
+        import core.thread : Thread;
+
+        auto sw = StopWatch(AutoStart.yes);
+        Thread.sleep(usecs(1));
+        sw.stop();
+        assert(sw.peek() > Duration.zero);
+        sw.reset();
+        assert(sw.peek() == Duration.zero);
+    
+}
+
+@system nothrow @nogc unittest
+{
+    import std.datetime.stopwatch;
+
+        import core.thread : Thread;
+
+        StopWatch sw;
+        assert(!sw.running);
+        assert(sw.peek() == Duration.zero);
+        sw.start();
+        assert(sw.running);
+        Thread.sleep(usecs(1));
+        assert(sw.peek() > Duration.zero);
+    
+}
+
+@system nothrow @nogc unittest
+{
+    import std.datetime.stopwatch;
+
+        import core.thread : Thread;
+
+        auto sw = StopWatch(AutoStart.yes);
+        assert(sw.running);
+        Thread.sleep(usecs(1));
+        immutable t1 = sw.peek();
+        assert(t1 > Duration.zero);
+
+        sw.stop();
+        assert(!sw.running);
+        immutable t2 = sw.peek();
+        assert(t2 >= t1);
+        immutable t3 = sw.peek();
+        assert(t2 == t3);
+    
+}
+
+@system nothrow @nogc unittest
+{
+    import std.datetime.stopwatch;
+
+        import core.thread : Thread;
+
+        auto sw = StopWatch(AutoStart.no);
+        assert(sw.peek() == Duration.zero);
+        sw.start();
+
+        Thread.sleep(usecs(1));
+        assert(sw.peek() >= usecs(1));
+
+        Thread.sleep(usecs(1));
+        assert(sw.peek() >= usecs(2));
+
+        sw.stop();
+        immutable stopped = sw.peek();
+        Thread.sleep(usecs(1));
+        assert(sw.peek() == stopped);
+
+        sw.start();
+        Thread.sleep(usecs(1));
+        assert(sw.peek() > stopped);
+    
+}
+
+@system nothrow @nogc unittest
+{
+    import std.datetime.stopwatch;
+
+        import core.thread : Thread;
+
+        StopWatch sw;
+        sw.setTimeElapsed(hours(1));
+
+        // As discussed in MonoTime's documentation, converting between
+        // Duration and ticks is not exact, though it will be close.
+        // How exact it is depends on the frequency/resolution of the
+        // system's monotonic clock.
+        assert(abs(sw.peek() - hours(1)) < usecs(1));
+
+        sw.start();
+        Thread.sleep(usecs(1));
+        assert(sw.peek() > hours(1) + usecs(1));
+    
+}
+
+@safe nothrow @nogc unittest
+{
+    import std.datetime.stopwatch;
+
+        StopWatch sw;
+        assert(!sw.running);
+        sw.start();
+        assert(sw.running);
+        sw.stop();
+        assert(!sw.running);
+    
+}
+
+@safe nothrow @nogc unittest
+{
+    import std.datetime.stopwatch;
+
+    auto sw = StopWatch(AutoStart.no);
+    sw.start();
+    // ... Insert operations to be timed here ...
+    sw.stop();
+
+    long msecs = sw.peek.total!"msecs";
+    long usecs = sw.peek.total!"usecs";
+    long nsecs = sw.peek.total!"nsecs";
+
+    assert(usecs >= msecs * 1000);
+    assert(nsecs >= usecs * 1000);
+}
+
+@system nothrow @nogc unittest
+{
+    import std.datetime.stopwatch;
+
+    import core.thread : Thread;
+
+    auto sw = StopWatch(AutoStart.yes);
+
+    Duration t1 = sw.peek();
+    Thread.sleep(usecs(1));
+    Duration t2 = sw.peek();
+    assert(t2 > t1);
+
+    Thread.sleep(usecs(1));
+    sw.stop();
+
+    Duration t3 = sw.peek();
+    assert(t3 > t2);
+    Duration t4 = sw.peek();
+    assert(t3 == t4);
+
+    sw.start();
+    Thread.sleep(usecs(1));
+
+    Duration t5 = sw.peek();
+    assert(t5 > t4);
+
+    // If stopping or resetting the StopWatch is not required, then
+    // MonoTime can easily be used by itself without StopWatch.
+    auto before = MonoTime.currTime;
+    // do stuff...
+    auto timeElapsed = MonoTime.currTime - before;
+}
+
+@safe unittest
+{
+    import std.datetime.stopwatch;
+
+    import std.conv : to;
+
+    int a;
+    void f0() {}
+    void f1() { auto b = a; }
+    void f2() { auto b = to!string(a); }
+    auto r = benchmark!(f0, f1, f2)(10_000);
+    Duration f0Result = r[0]; // time f0 took to run 10,000 times
+    Duration f1Result = r[1]; // time f1 took to run 10,000 times
+    Duration f2Result = r[2]; // time f2 took to run 10,000 times
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_datetime_systime.d b/libphobos/testsuite/libphobos.phobos/std_datetime_systime.d
new file mode 100644
index 0000000000000000000000000000000000000000..a0f410374d6b40c9acfc73db3b6ec08c6edbedee
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_datetime_systime.d
@@ -0,0 +1,750 @@
+@safe unittest
+{
+    import std.datetime.systime;
+
+    import std.datetime.timezone : LocalTime;
+    SysTime today = Clock.currTime();
+    assert(today.timezone is LocalTime());
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+    import std.datetime.date : DateTime;
+    import std.datetime.timezone : UTC;
+
+    auto st = SysTime.fromISOExtString("2018-01-01T10:30:00Z");
+    assert(st == SysTime(DateTime(2018, 1, 1, 10, 30, 0), UTC()));
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+    import core.time : hours;
+    import std.datetime.date : DateTime;
+    import std.datetime.timezone : SimpleTimeZone;
+
+    auto ny = SysTime(
+        DateTime(2018, 1, 1, 10, 30, 0),
+        new immutable SimpleTimeZone(-5.hours, "America/New_York")
+    );
+
+    // ISO standard time strings
+    assert(ny.toISOString() == "20180101T103000-05:00");
+    assert(ny.toISOExtString() == "2018-01-01T10:30:00-05:00");
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+    import std.datetime.timezone : LocalTime;
+    SysTime today = Clock.currTime();
+    assert(today.timezone is LocalTime());
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import std.datetime.date : DateTime;
+
+        assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).year == 1999);
+        assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).year == 2010);
+        assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).year == -7);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import std.datetime.date : DateTime;
+
+        assert(SysTime(DateTime(0, 1, 1, 12, 30, 33)).yearBC == 1);
+        assert(SysTime(DateTime(-1, 1, 1, 10, 7, 2)).yearBC == 2);
+        assert(SysTime(DateTime(-100, 1, 1, 4, 59, 0)).yearBC == 101);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import std.datetime.date : DateTime;
+
+        assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).month == 7);
+        assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).month == 10);
+        assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).month == 4);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import std.datetime.date : DateTime;
+
+        assert(SysTime(DateTime(1999, 7, 6, 9, 7, 5)).day == 6);
+        assert(SysTime(DateTime(2010, 10, 4, 0, 0, 30)).day == 4);
+        assert(SysTime(DateTime(-7, 4, 5, 7, 45, 2)).day == 5);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import core.time : msecs, usecs, hnsecs, nsecs;
+        import std.datetime.date : DateTime;
+
+        auto dt = DateTime(1982, 4, 1, 20, 59, 22);
+        assert(SysTime(dt, msecs(213)).fracSecs == msecs(213));
+        assert(SysTime(dt, usecs(5202)).fracSecs == usecs(5202));
+        assert(SysTime(dt, hnsecs(1234567)).fracSecs == hnsecs(1234567));
+
+        // SysTime and Duration both have a precision of hnsecs (100 ns),
+        // so nsecs are going to be truncated.
+        assert(SysTime(dt, nsecs(123456789)).fracSecs == nsecs(123456700));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import core.time : Duration, msecs, hnsecs, nsecs;
+        import std.datetime.date : DateTime;
+
+        auto st = SysTime(DateTime(1982, 4, 1, 20, 59, 22));
+        assert(st.fracSecs == Duration.zero);
+
+        st.fracSecs = msecs(213);
+        assert(st.fracSecs == msecs(213));
+
+        st.fracSecs = hnsecs(1234567);
+        assert(st.fracSecs == hnsecs(1234567));
+
+        // SysTime has a precision of hnsecs (100 ns), so nsecs are
+        // going to be truncated.
+        st.fracSecs = nsecs(123456789);
+        assert(st.fracSecs == hnsecs(1234567));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import core.time : hours;
+        import std.datetime.date : DateTime;
+        import std.datetime.timezone : SimpleTimeZone, UTC;
+
+        assert(SysTime(DateTime(1970, 1, 1), UTC()).toUnixTime() == 0);
+
+        auto pst = new immutable SimpleTimeZone(hours(-8));
+        assert(SysTime(DateTime(1970, 1, 1), pst).toUnixTime() == 28800);
+
+        auto utc = SysTime(DateTime(2007, 12, 22, 8, 14, 45), UTC());
+        assert(utc.toUnixTime() == 1_198_311_285);
+
+        auto ca = SysTime(DateTime(2007, 12, 22, 8, 14, 45), pst);
+        assert(ca.toUnixTime() == 1_198_340_085);
+
+        static void testScope(scope ref SysTime st) @safe
+        {
+            auto result = st.toUnixTime();
+        }
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import core.time : hours;
+        import std.datetime.date : DateTime;
+        import std.datetime.timezone : SimpleTimeZone, UTC;
+
+        assert(SysTime.fromUnixTime(0) ==
+               SysTime(DateTime(1970, 1, 1), UTC()));
+
+        auto pst = new immutable SimpleTimeZone(hours(-8));
+        assert(SysTime.fromUnixTime(28800) ==
+               SysTime(DateTime(1970, 1, 1), pst));
+
+        auto st1 = SysTime.fromUnixTime(1_198_311_285, UTC());
+        assert(st1 == SysTime(DateTime(2007, 12, 22, 8, 14, 45), UTC()));
+        assert(st1.timezone is UTC());
+        assert(st1 == SysTime(DateTime(2007, 12, 22, 0, 14, 45), pst));
+
+        auto st2 = SysTime.fromUnixTime(1_198_311_285, pst);
+        assert(st2 == SysTime(DateTime(2007, 12, 22, 8, 14, 45), UTC()));
+        assert(st2.timezone is pst);
+        assert(st2 == SysTime(DateTime(2007, 12, 22, 0, 14, 45), pst));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import std.datetime.date : AllowDayOverflow, DateTime;
+
+        auto st1 = SysTime(DateTime(2010, 1, 1, 12, 33, 33));
+        st1.roll!"months"(1);
+        assert(st1 == SysTime(DateTime(2010, 2, 1, 12, 33, 33)));
+
+        auto st2 = SysTime(DateTime(2010, 1, 1, 12, 33, 33));
+        st2.roll!"months"(-1);
+        assert(st2 == SysTime(DateTime(2010, 12, 1, 12, 33, 33)));
+
+        auto st3 = SysTime(DateTime(1999, 1, 29, 12, 33, 33));
+        st3.roll!"months"(1);
+        assert(st3 == SysTime(DateTime(1999, 3, 1, 12, 33, 33)));
+
+        auto st4 = SysTime(DateTime(1999, 1, 29, 12, 33, 33));
+        st4.roll!"months"(1, AllowDayOverflow.no);
+        assert(st4 == SysTime(DateTime(1999, 2, 28, 12, 33, 33)));
+
+        auto st5 = SysTime(DateTime(2000, 2, 29, 12, 30, 33));
+        st5.roll!"years"(1);
+        assert(st5 == SysTime(DateTime(2001, 3, 1, 12, 30, 33)));
+
+        auto st6 = SysTime(DateTime(2000, 2, 29, 12, 30, 33));
+        st6.roll!"years"(1, AllowDayOverflow.no);
+        assert(st6 == SysTime(DateTime(2001, 2, 28, 12, 30, 33)));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import core.time : msecs, hnsecs;
+        import std.datetime.date : DateTime;
+
+        auto st1 = SysTime(DateTime(2010, 1, 1, 11, 23, 12));
+        st1.roll!"days"(1);
+        assert(st1 == SysTime(DateTime(2010, 1, 2, 11, 23, 12)));
+        st1.roll!"days"(365);
+        assert(st1 == SysTime(DateTime(2010, 1, 26, 11, 23, 12)));
+        st1.roll!"days"(-32);
+        assert(st1 == SysTime(DateTime(2010, 1, 25, 11, 23, 12)));
+
+        auto st2 = SysTime(DateTime(2010, 7, 4, 12, 0, 0));
+        st2.roll!"hours"(1);
+        assert(st2 == SysTime(DateTime(2010, 7, 4, 13, 0, 0)));
+
+        auto st3 = SysTime(DateTime(2010, 2, 12, 12, 0, 0));
+        st3.roll!"hours"(-1);
+        assert(st3 == SysTime(DateTime(2010, 2, 12, 11, 0, 0)));
+
+        auto st4 = SysTime(DateTime(2009, 12, 31, 0, 0, 0));
+        st4.roll!"minutes"(1);
+        assert(st4 == SysTime(DateTime(2009, 12, 31, 0, 1, 0)));
+
+        auto st5 = SysTime(DateTime(2010, 1, 1, 0, 0, 0));
+        st5.roll!"minutes"(-1);
+        assert(st5 == SysTime(DateTime(2010, 1, 1, 0, 59, 0)));
+
+        auto st6 = SysTime(DateTime(2009, 12, 31, 0, 0, 0));
+        st6.roll!"seconds"(1);
+        assert(st6 == SysTime(DateTime(2009, 12, 31, 0, 0, 1)));
+
+        auto st7 = SysTime(DateTime(2010, 1, 1, 0, 0, 0));
+        st7.roll!"seconds"(-1);
+        assert(st7 == SysTime(DateTime(2010, 1, 1, 0, 0, 59)));
+
+        auto dt = DateTime(2010, 1, 1, 0, 0, 0);
+        auto st8 = SysTime(dt);
+        st8.roll!"msecs"(1);
+        assert(st8 == SysTime(dt, msecs(1)));
+
+        auto st9 = SysTime(dt);
+        st9.roll!"msecs"(-1);
+        assert(st9 == SysTime(dt, msecs(999)));
+
+        auto st10 = SysTime(dt);
+        st10.roll!"hnsecs"(1);
+        assert(st10 == SysTime(dt, hnsecs(1)));
+
+        auto st11 = SysTime(dt);
+        st11.roll!"hnsecs"(-1);
+        assert(st11 == SysTime(dt, hnsecs(9_999_999)));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import core.time : hours, seconds;
+        import std.datetime.date : DateTime;
+
+        assert(SysTime(DateTime(2015, 12, 31, 23, 59, 59)) + seconds(1) ==
+               SysTime(DateTime(2016, 1, 1, 0, 0, 0)));
+
+        assert(SysTime(DateTime(2015, 12, 31, 23, 59, 59)) + hours(1) ==
+               SysTime(DateTime(2016, 1, 1, 0, 59, 59)));
+
+        assert(SysTime(DateTime(2016, 1, 1, 0, 0, 0)) - seconds(1) ==
+               SysTime(DateTime(2015, 12, 31, 23, 59, 59)));
+
+        assert(SysTime(DateTime(2016, 1, 1, 0, 59, 59)) - hours(1) ==
+               SysTime(DateTime(2015, 12, 31, 23, 59, 59)));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import core.time;
+        import std.datetime.date : Date;
+
+        assert(SysTime(Date(1999, 2, 1)).diffMonths(
+                   SysTime(Date(1999, 1, 31))) == 1);
+
+        assert(SysTime(Date(1999, 1, 31)).diffMonths(
+                   SysTime(Date(1999, 2, 1))) == -1);
+
+        assert(SysTime(Date(1999, 3, 1)).diffMonths(
+                   SysTime(Date(1999, 1, 1))) == 2);
+
+        assert(SysTime(Date(1999, 1, 1)).diffMonths(
+                   SysTime(Date(1999, 3, 31))) == -2);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import core.time;
+        import std.datetime.date : DateTime;
+
+        assert(SysTime(DateTime(1999, 1, 1, 12, 22, 7)).dayOfYear == 1);
+        assert(SysTime(DateTime(1999, 12, 31, 7, 2, 59)).dayOfYear == 365);
+        assert(SysTime(DateTime(2000, 12, 31, 21, 20, 0)).dayOfYear == 366);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import core.time;
+        import std.datetime.date : DateTime;
+
+        assert(SysTime(DateTime(1, 1, 1, 0, 0, 0)).dayOfGregorianCal == 1);
+        assert(SysTime(DateTime(1, 12, 31, 23, 59, 59)).dayOfGregorianCal == 365);
+        assert(SysTime(DateTime(2, 1, 1, 2, 2, 2)).dayOfGregorianCal == 366);
+
+        assert(SysTime(DateTime(0, 12, 31, 7, 7, 7)).dayOfGregorianCal == 0);
+        assert(SysTime(DateTime(0, 1, 1, 19, 30, 0)).dayOfGregorianCal == -365);
+        assert(SysTime(DateTime(-1, 12, 31, 4, 7, 0)).dayOfGregorianCal == -366);
+
+        assert(SysTime(DateTime(2000, 1, 1, 9, 30, 20)).dayOfGregorianCal == 730_120);
+        assert(SysTime(DateTime(2010, 12, 31, 15, 45, 50)).dayOfGregorianCal == 734_137);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import core.time;
+        import std.datetime.date : DateTime;
+
+        auto st = SysTime(DateTime(0, 1, 1, 12, 0, 0));
+        st.dayOfGregorianCal = 1;
+        assert(st == SysTime(DateTime(1, 1, 1, 12, 0, 0)));
+
+        st.dayOfGregorianCal = 365;
+        assert(st == SysTime(DateTime(1, 12, 31, 12, 0, 0)));
+
+        st.dayOfGregorianCal = 366;
+        assert(st == SysTime(DateTime(2, 1, 1, 12, 0, 0)));
+
+        st.dayOfGregorianCal = 0;
+        assert(st == SysTime(DateTime(0, 12, 31, 12, 0, 0)));
+
+        st.dayOfGregorianCal = -365;
+        assert(st == SysTime(DateTime(-0, 1, 1, 12, 0, 0)));
+
+        st.dayOfGregorianCal = -366;
+        assert(st == SysTime(DateTime(-1, 12, 31, 12, 0, 0)));
+
+        st.dayOfGregorianCal = 730_120;
+        assert(st == SysTime(DateTime(2000, 1, 1, 12, 0, 0)));
+
+        st.dayOfGregorianCal = 734_137;
+        assert(st == SysTime(DateTime(2010, 12, 31, 12, 0, 0)));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import core.time;
+        import std.datetime.date : Date;
+
+        auto st = SysTime(Date(1999, 7, 6));
+        const cst = SysTime(Date(2010, 5, 1));
+        immutable ist = SysTime(Date(2015, 10, 10));
+
+        assert(st.isoWeek == 27);
+        assert(cst.isoWeek == 17);
+        assert(ist.isoWeek == 41);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import core.time : msecs, usecs, hnsecs;
+        import std.datetime.date : DateTime;
+
+        assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).endOfMonth ==
+               SysTime(DateTime(1999, 1, 31, 23, 59, 59), hnsecs(9_999_999)));
+
+        assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0), msecs(24)).endOfMonth ==
+               SysTime(DateTime(1999, 2, 28, 23, 59, 59), hnsecs(9_999_999)));
+
+        assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27), usecs(5203)).endOfMonth ==
+               SysTime(DateTime(2000, 2, 29, 23, 59, 59), hnsecs(9_999_999)));
+
+        assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9), hnsecs(12345)).endOfMonth ==
+               SysTime(DateTime(2000, 6, 30, 23, 59, 59), hnsecs(9_999_999)));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import core.time;
+        import std.datetime.date : DateTime;
+
+        assert(SysTime(DateTime(1999, 1, 6, 0, 0, 0)).daysInMonth == 31);
+        assert(SysTime(DateTime(1999, 2, 7, 19, 30, 0)).daysInMonth == 28);
+        assert(SysTime(DateTime(2000, 2, 7, 5, 12, 27)).daysInMonth == 29);
+        assert(SysTime(DateTime(2000, 6, 4, 12, 22, 9)).daysInMonth == 30);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import core.time;
+        import std.datetime.date : DateTime;
+
+        assert(SysTime(DateTime(1, 1, 1, 12, 7, 0)).isAD);
+        assert(SysTime(DateTime(2010, 12, 31, 0, 0, 0)).isAD);
+        assert(!SysTime(DateTime(0, 12, 31, 23, 59, 59)).isAD);
+        assert(!SysTime(DateTime(-2010, 1, 1, 2, 2, 2)).isAD);
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import core.time : msecs, hnsecs;
+        import std.datetime.date : DateTime;
+
+        assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOString() ==
+               "20100704T070612");
+
+        assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(24)).toISOString() ==
+               "19981225T021500.024");
+
+        assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toISOString() ==
+               "00000105T230959");
+
+        assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), hnsecs(520_920)).toISOString() ==
+               "-00040105T000002.052092");
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import core.time : msecs, hnsecs;
+        import std.datetime.date : DateTime;
+
+        assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toISOExtString() ==
+               "2010-07-04T07:06:12");
+
+        assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(24)).toISOExtString() ==
+               "1998-12-25T02:15:00.024");
+
+        assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toISOExtString() ==
+               "0000-01-05T23:09:59");
+
+        assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), hnsecs(520_920)).toISOExtString() ==
+               "-0004-01-05T00:00:02.052092");
+
+        assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), hnsecs(520_920)).toISOExtString(4) ==
+               "-0004-01-05T00:00:02.0520");
+
+        assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), hnsecs(520_920)).toISOExtString(2) ==
+               "-0004-01-05T00:00:02.05");
+
+        assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), hnsecs(520_920)).toISOExtString(7) ==
+               "-0004-01-05T00:00:02.0520920");
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import core.time : msecs, hnsecs;
+        import std.datetime.date : DateTime;
+
+        assert(SysTime(DateTime(2010, 7, 4, 7, 6, 12)).toSimpleString() ==
+               "2010-Jul-04 07:06:12");
+
+        assert(SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(24)).toSimpleString() ==
+               "1998-Dec-25 02:15:00.024");
+
+        assert(SysTime(DateTime(0, 1, 5, 23, 9, 59)).toSimpleString() ==
+               "0000-Jan-05 23:09:59");
+
+        assert(SysTime(DateTime(-4, 1, 5, 0, 0, 2), hnsecs(520_920)).toSimpleString() ==
+                "-0004-Jan-05 00:00:02.052092");
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import core.time : hours, msecs, usecs, hnsecs;
+        import std.datetime.date : DateTime;
+        import std.datetime.timezone : SimpleTimeZone, UTC;
+
+        assert(SysTime.fromISOString("20100704T070612") ==
+               SysTime(DateTime(2010, 7, 4, 7, 6, 12)));
+
+        assert(SysTime.fromISOString("19981225T021500.007") ==
+               SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(7)));
+
+        assert(SysTime.fromISOString("00000105T230959.00002") ==
+               SysTime(DateTime(0, 1, 5, 23, 9, 59), usecs(20)));
+
+        assert(SysTime.fromISOString("20130207T043937.000050392") ==
+               SysTime(DateTime(2013, 2, 7, 4, 39, 37), hnsecs(503)));
+
+        assert(SysTime.fromISOString("-00040105T000002") ==
+               SysTime(DateTime(-4, 1, 5, 0, 0, 2)));
+
+        assert(SysTime.fromISOString(" 20100704T070612 ") ==
+               SysTime(DateTime(2010, 7, 4, 7, 6, 12)));
+
+        assert(SysTime.fromISOString("20100704T070612Z") ==
+               SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC()));
+
+        assert(SysTime.fromISOString("20100704T070612-0800") ==
+               SysTime(DateTime(2010, 7, 4, 7, 6, 12),
+                       new immutable SimpleTimeZone(hours(-8))));
+
+        assert(SysTime.fromISOString("20100704T070612+0800") ==
+               SysTime(DateTime(2010, 7, 4, 7, 6, 12),
+                       new immutable SimpleTimeZone(hours(8))));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import core.time : hours, msecs, usecs, hnsecs;
+        import std.datetime.date : DateTime;
+        import std.datetime.timezone : SimpleTimeZone, UTC;
+
+        assert(SysTime.fromISOExtString("2010-07-04T07:06:12") ==
+               SysTime(DateTime(2010, 7, 4, 7, 6, 12)));
+
+        assert(SysTime.fromISOExtString("1998-12-25T02:15:00.007") ==
+               SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(7)));
+
+        assert(SysTime.fromISOExtString("0000-01-05T23:09:59.00002") ==
+               SysTime(DateTime(0, 1, 5, 23, 9, 59), usecs(20)));
+
+        assert(SysTime.fromISOExtString("2013-02-07T04:39:37.000050392") ==
+               SysTime(DateTime(2013, 2, 7, 4, 39, 37), hnsecs(503)));
+
+        assert(SysTime.fromISOExtString("-0004-01-05T00:00:02") ==
+               SysTime(DateTime(-4, 1, 5, 0, 0, 2)));
+
+        assert(SysTime.fromISOExtString(" 2010-07-04T07:06:12 ") ==
+               SysTime(DateTime(2010, 7, 4, 7, 6, 12)));
+
+        assert(SysTime.fromISOExtString("2010-07-04T07:06:12Z") ==
+               SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC()));
+
+        assert(SysTime.fromISOExtString("2010-07-04T07:06:12-08:00") ==
+               SysTime(DateTime(2010, 7, 4, 7, 6, 12),
+                       new immutable SimpleTimeZone(hours(-8))));
+        assert(SysTime.fromISOExtString("2010-07-04T07:06:12+08:00") ==
+               SysTime(DateTime(2010, 7, 4, 7, 6, 12),
+                       new immutable SimpleTimeZone(hours(8))));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+        import core.time : hours, msecs, usecs, hnsecs;
+        import std.datetime.date : DateTime;
+        import std.datetime.timezone : SimpleTimeZone, UTC;
+
+        assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12") ==
+               SysTime(DateTime(2010, 7, 4, 7, 6, 12)));
+
+        assert(SysTime.fromSimpleString("1998-Dec-25 02:15:00.007") ==
+               SysTime(DateTime(1998, 12, 25, 2, 15, 0), msecs(7)));
+
+        assert(SysTime.fromSimpleString("0000-Jan-05 23:09:59.00002") ==
+               SysTime(DateTime(0, 1, 5, 23, 9, 59), usecs(20)));
+
+        assert(SysTime.fromSimpleString("2013-Feb-07 04:39:37.000050392") ==
+               SysTime(DateTime(2013, 2, 7, 4, 39, 37), hnsecs(503)));
+
+        assert(SysTime.fromSimpleString("-0004-Jan-05 00:00:02") ==
+               SysTime(DateTime(-4, 1, 5, 0, 0, 2)));
+
+        assert(SysTime.fromSimpleString(" 2010-Jul-04 07:06:12 ") ==
+               SysTime(DateTime(2010, 7, 4, 7, 6, 12)));
+
+        assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12Z") ==
+               SysTime(DateTime(2010, 7, 4, 7, 6, 12), UTC()));
+
+        assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12-08:00") ==
+               SysTime(DateTime(2010, 7, 4, 7, 6, 12),
+                       new immutable SimpleTimeZone(hours(-8))));
+
+        assert(SysTime.fromSimpleString("2010-Jul-04 07:06:12+08:00") ==
+               SysTime(DateTime(2010, 7, 4, 7, 6, 12),
+                       new immutable SimpleTimeZone(hours(8))));
+    
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+    import core.time : days, hours, seconds;
+    import std.datetime.date : Date, DateTime;
+    import std.datetime.timezone : SimpleTimeZone, UTC;
+
+    const dt = DateTime(2018, 1, 1, 10, 30, 0);
+    // make a specific point in time in the UTC timezone
+    auto st = SysTime(dt, UTC());
+    assert(st.year == 2018);
+    assert(st.hour == 10);
+
+    // cast to convert
+    assert(cast(DateTime) st == dt);
+    assert(cast(Date) st == Date(2018, 1, 1));
+
+    // make a specific point in time in the New York timezone
+    const ny = SysTime(dt,
+        new immutable SimpleTimeZone(-5.hours, "America/New_York")
+    );
+    assert(ny != st);
+    assert(ny.hour == 10);
+
+    // ISO standard time strings
+    assert(st.toISOString() == "20180101T103000Z");
+    assert(st.toISOExtString() == "2018-01-01T10:30:00Z");
+
+    // add two days and 30 seconds
+    st += 2.days + 30.seconds;
+    assert(st.toISOExtString() == "2018-01-03T10:30:30Z");
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+    import std.datetime.date : DateTime;
+    import std.datetime.timezone : UTC;
+
+    // Midnight, January 1st, 1970
+    assert(unixTimeToStdTime(0) == 621_355_968_000_000_000L);
+    assert(SysTime(unixTimeToStdTime(0)) ==
+           SysTime(DateTime(1970, 1, 1), UTC()));
+
+    assert(unixTimeToStdTime(int.max) == 642_830_804_470_000_000L);
+    assert(SysTime(unixTimeToStdTime(int.max)) ==
+           SysTime(DateTime(2038, 1, 19, 3, 14, 7), UTC()));
+
+    assert(unixTimeToStdTime(-127_127) == 621_354_696_730_000_000L);
+    assert(SysTime(unixTimeToStdTime(-127_127)) ==
+           SysTime(DateTime(1969, 12, 30, 12, 41, 13), UTC()));
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+    // Midnight, January 1st, 1970 UTC
+    assert(stdTimeToUnixTime(621_355_968_000_000_000L) == 0);
+
+    // 2038-01-19 03:14:07 UTC
+    assert(stdTimeToUnixTime(642_830_804_470_000_000L) == int.max);
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+    import std.datetime.date : DateTime;
+
+    assert(DosFileTimeToSysTime(0b00000000001000010000000000000000) == SysTime(DateTime(1980, 1, 1, 0, 0, 0)));
+    assert(DosFileTimeToSysTime(0b11111111100111111011111101111101) == SysTime(DateTime(2107, 12, 31, 23, 59, 58)));
+    assert(DosFileTimeToSysTime(0x3E3F8456) == SysTime(DateTime(2011, 1, 31, 16, 34, 44)));
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+    import std.datetime.date : DateTime;
+
+    assert(SysTimeToDosFileTime(SysTime(DateTime(1980, 1, 1, 0, 0, 0))) == 0b00000000001000010000000000000000);
+    assert(SysTimeToDosFileTime(SysTime(DateTime(2107, 12, 31, 23, 59, 58))) == 0b11111111100111111011111101111101);
+    assert(SysTimeToDosFileTime(SysTime(DateTime(2011, 1, 31, 16, 34, 44))) == 0x3E3F8456);
+}
+
+@safe unittest
+{
+    import std.datetime.systime;
+
+    import core.time : hours;
+    import std.datetime.date : DateTime, DateTimeException;
+    import std.datetime.timezone : SimpleTimeZone, UTC;
+    import std.exception : assertThrown;
+
+    auto tz = new immutable SimpleTimeZone(hours(-8));
+    assert(parseRFC822DateTime("Sat, 6 Jan 1990 12:14:19 -0800") ==
+           SysTime(DateTime(1990, 1, 6, 12, 14, 19), tz));
+
+    assert(parseRFC822DateTime("9 Jul 2002 13:11 +0000") ==
+           SysTime(DateTime(2002, 7, 9, 13, 11, 0), UTC()));
+
+    auto badStr = "29 Feb 2001 12:17:16 +0200";
+    assertThrown!DateTimeException(parseRFC822DateTime(badStr));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_datetime_timezone.d b/libphobos/testsuite/libphobos.phobos/std_datetime_timezone.d
new file mode 100644
index 0000000000000000000000000000000000000000..007902c339ec9b7517984fce82ad9a7a6059915a
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_datetime_timezone.d
@@ -0,0 +1,15 @@
+@safe unittest
+{
+    import std.datetime.timezone;
+
+        version (Posix)
+        {
+            auto tz = PosixTimeZone.getTimeZone("America/Los_Angeles");
+
+            assert(tz.name == "America/Los_Angeles");
+            assert(tz.stdName == "PST");
+            assert(tz.dstName == "PDT");
+        }
+    
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_demangle.d b/libphobos/testsuite/libphobos.phobos/std_demangle.d
new file mode 100644
index 0000000000000000000000000000000000000000..7ec7843a7c6b54dd6cfbcb4793ff6ae798b55012
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_demangle.d
@@ -0,0 +1,33 @@
+@safe pure unittest
+{
+    import std.demangle;
+
+    // int b in module a
+    assert(demangle("_D1a1bi") == "int a.b");
+    // char array foo in module test
+    assert(demangle("_D4test3fooAa") == "char[] test.foo");
+}
+
+@system unittest
+{
+    import std.demangle;
+
+    import std.ascii : isAlphaNum;
+    import std.algorithm.iteration : chunkBy, joiner, map;
+    import std.algorithm.mutation : copy;
+    import std.conv : to;
+    import std.demangle : demangle;
+    import std.functional : pipe;
+    import std.stdio : stdin, stdout;
+
+    void main()
+    {
+        stdin.byLineCopy
+            .map!(
+                l => l.chunkBy!(a => isAlphaNum(a) || a == '_')
+                      .map!(a => a[1].pipe!(to!string, demangle)).joiner
+            )
+            .copy(stdout.lockingTextWriter);
+    }
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_digest_crc.d b/libphobos/testsuite/libphobos.phobos/std_digest_crc.d
new file mode 100644
index 0000000000000000000000000000000000000000..9ca8dd698f7d57d57178a4604f5735a88e9c146b
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_digest_crc.d
@@ -0,0 +1,207 @@
+@safe unittest
+{
+    import std.digest.crc;
+
+    //Template API
+    import std.digest.crc;
+
+    ubyte[4] hash = crc32Of("The quick brown fox jumps over the lazy dog");
+    assert(crcHexString(hash) == "414FA339");
+
+    //Feeding data
+    ubyte[1024] data;
+    CRC32 crc;
+    crc.put(data[]);
+    crc.start(); //Start again
+    crc.put(data[]);
+    hash = crc.finish();
+}
+
+@safe unittest
+{
+    import std.digest.crc;
+
+    //OOP API
+    import std.digest.crc;
+
+    auto crc = new CRC32Digest();
+    ubyte[] hash = crc.digest("The quick brown fox jumps over the lazy dog");
+    assert(crcHexString(hash) == "414FA339"); //352441c2
+
+    //Feeding data
+    ubyte[1024] data;
+    crc.put(data[]);
+    crc.reset(); //Start again
+    crc.put(data[]);
+    hash = crc.finish();
+}
+
+@safe unittest
+{
+    import std.digest.crc;
+
+    //Simple example, hashing a string using crc32Of helper function
+    ubyte[4] hash32 = crc32Of("abc");
+    //Let's get a hash string
+    assert(crcHexString(hash32) == "352441C2");
+    // Repeat for CRC64
+    ubyte[8] hash64ecma = crc64ECMAOf("abc");
+    assert(crcHexString(hash64ecma) == "2CD8094A1A277627");
+    ubyte[8] hash64iso = crc64ISOOf("abc");
+    assert(crcHexString(hash64iso) == "3776C42000000000");
+}
+
+@safe unittest
+{
+    import std.digest.crc;
+
+    ubyte[1024] data;
+    //Using the basic API
+    CRC32 hash32;
+    CRC64ECMA hash64ecma;
+    CRC64ISO hash64iso;
+    //Initialize data here...
+    hash32.put(data);
+    ubyte[4] result32 = hash32.finish();
+    hash64ecma.put(data);
+    ubyte[8] result64ecma = hash64ecma.finish();
+    hash64iso.put(data);
+    ubyte[8] result64iso = hash64iso.finish();
+}
+
+@safe unittest
+{
+    import std.digest.crc;
+
+    //Let's use the template features:
+    //Note: When passing a CRC32 to a function, it must be passed by reference!
+    void doSomething(T)(ref T hash)
+    if (isDigest!T)
+    {
+      hash.put(cast(ubyte) 0);
+    }
+    CRC32 crc32;
+    crc32.start();
+    doSomething(crc32);
+    assert(crcHexString(crc32.finish()) == "D202EF8D");
+    // repeat for CRC64
+    CRC64ECMA crc64ecma;
+    crc64ecma.start();
+    doSomething(crc64ecma);
+    assert(crcHexString(crc64ecma.finish()) == "1FADA17364673F59");
+    CRC64ISO crc64iso;
+    crc64iso.start();
+    doSomething(crc64iso);
+    assert(crcHexString(crc64iso.finish()) == "6F90000000000000");
+}
+
+@system unittest
+{
+    import std.digest.crc;
+
+    ubyte[] data = [4,5,7,25];
+    assert(data.crc32Of == [167, 180, 199, 131]);
+
+    import std.utf : byChar;
+    assert("hello"d.byChar.crc32Of == [134, 166, 16, 54]);
+
+    ubyte[4] hash = "abc".crc32Of();
+    assert(hash == digest!CRC32("ab", "c"));
+
+    import std.range : iota;
+    enum ubyte S = 5, F = 66;
+    assert(iota(S, F).crc32Of == [59, 140, 234, 154]);
+}
+
+@system unittest
+{
+    import std.digest.crc;
+
+    ubyte[] data = [4,5,7,25];
+    assert(data.crc64ECMAOf == [58, 142, 220, 214, 118, 98, 105, 69]);
+
+    import std.utf : byChar;
+    assert("hello"d.byChar.crc64ECMAOf == [177, 55, 185, 219, 229, 218, 30, 155]);
+
+    ubyte[8] hash = "abc".crc64ECMAOf();
+    assert("abc".crc64ECMAOf == [39, 118, 39, 26, 74, 9, 216, 44]);
+    assert(hash == digest!CRC64ECMA("ab", "c"));
+
+    import std.range : iota;
+    enum ubyte S = 5, F = 66;
+    assert(iota(S, F).crc64ECMAOf == [6, 184, 91, 238, 46, 213, 127, 188]);
+}
+
+@system unittest
+{
+    import std.digest.crc;
+
+    ubyte[] data = [4,5,7,25];
+    assert(data.crc64ISOOf == [0, 0, 0, 80, 137, 232, 203, 120]);
+
+    import std.utf : byChar;
+    assert("hello"d.byChar.crc64ISOOf == [0, 0, 16, 216, 226, 238, 62, 60]);
+
+    ubyte[8] hash = "abc".crc64ISOOf();
+    assert("abc".crc64ISOOf == [0, 0, 0, 0, 32, 196, 118, 55]);
+    assert(hash == digest!CRC64ISO("ab", "c"));
+
+    import std.range : iota;
+    enum ubyte S = 5, F = 66;
+
+    assert(iota(S, F).crc64ISOOf == [21, 185, 116, 95, 219, 11, 54, 7]);
+}
+
+@safe unittest
+{
+    import std.digest.crc;
+
+    //Simple example, hashing a string using CRC32Digest.digest helper function
+    auto crc = new CRC32Digest();
+    ubyte[] hash = crc.digest("abc");
+    //Let's get a hash string
+    assert(crcHexString(hash) == "352441C2");
+}
+
+@system unittest
+{
+    import std.digest.crc;
+
+     //Let's use the OOP features:
+    void test(Digest dig)
+    {
+      dig.put(cast(ubyte) 0);
+    }
+    auto crc = new CRC32Digest();
+    test(crc);
+
+    //Let's use a custom buffer:
+    ubyte[4] buf;
+    ubyte[] result = crc.finish(buf[]);
+    assert(crcHexString(result) == "D202EF8D");
+}
+
+@safe unittest
+{
+    import std.digest.crc;
+
+    //Simple example
+    auto hash = new CRC32Digest();
+    hash.put(cast(ubyte) 0);
+    ubyte[] result = hash.finish();
+}
+
+@system unittest
+{
+    import std.digest.crc;
+
+    //using a supplied buffer
+    ubyte[4] buf;
+    auto hash = new CRC32Digest();
+    hash.put(cast(ubyte) 0);
+    ubyte[] result = hash.finish(buf[]);
+    //The result is now in result (and in buf. If you pass a buffer which is bigger than
+    //necessary, result will have the correct length, but buf will still have it's original
+    //length)
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_digest_hmac.d b/libphobos/testsuite/libphobos.phobos/std_digest_hmac.d
new file mode 100644
index 0000000000000000000000000000000000000000..db5fb972078216e17b8aea4761bf4de8871ab83c
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_digest_hmac.d
@@ -0,0 +1,145 @@
+@safe unittest
+{
+    import std.digest.hmac;
+
+    import std.ascii : LetterCase;
+    import std.digest : toHexString;
+    import std.digest.sha : SHA1;
+    import std.string : representation;
+
+    auto secret = "secret".representation;
+    assert("The quick brown fox jumps over the lazy dog"
+            .representation
+            .hmac!SHA1(secret)
+            .toHexString!(LetterCase.lower) == "198ea1ea04c435c1246b586a06d5cf11c3ffcda6");
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.digest.hmac;
+
+        import std.digest.sha : SHA1;
+        import std.string : representation;
+        auto hmac = HMAC!SHA1("My s3cR3T keY".representation);
+        hmac.put("Hello, world".representation);
+        static immutable expected = [
+            130, 32, 235, 44, 208, 141,
+            150, 232, 211, 214, 162, 195,
+            188, 127, 52, 89, 100, 68, 90, 216];
+        assert(hmac.finish() == expected);
+    
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.digest.hmac;
+
+        import std.digest.sha : SHA1;
+        import std.string : representation;
+        string data1 = "Hello, world", data2 = "Hola mundo";
+        auto hmac = HMAC!SHA1("My s3cR3T keY".representation);
+        hmac.put(data1.representation);
+        hmac.start();                   // reset digest
+        hmac.put(data2.representation); // start over
+        static immutable expected = [
+            122, 151, 232, 240, 249, 80,
+            19, 178, 186, 77, 110, 23, 208,
+            52, 11, 88, 34, 151, 192, 255];
+        assert(hmac.finish() == expected);
+    
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.digest.hmac;
+
+        import std.digest.hmac, std.digest.sha;
+        import std.string : representation;
+        string data1 = "Hello, world", data2 = "Hola mundo";
+        auto hmac = HMAC!SHA1("My s3cR3T keY".representation);
+        hmac.put(data1.representation)
+            .put(data2.representation);
+        static immutable expected = [
+            197, 57, 52, 3, 13, 194, 13,
+            36, 117, 228, 8, 11, 111, 51,
+            165, 3, 123, 31, 251, 113];
+        assert(hmac.finish() == expected);
+    
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.digest.hmac;
+
+        import std.digest.sha : SHA1;
+        import std.string : representation;
+        string data1 = "Hello, world", data2 = "Hola mundo";
+        auto hmac = HMAC!SHA1("My s3cR3T keY".representation);
+        auto testDigest = hmac.put(data1.representation)
+                          .put(data2.representation)
+                          .finish();
+        static immutable expected = [
+            197, 57, 52, 3, 13, 194, 13,
+            36, 117, 228, 8, 11, 111, 51,
+            165, 3, 123, 31, 251, 113];
+        assert(testDigest == expected);
+    
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.digest.hmac;
+
+        import std.digest.sha : SHA1;
+        import std.string : representation;
+        string data1 = "Hello, world", data2 = "Hola mundo";
+        auto digest = hmac!SHA1("My s3cR3T keY".representation)
+                          .put(data1.representation)
+                          .put(data2.representation)
+                          .finish();
+        static immutable expected = [
+            197, 57, 52, 3, 13, 194, 13, 36,
+            117, 228, 8, 11, 111, 51, 165,
+            3, 123, 31, 251, 113];
+        assert(digest == expected);
+    
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.digest.hmac;
+
+        import std.algorithm.iteration : map;
+        import std.digest.sha : SHA1;
+        import std.string : representation;
+        string data = "Hello, world";
+        auto digest = data.representation
+                      .map!(a => cast(ubyte)(a+1))
+                      .hmac!SHA1("My s3cR3T keY".representation);
+        static assert(is(typeof(digest) == ubyte[20]));
+        static immutable expected = [
+            163, 208, 118, 179, 216, 93,
+            17, 10, 84, 200, 87, 104, 244,
+            111, 136, 214, 167, 210, 58, 10];
+        assert(digest == expected);
+    
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.digest.hmac;
+
+    import std.digest.sha : SHA1;
+    import std.string : representation;
+    string data1 = "Hello, world", data2 = "Hola mundo";
+    auto hmac = HMAC!SHA1("My s3cR3T keY".representation);
+    auto digest = hmac.put(data1.representation)
+                      .put(data2.representation)
+                      .finish();
+    static immutable expected = [
+        197, 57, 52, 3, 13, 194, 13,
+        36, 117, 228, 8, 11, 111, 51,
+        165, 3, 123, 31, 251, 113];
+    assert(digest == expected);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_digest_md.d b/libphobos/testsuite/libphobos.phobos/std_digest_md.d
new file mode 100644
index 0000000000000000000000000000000000000000..a9cf45fa94e056b4a2fd8fd18dfe51e7c546ae36
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_digest_md.d
@@ -0,0 +1,124 @@
+@safe unittest
+{
+    import std.digest.md;
+
+    //Template API
+    import std.digest.md;
+
+    //Feeding data
+    ubyte[1024] data;
+    MD5 md5;
+    md5.start();
+    md5.put(data[]);
+    md5.start(); //Start again
+    md5.put(data[]);
+    auto hash = md5.finish();
+}
+
+@safe unittest
+{
+    import std.digest.md;
+
+    //OOP API
+    import std.digest.md;
+
+    auto md5 = new MD5Digest();
+    ubyte[] hash = md5.digest("abc");
+    assert(toHexString(hash) == "900150983CD24FB0D6963F7D28E17F72");
+
+    //Feeding data
+    ubyte[1024] data;
+    md5.put(data[]);
+    md5.reset(); //Start again
+    md5.put(data[]);
+    hash = md5.finish();
+}
+
+@safe unittest
+{
+    import std.digest.md;
+
+            //Simple example
+            MD5 hash;
+            hash.start();
+            hash.put(cast(ubyte) 0);
+            ubyte[16] result = hash.finish();
+        
+}
+
+@safe unittest
+{
+    import std.digest.md;
+
+    //Simple example, hashing a string using md5Of helper function
+    ubyte[16] hash = md5Of("abc");
+    //Let's get a hash string
+    assert(toHexString(hash) == "900150983CD24FB0D6963F7D28E17F72");
+}
+
+@safe unittest
+{
+    import std.digest.md;
+
+    //Using the basic API
+    MD5 hash;
+    hash.start();
+    ubyte[1024] data;
+    //Initialize data here...
+    hash.put(data);
+    ubyte[16] result = hash.finish();
+}
+
+@safe unittest
+{
+    import std.digest.md;
+
+    //Let's use the template features:
+    void doSomething(T)(ref T hash)
+    if (isDigest!T)
+    {
+        hash.put(cast(ubyte) 0);
+    }
+    MD5 md5;
+    md5.start();
+    doSomething(md5);
+    assert(toHexString(md5.finish()) == "93B885ADFE0DA089CDF634904FD59F71");
+}
+
+@safe unittest
+{
+    import std.digest.md;
+
+    ubyte[16] hash = md5Of("abc");
+    assert(hash == digest!MD5("abc"));
+}
+
+@safe unittest
+{
+    import std.digest.md;
+
+    //Simple example, hashing a string using Digest.digest helper function
+    auto md5 = new MD5Digest();
+    ubyte[] hash = md5.digest("abc");
+    //Let's get a hash string
+    assert(toHexString(hash) == "900150983CD24FB0D6963F7D28E17F72");
+}
+
+@system unittest
+{
+    import std.digest.md;
+
+     //Let's use the OOP features:
+    void test(Digest dig)
+    {
+      dig.put(cast(ubyte) 0);
+    }
+    auto md5 = new MD5Digest();
+    test(md5);
+
+    //Let's use a custom buffer:
+    ubyte[16] buf;
+    ubyte[] result = md5.finish(buf[]);
+    assert(toHexString(result) == "93B885ADFE0DA089CDF634904FD59F71");
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_digest_murmurhash.d b/libphobos/testsuite/libphobos.phobos/std_digest_murmurhash.d
new file mode 100644
index 0000000000000000000000000000000000000000..c44496d54780e69f7689c51bfcc3bb37743d08b7
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_digest_murmurhash.d
@@ -0,0 +1,82 @@
+@safe unittest
+{
+    import std.digest.murmurhash;
+
+    // MurmurHash3!32, MurmurHash3!(128, 32) and MurmurHash3!(128, 64) implement
+    // the std.digest Template API.
+    static assert(isDigest!(MurmurHash3!32));
+    // The convenient digest template allows for quick hashing of any data.
+    ubyte[4] hashed = digest!(MurmurHash3!32)([1, 2, 3, 4]);
+    assert(hashed == [0, 173, 69, 68]);
+}
+
+@safe unittest
+{
+    import std.digest.murmurhash;
+
+    // One can also hash ubyte data piecewise by instanciating a hasher and call
+    // the 'put' method.
+    const(ubyte)[] data1 = [1, 2, 3];
+    const(ubyte)[] data2 = [4, 5, 6, 7];
+    // The incoming data will be buffered and hashed element by element.
+    MurmurHash3!32 hasher;
+    hasher.put(data1);
+    hasher.put(data2);
+    // The call to 'finish' ensures:
+    // - the remaining bits are processed
+    // - the hash gets finalized
+    auto hashed = hasher.finish();
+    assert(hashed == [181, 151, 88, 252]);
+}
+
+@safe unittest
+{
+    import std.digest.murmurhash;
+
+    // Using `putElements`, `putRemainder` and `finalize` you gain full
+    // control over which part of the algorithm to run.
+    // This allows for maximum throughput but needs extra care.
+
+    // Data type must be the same as the hasher's element type:
+    // - uint for MurmurHash3!32
+    // - uint[4] for MurmurHash3!(128, 32)
+    // - ulong[2] for MurmurHash3!(128, 64)
+    const(uint)[] data = [1, 2, 3, 4];
+    // Note the hasher starts with 'Fast'.
+    MurmurHash3!32 hasher;
+    // Push as many array of elements as you need. The less calls the better.
+    hasher.putElements(data);
+    // Put remainder bytes if needed. This method can be called only once.
+    hasher.putRemainder(ubyte(1), ubyte(1), ubyte(1));
+    // Call finalize to incorporate data length in the hash.
+    hasher.finalize();
+    // Finally get the hashed value.
+    auto hashed = hasher.getBytes();
+    assert(hashed == [188, 165, 108, 2]);
+}
+
+@safe unittest
+{
+    import std.digest.murmurhash;
+
+    ubyte[4] hashed = digest!(MurmurHash3!32)([1, 2, 3, 4]);
+    assert(hashed == [0, 173, 69, 68]);
+}
+
+@safe unittest
+{
+    import std.digest.murmurhash;
+
+    const(ubyte)[] data1 = [1, 2, 3];
+    const(ubyte)[] data2 = [4, 5, 6, 7];
+    // The incoming data will be buffered and hashed element by element.
+    MurmurHash3!32 hasher;
+    hasher.put(data1);
+    hasher.put(data2);
+    // The call to 'finish' ensures:
+    // - the remaining bits are processed
+    // - the hash gets finalized
+    auto hashed = hasher.finish();
+    assert(hashed == [181, 151, 88, 252]);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_digest_package.d b/libphobos/testsuite/libphobos.phobos/std_digest_package.d
new file mode 100644
index 0000000000000000000000000000000000000000..330326c3fdecba9e7783e828e327ed67e18b2e23
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_digest_package.d
@@ -0,0 +1,492 @@
+@system unittest
+{
+    import std.digest;
+
+    import std.digest.crc;
+
+    //Simple example
+    char[8] hexHash = hexDigest!CRC32("The quick brown fox jumps over the lazy dog");
+    assert(hexHash == "39A34F41");
+
+    //Simple example, using the API manually
+    CRC32 context = makeDigest!CRC32();
+    context.put(cast(ubyte[])"The quick brown fox jumps over the lazy dog");
+    ubyte[4] hash = context.finish();
+    assert(toHexString(hash) == "39A34F41");
+}
+
+@system unittest
+{
+    import std.digest;
+
+    //Generating the hashes of a file, idiomatic D way
+    import std.digest.crc, std.digest.md, std.digest.sha;
+    import std.stdio;
+
+    // Digests a file and prints the result.
+    void digestFile(Hash)(string filename)
+    if (isDigest!Hash)
+    {
+        auto file = File(filename);
+        auto result = digest!Hash(file.byChunk(4096 * 1024));
+        writefln("%s (%s) = %s", Hash.stringof, filename, toHexString(result));
+    }
+
+    void main(string[] args)
+    {
+        foreach (name; args[1 .. $])
+        {
+            digestFile!MD5(name);
+            digestFile!SHA1(name);
+            digestFile!CRC32(name);
+        }
+    }
+}
+
+@system unittest
+{
+    import std.digest;
+
+    //Generating the hashes of a file using the template API
+    import std.digest.crc, std.digest.md, std.digest.sha;
+    import std.stdio;
+    // Digests a file and prints the result.
+    void digestFile(Hash)(ref Hash hash, string filename)
+    if (isDigest!Hash)
+    {
+        File file = File(filename);
+
+        //As digests imlement OutputRange, we could use std.algorithm.copy
+        //Let's do it manually for now
+        foreach (buffer; file.byChunk(4096 * 1024))
+            hash.put(buffer);
+
+        auto result = hash.finish();
+        writefln("%s (%s) = %s", Hash.stringof, filename, toHexString(result));
+    }
+
+    void uMain(string[] args)
+    {
+        MD5 md5;
+        SHA1 sha1;
+        CRC32 crc32;
+
+        md5.start();
+        sha1.start();
+        crc32.start();
+
+        foreach (arg; args[1 .. $])
+        {
+            digestFile(md5, arg);
+            digestFile(sha1, arg);
+            digestFile(crc32, arg);
+        }
+    }
+}
+
+@system unittest
+{
+    import std.digest;
+
+    import std.digest.crc, std.digest.md, std.digest.sha;
+    import std.stdio;
+
+    // Digests a file and prints the result.
+    void digestFile(Digest hash, string filename)
+    {
+        File file = File(filename);
+
+        //As digests implement OutputRange, we could use std.algorithm.copy
+        //Let's do it manually for now
+        foreach (buffer; file.byChunk(4096 * 1024))
+          hash.put(buffer);
+
+        ubyte[] result = hash.finish();
+        writefln("%s (%s) = %s", typeid(hash).toString(), filename, toHexString(result));
+    }
+
+    void umain(string[] args)
+    {
+        auto md5 = new MD5Digest();
+        auto sha1 = new SHA1Digest();
+        auto crc32 = new CRC32Digest();
+
+        foreach (arg; args[1 .. $])
+        {
+          digestFile(md5, arg);
+          digestFile(sha1, arg);
+          digestFile(crc32, arg);
+        }
+    }
+}
+
+@system unittest
+{
+    import std.digest;
+
+    //Using the OutputRange feature
+    import std.algorithm.mutation : copy;
+    import std.digest.md;
+    import std.range : repeat;
+
+    auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000);
+    auto ctx = makeDigest!MD5();
+    copy(oneMillionRange, &ctx); //Note: You must pass a pointer to copy!
+    assert(ctx.finish().toHexString() == "7707D6AE4E027C70EEA2A935C2296F21");
+}
+
+@system unittest
+{
+    import std.digest;
+
+    import std.digest.crc;
+    static assert(isDigest!CRC32);
+}
+
+@system unittest
+{
+    import std.digest;
+
+    import std.digest.crc;
+    void myFunction(T)()
+    if (isDigest!T)
+    {
+        T dig;
+        dig.start();
+        auto result = dig.finish();
+    }
+    myFunction!CRC32();
+}
+
+@system unittest
+{
+    import std.digest;
+
+    import std.digest.crc;
+    assert(is(DigestType!(CRC32) == ubyte[4]));
+}
+
+@system unittest
+{
+    import std.digest;
+
+    import std.digest.crc;
+    CRC32 dig;
+    dig.start();
+    DigestType!CRC32 result = dig.finish();
+}
+
+@system unittest
+{
+    import std.digest;
+
+    import std.digest.crc, std.digest.md;
+    assert(!hasPeek!(MD5));
+    assert(hasPeek!CRC32);
+}
+
+@system unittest
+{
+    import std.digest;
+
+    import std.digest.crc;
+    void myFunction(T)()
+    if (hasPeek!T)
+    {
+        T dig;
+        dig.start();
+        auto result = dig.peek();
+    }
+    myFunction!CRC32();
+}
+
+@system unittest
+{
+    import std.digest;
+
+    import std.digest.hmac, std.digest.md;
+    static assert(hasBlockSize!MD5        && MD5.blockSize      == 512);
+    static assert(hasBlockSize!(HMAC!MD5) && HMAC!MD5.blockSize == 512);
+}
+
+@system unittest
+{
+    import std.digest;
+
+    import std.digest.md;
+    import std.range : repeat;
+    auto testRange = repeat!ubyte(cast(ubyte)'a', 100);
+    auto md5 = digest!MD5(testRange);
+}
+
+@system unittest
+{
+    import std.digest;
+
+    import std.digest.crc, std.digest.md, std.digest.sha;
+    auto md5   = digest!MD5(  "The quick brown fox jumps over the lazy dog");
+    auto sha1  = digest!SHA1( "The quick brown fox jumps over the lazy dog");
+    auto crc32 = digest!CRC32("The quick brown fox jumps over the lazy dog");
+    assert(toHexString(crc32) == "39A34F41");
+}
+
+@system unittest
+{
+    import std.digest;
+
+    import std.digest.crc;
+    auto crc32 = digest!CRC32("The quick ", "brown ", "fox jumps over the lazy dog");
+    assert(toHexString(crc32) == "39A34F41");
+}
+
+@system unittest
+{
+    import std.digest;
+
+    import std.digest.md;
+    import std.range : repeat;
+    auto testRange = repeat!ubyte(cast(ubyte)'a', 100);
+    assert(hexDigest!MD5(testRange) == "36A92CC94A9E0FA21F625F8BFB007ADF");
+}
+
+@system unittest
+{
+    import std.digest;
+
+    import std.digest.crc;
+    assert(hexDigest!(CRC32, Order.decreasing)("The quick brown fox jumps over the lazy dog") == "414FA339");
+}
+
+@system unittest
+{
+    import std.digest;
+
+    import std.digest.crc;
+    assert(hexDigest!(CRC32, Order.decreasing)("The quick ", "brown ", "fox jumps over the lazy dog") == "414FA339");
+}
+
+@system unittest
+{
+    import std.digest;
+
+    import std.digest.md;
+    auto md5 = makeDigest!MD5();
+    md5.put(0);
+    assert(toHexString(md5.finish()) == "93B885ADFE0DA089CDF634904FD59F71");
+}
+
+@system unittest
+{
+    import std.digest;
+
+    //Using the OutputRange feature
+    import std.algorithm.mutation : copy;
+    import std.digest.md;
+    import std.range : repeat;
+
+    auto oneMillionRange = repeat!ubyte(cast(ubyte)'a', 1000000);
+    auto ctx = new MD5Digest();
+    copy(oneMillionRange, ctx);
+    assert(ctx.finish().toHexString() == "7707D6AE4E027C70EEA2A935C2296F21");
+}
+
+@system unittest
+{
+    import std.digest;
+
+    import std.digest.crc, std.digest.md, std.digest.sha;
+    ubyte[] md5   = (new MD5Digest()).digest("The quick brown fox jumps over the lazy dog");
+    ubyte[] sha1  = (new SHA1Digest()).digest("The quick brown fox jumps over the lazy dog");
+    ubyte[] crc32 = (new CRC32Digest()).digest("The quick brown fox jumps over the lazy dog");
+    assert(crcHexString(crc32) == "414FA339");
+}
+
+@system unittest
+{
+    import std.digest;
+
+    import std.digest.crc;
+    ubyte[] crc32 = (new CRC32Digest()).digest("The quick ", "brown ", "fox jumps over the lazy dog");
+    assert(crcHexString(crc32) == "414FA339");
+}
+
+@system unittest
+{
+    import std.digest;
+
+    void test(Digest dig)
+    {
+        dig.put(cast(ubyte) 0); //single ubyte
+        dig.put(cast(ubyte) 0, cast(ubyte) 0); //variadic
+        ubyte[10] buf;
+        dig.put(buf); //buffer
+    }
+}
+
+@safe unittest
+{
+    import std.digest;
+
+    import std.digest.crc : CRC32;
+
+    auto crc32 = digest!CRC32("The quick ", "brown ", "fox jumps over the lazy dog");
+    assert(crc32.toHexString!(Order.decreasing) == "414FA339");
+    assert(crc32.toHexString!(LetterCase.lower, Order.decreasing) == "414fa339");
+}
+
+@safe unittest
+{
+    import std.digest;
+
+    import std.digest.crc;
+    //Test with template API:
+    auto crc32 = digest!CRC32("The quick ", "brown ", "fox jumps over the lazy dog");
+    //Lower case variant:
+    assert(toHexString!(LetterCase.lower)(crc32) == "39a34f41");
+    //Usually CRCs are printed in this order, though:
+    assert(toHexString!(Order.decreasing)(crc32) == "414FA339");
+    assert(toHexString!(LetterCase.lower, Order.decreasing)(crc32) == "414fa339");
+}
+
+@safe unittest
+{
+    import std.digest;
+
+    import std.digest.crc;
+    // With OOP API
+    auto crc32 = (new CRC32Digest()).digest("The quick ", "brown ", "fox jumps over the lazy dog");
+    //Usually CRCs are printed in this order, though:
+    assert(toHexString!(Order.decreasing)(crc32) == "414FA339");
+}
+
+@system unittest
+{
+    import std.digest;
+
+    import std.digest.md;
+    //Simple example
+    auto hash = new WrapperDigest!MD5();
+    hash.put(cast(ubyte) 0);
+    auto result = hash.finish();
+}
+
+@system unittest
+{
+    import std.digest;
+
+    //using a supplied buffer
+    import std.digest.md;
+    ubyte[16] buf;
+    auto hash = new WrapperDigest!MD5();
+    hash.put(cast(ubyte) 0);
+    auto result = hash.finish(buf[]);
+    //The result is now in result (and in buf). If you pass a buffer which is bigger than
+    //necessary, result will have the correct length, but buf will still have it's original
+    //length
+}
+
+@system pure unittest
+{
+    import std.digest;
+
+    import std.digest.hmac : hmac;
+    import std.digest.sha : SHA1;
+    import std.string : representation;
+
+    // a typical HMAC data integrity verification
+    auto secret = "A7GZIP6TAQA6OHM7KZ42KB9303CEY0MOV5DD6NTV".representation;
+    auto data = "data".representation;
+
+    auto hex1 = data.hmac!SHA1(secret).toHexString;
+    auto hex2 = data.hmac!SHA1(secret).toHexString;
+    auto hex3 = "data1".representation.hmac!SHA1(secret).toHexString;
+
+    assert( secureEqual(hex1[], hex2[]));
+    assert(!secureEqual(hex1[], hex3[]));
+}
+
+@safe unittest
+{
+    import std.digest;
+
+    assert(isHexString("0x0123456789ABCDEFabcdef"));
+    assert(isHexString("0123456789ABCDEFabcdef"));
+    assert(!isHexString("g"));
+    assert(!isHexString("#"));
+}
+
+@safe unittest
+{
+    import std.digest;
+
+    import std.range.primitives : ElementType, isForwardRange;
+    import std.traits : ReturnType;
+
+    // The decoder implements a forward range.
+    static assert(isForwardRange!(ReturnType!(fromHexStringAsRange!string)));
+    static assert(isForwardRange!(ReturnType!(fromHexStringAsRange!wstring)));
+    static assert(isForwardRange!(ReturnType!(fromHexStringAsRange!dstring)));
+
+    // The element type of the range is always `ubyte`.
+    static assert(
+        is(ElementType!(ReturnType!(fromHexStringAsRange!string)) == ubyte)
+    );
+    static assert(
+        is(ElementType!(ReturnType!(fromHexStringAsRange!wstring)) == ubyte)
+    );
+    static assert(
+        is(ElementType!(ReturnType!(fromHexStringAsRange!dstring)) == ubyte)
+    );
+}
+
+@safe unittest
+{
+    import std.digest;
+
+    // Single byte
+    assert("0xff".fromHexString  == [255]);
+    assert("0xff"w.fromHexString == [255]);
+    assert("0xff"d.fromHexString == [255]);
+    assert("0xC0".fromHexString  == [192]);
+    assert("0x00".fromHexString  == [0]);
+
+    // Nothing
+    assert("".fromHexString  == []);
+    assert(""w.fromHexString == []);
+    assert(""d.fromHexString == []);
+
+    // Nothing but a prefix
+    assert("0x".fromHexString  == []);
+    assert("0x"w.fromHexString == []);
+    assert("0x"d.fromHexString == []);
+
+    // Half a byte
+    assert("0x1".fromHexString  == [0x01]);
+    assert("0x1"w.fromHexString == [0x01]);
+    assert("0x1"d.fromHexString == [0x01]);
+
+    // Mixed case is fine.
+    assert("0xAf".fromHexString == [0xAF]);
+    assert("0xaF".fromHexString == [0xAF]);
+
+    // Multiple bytes
+    assert("0xfff".fromHexString     == [0x0F, 0xFF]);
+    assert("0x123AaAa".fromHexString == [0x01, 0x23, 0xAA, 0xAA]);
+    assert("EBBBBF".fromHexString    == [0xEB, 0xBB, 0xBF]);
+
+    // md5 sum
+    assert("d41d8cd98f00b204e9800998ecf8427e".fromHexString == [
+        0xD4, 0x1D, 0x8C, 0xD9, 0x8F, 0x00, 0xB2, 0x04,
+        0xE9, 0x80, 0x09, 0x98, 0xEC, 0xF8, 0x42, 0x7E,
+    ]);
+}
+
+@safe unittest
+{
+    import std.digest;
+
+    // Cycle self-test
+    const ubyte[] initial = [0x00, 0x12, 0x34, 0xEB];
+    assert(initial == initial.toHexString().fromHexString());
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_digest_ripemd.d b/libphobos/testsuite/libphobos.phobos/std_digest_ripemd.d
new file mode 100644
index 0000000000000000000000000000000000000000..582a21bd7219abe6a25369fb9db55c144c8ed825
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_digest_ripemd.d
@@ -0,0 +1,127 @@
+@safe unittest
+{
+    import std.digest.ripemd;
+
+    //Template API
+    import std.digest.md;
+
+    ubyte[20] hash = ripemd160Of("abc");
+    assert(toHexString(hash) == "8EB208F7E05D987A9B044A8E98C6B087F15A0BFC");
+
+    //Feeding data
+    ubyte[1024] data;
+    RIPEMD160 md;
+    md.start();
+    md.put(data[]);
+    md.start(); //Start again
+    md.put(data[]);
+    hash = md.finish();
+}
+
+@safe unittest
+{
+    import std.digest.ripemd;
+
+    //OOP API
+    import std.digest.md;
+
+    auto md = new RIPEMD160Digest();
+    ubyte[] hash = md.digest("abc");
+    assert(toHexString(hash) == "8EB208F7E05D987A9B044A8E98C6B087F15A0BFC");
+
+    //Feeding data
+    ubyte[1024] data;
+    md.put(data[]);
+    md.reset(); //Start again
+    md.put(data[]);
+    hash = md.finish();
+}
+
+@safe unittest
+{
+    import std.digest.ripemd;
+
+    //Simple example, hashing a string using ripemd160Of helper function
+    ubyte[20] hash = ripemd160Of("abc");
+    //Let's get a hash string
+    assert(toHexString(hash) == "8EB208F7E05D987A9B044A8E98C6B087F15A0BFC");
+}
+
+@safe unittest
+{
+    import std.digest.ripemd;
+
+    //Using the basic API
+    RIPEMD160 hash;
+    hash.start();
+    ubyte[1024] data;
+    //Initialize data here...
+    hash.put(data);
+    ubyte[20] result = hash.finish();
+}
+
+@safe unittest
+{
+    import std.digest.ripemd;
+
+    //Let's use the template features:
+    void doSomething(T)(ref T hash)
+    if (isDigest!T)
+    {
+        hash.put(cast(ubyte) 0);
+    }
+    RIPEMD160 md;
+    md.start();
+    doSomething(md);
+    assert(toHexString(md.finish()) == "C81B94933420221A7AC004A90242D8B1D3E5070D");
+}
+
+@safe unittest
+{
+    import std.digest.ripemd;
+
+    //Simple example
+    RIPEMD160 hash;
+    hash.start();
+    hash.put(cast(ubyte) 0);
+    ubyte[20] result = hash.finish();
+    assert(toHexString(result) == "C81B94933420221A7AC004A90242D8B1D3E5070D");
+}
+
+@safe unittest
+{
+    import std.digest.ripemd;
+
+    ubyte[20] hash = ripemd160Of("abc");
+    assert(hash == digest!RIPEMD160("abc"));
+}
+
+@safe unittest
+{
+    import std.digest.ripemd;
+
+    //Simple example, hashing a string using Digest.digest helper function
+    auto md = new RIPEMD160Digest();
+    ubyte[] hash = md.digest("abc");
+    //Let's get a hash string
+    assert(toHexString(hash) == "8EB208F7E05D987A9B044A8E98C6B087F15A0BFC");
+}
+
+@system unittest
+{
+    import std.digest.ripemd;
+
+    //Let's use the OOP features:
+    void test(Digest dig)
+    {
+      dig.put(cast(ubyte) 0);
+    }
+    auto md = new RIPEMD160Digest();
+    test(md);
+
+    //Let's use a custom buffer:
+    ubyte[20] buf;
+    ubyte[] result = md.finish(buf[]);
+    assert(toHexString(result) == "C81B94933420221A7AC004A90242D8B1D3E5070D");
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_digest_sha.d b/libphobos/testsuite/libphobos.phobos/std_digest_sha.d
new file mode 100644
index 0000000000000000000000000000000000000000..04339ef8ac5134e7cfedba17a95b6c53a0980b67
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_digest_sha.d
@@ -0,0 +1,163 @@
+@safe unittest
+{
+    import std.digest.sha;
+
+    //Template API
+    import std.digest.sha;
+
+    ubyte[20] hash1 = sha1Of("abc");
+    assert(toHexString(hash1) == "A9993E364706816ABA3E25717850C26C9CD0D89D");
+
+    ubyte[28] hash224 = sha224Of("abc");
+    assert(toHexString(hash224) == "23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7");
+
+    //Feeding data
+    ubyte[1024] data;
+    SHA1 sha1;
+    sha1.start();
+    sha1.put(data[]);
+    sha1.start(); //Start again
+    sha1.put(data[]);
+    hash1 = sha1.finish();
+}
+
+@safe unittest
+{
+    import std.digest.sha;
+
+    //OOP API
+    import std.digest.sha;
+
+    auto sha1 = new SHA1Digest();
+    ubyte[] hash1 = sha1.digest("abc");
+    assert(toHexString(hash1) == "A9993E364706816ABA3E25717850C26C9CD0D89D");
+
+    auto sha224 = new SHA224Digest();
+    ubyte[] hash224 = sha224.digest("abc");
+    assert(toHexString(hash224) == "23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7");
+
+    //Feeding data
+    ubyte[1024] data;
+    sha1.put(data[]);
+    sha1.reset(); //Start again
+    sha1.put(data[]);
+    hash1 = sha1.finish();
+}
+
+@safe unittest
+{
+    import std.digest.sha;
+
+            //Simple example
+            SHA1 hash;
+            hash.start();
+            hash.put(cast(ubyte) 0);
+            ubyte[20] result = hash.finish();
+        
+}
+
+@safe unittest
+{
+    import std.digest.sha;
+
+    //Simple example, hashing a string using sha1Of helper function
+    ubyte[20] hash = sha1Of("abc");
+    //Let's get a hash string
+    assert(toHexString(hash) == "A9993E364706816ABA3E25717850C26C9CD0D89D");
+
+    //The same, but using SHA-224
+    ubyte[28] hash224 = sha224Of("abc");
+    assert(toHexString(hash224) == "23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7");
+}
+
+@safe unittest
+{
+    import std.digest.sha;
+
+    //Using the basic API
+    SHA1 hash;
+    hash.start();
+    ubyte[1024] data;
+    //Initialize data here...
+    hash.put(data);
+    ubyte[20] result = hash.finish();
+}
+
+@safe unittest
+{
+    import std.digest.sha;
+
+    //Let's use the template features:
+    //Note: When passing a SHA1 to a function, it must be passed by reference!
+    void doSomething(T)(ref T hash)
+    if (isDigest!T)
+    {
+      hash.put(cast(ubyte) 0);
+    }
+    SHA1 sha;
+    sha.start();
+    doSomething(sha);
+    assert(toHexString(sha.finish()) == "5BA93C9DB0CFF93F52B521D7420E43F6EDA2784F");
+}
+
+@safe unittest
+{
+    import std.digest.sha;
+
+    ubyte[20] hash = sha1Of("abc");
+    assert(hash == digest!SHA1("abc"));
+
+    ubyte[28] hash224 = sha224Of("abc");
+    assert(hash224 == digest!SHA224("abc"));
+
+    ubyte[32] hash256 = sha256Of("abc");
+    assert(hash256 == digest!SHA256("abc"));
+
+    ubyte[48] hash384 = sha384Of("abc");
+    assert(hash384 == digest!SHA384("abc"));
+
+    ubyte[64] hash512 = sha512Of("abc");
+    assert(hash512 == digest!SHA512("abc"));
+
+    ubyte[28] hash512_224 = sha512_224Of("abc");
+    assert(hash512_224 == digest!SHA512_224("abc"));
+
+    ubyte[32] hash512_256 = sha512_256Of("abc");
+    assert(hash512_256 == digest!SHA512_256("abc"));
+}
+
+@safe unittest
+{
+    import std.digest.sha;
+
+    //Simple example, hashing a string using Digest.digest helper function
+    auto sha = new SHA1Digest();
+    ubyte[] hash = sha.digest("abc");
+    //Let's get a hash string
+    assert(toHexString(hash) == "A9993E364706816ABA3E25717850C26C9CD0D89D");
+
+    //The same, but using SHA-224
+    auto sha224 = new SHA224Digest();
+    ubyte[] hash224 = sha224.digest("abc");
+    //Let's get a hash string
+    assert(toHexString(hash224) == "23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7");
+}
+
+@system unittest
+{
+    import std.digest.sha;
+
+    //Let's use the OOP features:
+    void test(Digest dig)
+    {
+      dig.put(cast(ubyte) 0);
+    }
+    auto sha = new SHA1Digest();
+    test(sha);
+
+    //Let's use a custom buffer:
+    ubyte[20] buf;
+    ubyte[] result = sha.finish(buf[]);
+    assert(toHexString(result) == "5BA93C9DB0CFF93F52B521D7420E43F6EDA2784F");
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_encoding.d b/libphobos/testsuite/libphobos.phobos/std_encoding.d
new file mode 100644
index 0000000000000000000000000000000000000000..15003e370c971db4a67e1637be64fff81a59a711
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_encoding.d
@@ -0,0 +1,167 @@
+@safe unittest
+{
+    import std.encoding;
+
+    assert(encodingName!(char) == "UTF-8");
+    assert(encodingName!(wchar) == "UTF-16");
+    assert(encodingName!(dchar) == "UTF-32");
+    assert(encodingName!(AsciiChar) == "ASCII");
+    assert(encodingName!(Latin1Char) == "ISO-8859-1");
+    assert(encodingName!(Latin2Char) == "ISO-8859-2");
+    assert(encodingName!(Windows1250Char) == "windows-1250");
+    assert(encodingName!(Windows1251Char) == "windows-1251");
+    assert(encodingName!(Windows1252Char) == "windows-1252");
+}
+
+@safe pure unittest
+{
+    import std.encoding;
+
+    assert( canEncode!(Latin1Char)('A'));
+    assert( canEncode!(Latin2Char)('A'));
+    assert(!canEncode!(AsciiChar)('\u00A0'));
+    assert( canEncode!(Latin1Char)('\u00A0'));
+    assert( canEncode!(Latin2Char)('\u00A0'));
+    assert( canEncode!(Windows1250Char)('\u20AC'));
+    assert(!canEncode!(Windows1250Char)('\u20AD'));
+    assert(!canEncode!(Windows1250Char)('\uFFFD'));
+    assert( canEncode!(Windows1251Char)('\u0402'));
+    assert(!canEncode!(Windows1251Char)('\u20AD'));
+    assert(!canEncode!(Windows1251Char)('\uFFFD'));
+    assert( canEncode!(Windows1252Char)('\u20AC'));
+    assert(!canEncode!(Windows1252Char)('\u20AD'));
+    assert(!canEncode!(Windows1252Char)('\uFFFD'));
+    assert(!canEncode!(char)(cast(dchar) 0x110000));
+}
+
+@safe pure unittest
+{
+    import std.encoding;
+
+    import std.algorithm.searching : find;
+    import std.utf : byDchar;
+
+    assert("The quick brown fox"
+        .byDchar
+        .find!(x => !canEncode!AsciiChar(x))
+        .empty);
+}
+
+@system pure unittest
+{
+    import std.encoding;
+
+    assert(!isValidCodeUnit(cast(char) 0xC0));
+    assert(!isValidCodeUnit(cast(char) 0xFF));
+    assert( isValidCodeUnit(cast(wchar) 0xD800));
+    assert(!isValidCodeUnit(cast(dchar) 0xD800));
+    assert(!isValidCodeUnit(cast(AsciiChar) 0xA0));
+    assert( isValidCodeUnit(cast(Windows1250Char) 0x80));
+    assert(!isValidCodeUnit(cast(Windows1250Char) 0x81));
+    assert( isValidCodeUnit(cast(Windows1251Char) 0x80));
+    assert(!isValidCodeUnit(cast(Windows1251Char) 0x98));
+    assert( isValidCodeUnit(cast(Windows1252Char) 0x80));
+    assert(!isValidCodeUnit(cast(Windows1252Char) 0x81));
+}
+
+@system pure unittest
+{
+    import std.encoding;
+
+    assert( isValid("\u20AC100"));
+    assert(!isValid(cast(char[3])[167, 133, 175]));
+}
+
+@system pure unittest
+{
+    import std.encoding;
+
+    assert(sanitize("hello \xF0\x80world") == "hello \xEF\xBF\xBDworld");
+}
+
+@system pure unittest
+{
+    import std.encoding;
+
+    assert(firstSequence("\u20AC1000") == "\u20AC".length);
+    assert(firstSequence("hel") == "h".length);
+}
+
+@system pure unittest
+{
+    import std.encoding;
+
+    assert(lastSequence("1000\u20AC") == "\u20AC".length);
+    assert(lastSequence("hellö") == "ö".length);
+}
+
+@system pure unittest
+{
+    import std.encoding;
+
+    assert(index("\u20AC100",1) == 3);
+    assert(index("hällo",2) == 3);
+}
+
+@system unittest
+{
+    import std.encoding;
+
+    string s = "hello";
+    string t;
+    foreach (c;codePoints(s))
+    {
+        t ~= cast(char) c;
+    }
+    assert(s == t);
+}
+
+@system unittest
+{
+    import std.encoding;
+
+    char[] a;
+    foreach (c;codeUnits!(char)(cast(dchar)'\u20AC'))
+    {
+        a ~= c;
+    }
+    assert(a.length == 3);
+    assert(a[0] == 0xE2);
+    assert(a[1] == 0x82);
+    assert(a[2] == 0xAC);
+}
+
+@system pure unittest
+{
+    import std.encoding;
+
+    wstring ws;
+    // transcode from UTF-8 to UTF-16
+    transcode("hello world",ws);
+    assert(ws == "hello world"w);
+
+    Latin1String ls;
+    // transcode from UTF-16 to ISO-8859-1
+    transcode(ws, ls);
+    assert(ls == "hello world");
+}
+
+@system unittest
+{
+    import std.encoding;
+
+    import std.format : format;
+
+    auto ts = dchar(0x0000FEFF) ~ "Hello World"d;
+
+    auto entry = getBOM(cast(ubyte[]) ts);
+    version (BigEndian)
+    {
+        assert(entry.schema == BOM.utf32be, format("%s", entry.schema));
+    }
+    else
+    {
+        assert(entry.schema == BOM.utf32le, format("%s", entry.schema));
+    }
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_exception.d b/libphobos/testsuite/libphobos.phobos/std_exception.d
new file mode 100644
index 0000000000000000000000000000000000000000..8f973208c1543bdad1259d8aa03f14c55597308e
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_exception.d
@@ -0,0 +1,543 @@
+@system unittest
+{
+    import std.exception;
+
+    import core.stdc.stdlib : malloc, free;
+    import std.algorithm.comparison : equal;
+    import std.algorithm.iteration : map, splitter;
+    import std.algorithm.searching : endsWith;
+    import std.conv : ConvException, to;
+    import std.range : front, retro;
+
+    // use enforce like assert
+    int a = 3;
+    enforce(a > 2, "a needs to be higher than 2.");
+
+    // enforce can throw a custom exception
+    enforce!ConvException(a > 2, "a needs to be higher than 2.");
+
+    // enforce will return it's input
+    enum size = 42;
+    auto memory = enforce(malloc(size), "malloc failed")[0 .. size];
+    scope(exit) free(memory.ptr);
+
+    // collectException can be used to test for exceptions
+    Exception e = collectException("abc".to!int);
+    assert(e.file.endsWith("conv.d"));
+
+    // and just for the exception message
+    string msg = collectExceptionMsg("abc".to!int);
+    assert(msg == "Unexpected 'a' when converting from type string to type int");
+
+    // assertThrown can be used to assert that an exception is thrown
+    assertThrown!ConvException("abc".to!int);
+
+    // ifThrown can be used to provide a default value if an exception is thrown
+    assert("x".to!int().ifThrown(0) == 0);
+
+    // handle is a more advanced version of ifThrown for ranges
+    auto r = "12,1337z32,54".splitter(',').map!(a => to!int(a));
+    auto h = r.handle!(ConvException, RangePrimitive.front, (e, r) => 0);
+    assert(h.equal([12, 0, 54]));
+    assertThrown!ConvException(h.retro.equal([54, 0, 12]));
+
+    // basicExceptionCtors avoids the boilerplate when creating custom exceptions
+    static class MeaCulpa : Exception
+    {
+        mixin basicExceptionCtors;
+    }
+    e = collectException((){throw new MeaCulpa("diagnostic message");}());
+    assert(e.msg == "diagnostic message");
+    assert(e.file == __FILE__);
+    assert(e.line == __LINE__ - 3);
+
+    // assumeWontThrow can be used to cast throwing code into `nothrow`
+    void exceptionFreeCode() nothrow
+    {
+        // auto-decoding only throws if an invalid UTF char is given
+        assumeWontThrow("abc".front);
+    }
+
+    // assumeUnique can be used to cast mutable instance to an `immutable` one
+    // use with care
+    char[] str = "  mutable".dup;
+    str[0 .. 2] = "im";
+    immutable res = assumeUnique(str);
+    assert(res == "immutable");
+}
+
+@system unittest
+{
+    import std.exception;
+
+    import core.exception : AssertError;
+
+    import std.string;
+    assertNotThrown!StringException(enforce!StringException(true, "Error!"));
+
+    //Exception is the default.
+    assertNotThrown(enforce!StringException(true, "Error!"));
+
+    assert(collectExceptionMsg!AssertError(assertNotThrown!StringException(
+               enforce!StringException(false, "Error!"))) ==
+           `assertNotThrown failed: StringException was thrown: Error!`);
+}
+
+@system unittest
+{
+    import std.exception;
+
+    import core.exception : AssertError;
+    import std.string;
+
+    assertThrown!StringException(enforce!StringException(false, "Error!"));
+
+    //Exception is the default.
+    assertThrown(enforce!StringException(false, "Error!"));
+
+    assert(collectExceptionMsg!AssertError(assertThrown!StringException(
+               enforce!StringException(true, "Error!"))) ==
+           `assertThrown failed: No StringException was thrown.`);
+}
+
+@system unittest
+{
+    import std.exception;
+
+    import core.stdc.stdlib : malloc, free;
+    import std.conv : ConvException, to;
+
+    // use enforce like assert
+    int a = 3;
+    enforce(a > 2, "a needs to be higher than 2.");
+
+    // enforce can throw a custom exception
+    enforce!ConvException(a > 2, "a needs to be higher than 2.");
+
+    // enforce will return it's input
+    enum size = 42;
+    auto memory = enforce(malloc(size), "malloc failed")[0 .. size];
+    scope(exit) free(memory.ptr);
+}
+
+@safe unittest
+{
+    import std.exception;
+
+    assertNotThrown(enforce(true, new Exception("this should not be thrown")));
+    assertThrown(enforce(false, new Exception("this should be thrown")));
+}
+
+@safe unittest
+{
+    import std.exception;
+
+    assert(enforce(123) == 123);
+
+    try
+    {
+        enforce(false, "error");
+        assert(false);
+    }
+    catch (Exception e)
+    {
+        assert(e.msg == "error");
+        assert(e.file == __FILE__);
+        assert(e.line == __LINE__-7);
+    }
+}
+
+@safe unittest
+{
+    import std.exception;
+
+    import std.conv : ConvException;
+    alias convEnforce = enforce!ConvException;
+    assertNotThrown(convEnforce(true));
+    assertThrown!ConvException(convEnforce(false, "blah"));
+}
+
+@system unittest
+{
+    import std.exception;
+
+    import core.stdc.stdio : fclose, fgets, fopen;
+    import std.file : thisExePath;
+    import std.string : toStringz;
+
+    auto f = fopen(thisExePath.toStringz, "r").errnoEnforce;
+    scope(exit) fclose(f);
+    char[100] buf;
+    auto line = fgets(buf.ptr, buf.length, f);
+    enforce(line !is null); // expect a non-empty line
+}
+
+@system unittest
+{
+    import std.exception;
+
+    int b;
+    int foo() { throw new Exception("blah"); }
+    assert(collectException(foo(), b));
+
+    version (D_NoBoundsChecks) {}
+    else
+    {
+        // check for out of bounds error
+        int[] a = new int[3];
+        import core.exception : RangeError;
+        assert(collectException!RangeError(a[4], b));
+    }
+}
+
+@safe unittest
+{
+    import std.exception;
+
+    int foo() { throw new Exception("blah"); }
+    assert(collectException(foo()).msg == "blah");
+}
+
+@safe unittest
+{
+    import std.exception;
+
+    void throwFunc() { throw new Exception("My Message."); }
+    assert(collectExceptionMsg(throwFunc()) == "My Message.");
+
+    void nothrowFunc() {}
+    assert(collectExceptionMsg(nothrowFunc()) is null);
+
+    void throwEmptyFunc() { throw new Exception(""); }
+    assert(collectExceptionMsg(throwEmptyFunc()) == emptyExceptionMsg);
+}
+
+@system unittest
+{
+    import std.exception;
+
+    int[] arr = new int[1];
+    auto arr1 = arr.assumeUnique;
+    static assert(is(typeof(arr1) == immutable(int)[]));
+    assert(arr == null);
+    assert(arr1 == [0]);
+}
+
+@system unittest
+{
+    import std.exception;
+
+    int[string] arr = ["a":1];
+    auto arr1 = arr.assumeUnique;
+    static assert(is(typeof(arr1) == immutable(int[string])));
+    assert(arr == null);
+    assert(arr1.keys == ["a"]);
+}
+
+@safe unittest
+{
+    import std.exception;
+
+    import std.math.algebraic : sqrt;
+
+    // This function may throw.
+    int squareRoot(int x)
+    {
+        if (x < 0)
+            throw new Exception("Tried to take root of negative number");
+        return cast(int) sqrt(cast(double) x);
+    }
+
+    // This function never throws.
+    int computeLength(int x, int y) nothrow
+    {
+        // Since x*x + y*y is always positive, we can safely assume squareRoot
+        // won't throw, and use it to implement this nothrow function. If it
+        // does throw (e.g., if x*x + y*y overflows a 32-bit value), then the
+        // program will terminate.
+        return assumeWontThrow(squareRoot(x*x + y*y));
+    }
+
+    assert(computeLength(3, 4) == 5);
+}
+
+@system unittest
+{
+    import std.exception;
+
+    int  i = 0;
+    int* p = null;
+    assert(!p.doesPointTo(i));
+    p = &i;
+    assert( p.doesPointTo(i));
+}
+
+@system unittest
+{
+    import std.exception;
+
+    struct S
+    {
+        int v;
+        int* p;
+    }
+    int i;
+    auto s = S(0, &i);
+
+    // structs and unions "own" their members
+    // pointsTo will answer true if one of the members pointsTo.
+    assert(!s.doesPointTo(s.v)); //s.v is just v member of s, so not pointed.
+    assert( s.p.doesPointTo(i)); //i is pointed by s.p.
+    assert( s  .doesPointTo(i)); //which means i is pointed by s itself.
+
+    // Unions will behave exactly the same. Points to will check each "member"
+    // individually, even if they share the same memory
+}
+
+@system unittest
+{
+    import std.exception;
+
+    int i;
+     // trick the compiler when initializing slice
+     // https://issues.dlang.org/show_bug.cgi?id=18637
+    int* p = &i;
+    int[]  slice = [0, 1, 2, 3, 4];
+    int[5] arr   = [0, 1, 2, 3, 4];
+    int*[]  slicep = [p];
+    int*[1] arrp   = [&i];
+
+    // A slice points to all of its members:
+    assert( slice.doesPointTo(slice[3]));
+    assert(!slice[0 .. 2].doesPointTo(slice[3])); // Object 3 is outside of the
+                                                  // slice [0 .. 2]
+
+    // Note that a slice will not take into account what its members point to.
+    assert( slicep[0].doesPointTo(i));
+    assert(!slicep   .doesPointTo(i));
+
+    // static arrays are objects that own their members, just like structs:
+    assert(!arr.doesPointTo(arr[0])); // arr[0] is just a member of arr, so not
+                                      // pointed.
+    assert( arrp[0].doesPointTo(i));  // i is pointed by arrp[0].
+    assert( arrp   .doesPointTo(i));  // which means i is pointed by arrp
+                                      // itself.
+
+    // Notice the difference between static and dynamic arrays:
+    assert(!arr  .doesPointTo(arr[0]));
+    assert( arr[].doesPointTo(arr[0]));
+    assert( arrp  .doesPointTo(i));
+    assert(!arrp[].doesPointTo(i));
+}
+
+@system unittest
+{
+    import std.exception;
+
+    class C
+    {
+        this(int* p){this.p = p;}
+        int* p;
+    }
+    int i;
+    C a = new C(&i);
+    C b = a;
+
+    // Classes are a bit particular, as they are treated like simple pointers
+    // to a class payload.
+    assert( a.p.doesPointTo(i)); // a.p points to i.
+    assert(!a  .doesPointTo(i)); // Yet a itself does not point i.
+
+    //To check the class payload itself, iterate on its members:
+    ()
+    {
+        import std.traits : Fields;
+
+        foreach (index, _; Fields!C)
+            if (doesPointTo(a.tupleof[index], i))
+                return;
+        assert(0);
+    }();
+
+    // To check if a class points a specific payload, a direct memmory check
+    // can be done:
+    auto aLoc = cast(ubyte[__traits(classInstanceSize, C)]*) a;
+    assert(b.doesPointTo(*aLoc)); // b points to where a is pointing
+}
+
+@safe unittest
+{
+    import std.exception;
+
+    import core.stdc.errno : EAGAIN;
+    auto ex = new ErrnoException("oh no", EAGAIN);
+    assert(ex.errno == EAGAIN);
+}
+
+@safe unittest
+{
+    import std.exception;
+
+    import core.stdc.errno : errno, EAGAIN;
+
+    auto old = errno;
+    scope(exit) errno = old;
+
+    // fake that errno got set by the callee
+    errno = EAGAIN;
+    auto ex = new ErrnoException("oh no");
+    assert(ex.errno == EAGAIN);
+}
+
+@safe unittest
+{
+    import std.exception;
+
+    import std.conv : to;
+    assert("x".to!int.ifThrown(0) == 0);
+}
+
+@safe unittest
+{
+    import std.exception;
+
+    import std.conv : ConvException, to;
+    string s = "true";
+    assert(s.to!int.ifThrown(cast(int) s.to!double)
+                   .ifThrown(cast(int) s.to!bool) == 1);
+
+    s = "2.0";
+    assert(s.to!int.ifThrown(cast(int) s.to!double)
+                   .ifThrown(cast(int) s.to!bool) == 2);
+
+    // Respond differently to different types of errors
+    alias orFallback = (lazy a)  => a.ifThrown!ConvException("not a number")
+                                     .ifThrown!Exception("number too small");
+
+    assert(orFallback(enforce("x".to!int < 1).to!string) == "not a number");
+    assert(orFallback(enforce("2".to!int < 1).to!string) == "number too small");
+}
+
+@safe unittest
+{
+    import std.exception;
+
+    // null and new Object have a common type(Object).
+    static assert(is(typeof(null.ifThrown(new Object())) == Object));
+    static assert(is(typeof((new Object()).ifThrown(null)) == Object));
+
+    // 1 and new Object do not have a common type.
+    static assert(!__traits(compiles, 1.ifThrown(new Object())));
+    static assert(!__traits(compiles, (new Object()).ifThrown(1)));
+}
+
+@system unittest
+{
+    import std.exception;
+
+    import std.format : format;
+    assert("%s".format.ifThrown!Exception(e => typeid(e).name) == "std.format.FormatException");
+}
+
+pure @safe unittest
+{
+    import std.exception;
+
+    import std.algorithm.comparison : equal;
+    import std.algorithm.iteration : map, splitter;
+    import std.conv : to, ConvException;
+
+    auto s = "12,1337z32,54,2,7,9,1z,6,8";
+
+    // The next line composition will throw when iterated
+    // as some elements of the input do not convert to integer
+    auto r = s.splitter(',').map!(a => to!int(a));
+
+    // Substitute 0 for cases of ConvException
+    auto h = r.handle!(ConvException, RangePrimitive.front, (e, r) => 0);
+    assert(h.equal([12, 0, 54, 2, 7, 9, 0, 6, 8]));
+}
+
+pure @safe unittest
+{
+    import std.exception;
+
+    import std.algorithm.comparison : equal;
+    import std.range : retro;
+    import std.utf : UTFException;
+
+    auto str = "hello\xFFworld"; // 0xFF is an invalid UTF-8 code unit
+
+    auto handled = str.handle!(UTFException, RangePrimitive.access,
+            (e, r) => ' '); // Replace invalid code points with spaces
+
+    assert(handled.equal("hello world")); // `front` is handled,
+    assert(handled.retro.equal("dlrow olleh")); // as well as `back`
+}
+
+pure @safe unittest
+{
+    import std.exception;
+
+    import std.algorithm.comparison : equal;
+    import std.algorithm.iteration : map, splitter;
+    import std.conv : to, ConvException;
+
+    auto s = "12,1337z32,54,2,7,9,1z,6,8";
+
+    // The next line composition will throw when iterated
+    // as some elements of the input do not convert to integer
+    auto r = s.splitter(',').map!(a => to!int(a));
+
+    // Substitute 0 for cases of ConvException
+    auto h = r.handle!(ConvException, RangePrimitive.front, (e, r) => 0);
+    assert(h.equal([12, 0, 54, 2, 7, 9, 0, 6, 8]));
+}
+
+pure @safe unittest
+{
+    import std.exception;
+
+    import std.algorithm.comparison : equal;
+    import std.range : retro;
+    import std.utf : UTFException;
+
+    auto str = "hello\xFFworld"; // 0xFF is an invalid UTF-8 code unit
+
+    auto handled = str.handle!(UTFException, RangePrimitive.access,
+            (e, r) => ' '); // Replace invalid code points with spaces
+
+    assert(handled.equal("hello world")); // `front` is handled,
+    assert(handled.retro.equal("dlrow olleh")); // as well as `back`
+}
+
+@safe unittest
+{
+    import std.exception;
+
+    class MeaCulpa: Exception
+    {
+        ///
+        mixin basicExceptionCtors;
+    }
+
+    try
+        throw new MeaCulpa("test");
+    catch (MeaCulpa e)
+    {
+        assert(e.msg == "test");
+        assert(e.file == __FILE__);
+        assert(e.line == __LINE__ - 5);
+    }
+}
+
+@safe pure nothrow unittest
+{
+    import std.exception;
+
+    class TestException : Exception { mixin basicExceptionCtors; }
+    auto e = new Exception("msg");
+    auto te1 = new TestException("foo");
+    auto te2 = new TestException("foo", e);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_affix_allocator.d b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_affix_allocator.d
new file mode 100644
index 0000000000000000000000000000000000000000..6b99e4e51f2358891205d7d89000835da693882f
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_affix_allocator.d
@@ -0,0 +1,14 @@
+@system unittest
+{
+    import std.experimental.allocator.building_blocks.affix_allocator;
+
+    import std.experimental.allocator.mallocator : Mallocator;
+    // One word before and after each allocation.
+    alias A = AffixAllocator!(Mallocator, size_t, size_t);
+    auto b = A.instance.allocate(11);
+    A.instance.prefix(b) = 0xCAFE_BABE;
+    A.instance.suffix(b) = 0xDEAD_BEEF;
+    assert(A.instance.prefix(b) == 0xCAFE_BABE
+        && A.instance.suffix(b) == 0xDEAD_BEEF);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_aligned_block_list.d b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_aligned_block_list.d
new file mode 100644
index 0000000000000000000000000000000000000000..b1d97226142b718f8a8384e4891ac677acc27eec
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_aligned_block_list.d
@@ -0,0 +1,139 @@
+@system unittest
+{
+    import std.experimental.allocator.building_blocks.aligned_block_list;
+
+    import std.experimental.allocator.building_blocks.ascending_page_allocator : AscendingPageAllocator;
+    import std.experimental.allocator.building_blocks.segregator : Segregator;
+    import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock;
+    import std.typecons : Ternary;
+
+    /*
+    In this example we use 'AlignedBlockList' in conjunction with other allocators
+    in order to create a more complex allocator.
+
+    The 'SuperAllocator' uses a 'Segregator' to distribute allocations to sub-allocators,
+    based on the requested size.
+
+    Each sub-allocator is represented by an 'AlignedBlockList' of 'BitmappedBlocks'.
+    Each 'AlignedBlockList' draws memory from a root allocator which in this case is an 'AscendingPageAllocator'
+
+    Such an allocator not only provides good performance, but also a low degree of memory fragmentation.
+    */
+    alias SuperAllocator = Segregator!(
+        32,
+        AlignedBlockList!(BitmappedBlock!32, AscendingPageAllocator*, 1 << 12),
+        Segregator!(
+
+        64,
+        AlignedBlockList!(BitmappedBlock!64, AscendingPageAllocator*, 1 << 12),
+        Segregator!(
+
+        128,
+        AlignedBlockList!(BitmappedBlock!128, AscendingPageAllocator*, 1 << 12),
+        AscendingPageAllocator*
+    )));
+
+    SuperAllocator a;
+    auto pageAlloc = AscendingPageAllocator(128 * 4096);
+
+    // Set the parent allocator for all the sub allocators
+    a.allocatorForSize!256 = &pageAlloc;
+    a.allocatorForSize!128.parent = &pageAlloc;
+    a.allocatorForSize!64.parent = &pageAlloc;
+    a.allocatorForSize!32.parent = &pageAlloc;
+
+    enum testNum = 10;
+    void[][testNum] buf;
+
+    // Allocations of size 32 will go to the first 'AlignedBlockList'
+    foreach (j; 0 .. testNum)
+    {
+        buf[j] = a.allocate(32);
+        assert(buf[j].length == 32);
+
+        // This is owned by the first 'AlignedBlockList'
+        assert(a.allocatorForSize!32.owns(buf[j]) == Ternary.yes);
+    }
+
+    // Free the memory
+    foreach (j; 0 .. testNum)
+        assert(a.deallocate(buf[j]));
+
+    // Allocations of size 64 will go to the second 'AlignedBlockList'
+    foreach (j; 0 .. testNum)
+    {
+        buf[j] = a.allocate(64);
+        assert(buf[j].length == 64);
+
+        // This is owned by the second 'AlignedBlockList'
+        assert(a.allocatorForSize!64.owns(buf[j]) == Ternary.yes);
+    }
+
+    // Free the memory
+    foreach (j; 0 .. testNum)
+        assert(a.deallocate(buf[j]));
+
+    // Allocations of size 128 will go to the third 'AlignedBlockList'
+    foreach (j; 0 .. testNum)
+    {
+        buf[j] = a.allocate(128);
+        assert(buf[j].length == 128);
+
+        // This is owned by the third 'AlignedBlockList'
+        assert(a.allocatorForSize!128.owns(buf[j]) == Ternary.yes);
+    }
+
+    // Free the memory
+    foreach (j; 0 .. testNum)
+        assert(a.deallocate(buf[j]));
+
+    // Allocations which exceed 128, will go to the 'AscendingPageAllocator*'
+    void[] b = a.allocate(256);
+    assert(b.length == 256);
+    a.deallocate(b);
+}
+
+@system unittest
+{
+    import std.experimental.allocator.building_blocks.aligned_block_list;
+
+    import std.experimental.allocator.building_blocks.region : SharedBorrowedRegion;
+    import std.experimental.allocator.building_blocks.ascending_page_allocator : SharedAscendingPageAllocator;
+    import std.experimental.allocator.building_blocks.null_allocator : NullAllocator;
+    import core.thread : ThreadGroup;
+
+    enum numThreads = 8;
+    enum size = 2048;
+    enum maxIter = 10;
+
+    /*
+    In this example we use 'SharedAlignedBlockList' together with
+    'SharedBorrowedRegion', in order to create a fast, thread-safe allocator.
+    */
+    alias SuperAllocator = SharedAlignedBlockList!(
+            SharedBorrowedRegion!(1),
+            SharedAscendingPageAllocator,
+            4096);
+
+    SuperAllocator a;
+    // The 'SuperAllocator' will draw memory from a 'SharedAscendingPageAllocator'
+    a.parent = SharedAscendingPageAllocator(4096 * 1024);
+
+    // Launch 'numThreads', each performing allocations
+    void fun()
+    {
+        foreach (i; 0 .. maxIter)
+        {
+            void[] b = a.allocate(size);
+            assert(b.length == size);
+        }
+    }
+
+    auto tg = new ThreadGroup;
+    foreach (i; 0 .. numThreads)
+    {
+        tg.create(&fun);
+    }
+    tg.joinAll();
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_allocator_list.d b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_allocator_list.d
new file mode 100644
index 0000000000000000000000000000000000000000..e43d9616ddabad15b3efe86d554e1382f69c3bf8
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_allocator_list.d
@@ -0,0 +1,45 @@
+@system unittest
+{
+    import std.experimental.allocator.building_blocks.allocator_list;
+
+    import std.algorithm.comparison : max;
+    import std.experimental.allocator.building_blocks.free_list : ContiguousFreeList;
+    import std.experimental.allocator.building_blocks.null_allocator : NullAllocator;
+    import std.experimental.allocator.building_blocks.region : Region;
+    import std.experimental.allocator.building_blocks.segregator : Segregator;
+    import std.experimental.allocator.gc_allocator : GCAllocator;
+    import std.experimental.allocator.mmap_allocator : MmapAllocator;
+
+    // Ouroboros allocator list based upon 4MB regions, fetched directly from
+    // mmap. All memory is released upon destruction.
+    alias A1 = AllocatorList!((n) => Region!MmapAllocator(max(n, 1024 * 4096)),
+        NullAllocator);
+
+    // Allocator list based upon 4MB regions, fetched from the garbage
+    // collector. All memory is released upon destruction.
+    alias A2 = AllocatorList!((n) => Region!GCAllocator(max(n, 1024 * 4096)));
+
+    // Ouroboros allocator list based upon 4MB regions, fetched from the garbage
+    // collector. Memory is left to the collector.
+    alias A3 = AllocatorList!(
+        (n) => Region!NullAllocator(new ubyte[max(n, 1024 * 4096)]),
+        NullAllocator);
+
+    // Allocator list that creates one freelist for all objects
+    alias A4 =
+        Segregator!(
+            64, AllocatorList!(
+                (n) => ContiguousFreeList!(NullAllocator, 0, 64)(
+                    cast(ubyte[])(GCAllocator.instance.allocate(4096)))),
+            GCAllocator);
+
+    A4 a;
+    auto small = a.allocate(64);
+    assert(small);
+    a.deallocate(small);
+    auto b1 = a.allocate(1024 * 8192);
+    assert(b1 !is null); // still works due to overdimensioning
+    b1 = a.allocate(1024 * 10);
+    assert(b1.length == 1024 * 10);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_ascending_page_allocator.d b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_ascending_page_allocator.d
new file mode 100644
index 0000000000000000000000000000000000000000..086714dbd095cfd594c0ed470799497b7e9dd65d
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_ascending_page_allocator.d
@@ -0,0 +1,52 @@
+@system @nogc nothrow unittest
+{
+    import std.experimental.allocator.building_blocks.ascending_page_allocator;
+
+    import core.memory : pageSize;
+
+    size_t numPages = 100;
+    void[] buf;
+    void[] prevBuf = null;
+    AscendingPageAllocator a = AscendingPageAllocator(numPages * pageSize);
+
+    foreach (i; 0 .. numPages)
+    {
+        // Allocation is rounded up to page size
+        buf = a.allocate(pageSize - 100);
+        assert(buf.length == pageSize - 100);
+
+        // Allocations are served at increasing addresses
+        if (prevBuf)
+            assert(prevBuf.ptr + pageSize == buf.ptr);
+
+        assert(a.deallocate(buf));
+        prevBuf = buf;
+    }
+}
+
+@system unittest
+{
+    import std.experimental.allocator.building_blocks.ascending_page_allocator;
+
+    import core.memory : pageSize;
+    import core.thread : ThreadGroup;
+
+    enum numThreads = 100;
+    shared SharedAscendingPageAllocator a = SharedAscendingPageAllocator(pageSize * numThreads);
+
+    void fun()
+    {
+        void[] b = a.allocate(pageSize);
+        assert(b.length == pageSize);
+
+        assert(a.deallocate(b));
+    }
+
+    auto tg = new ThreadGroup;
+    foreach (i; 0 .. numThreads)
+    {
+        tg.create(&fun);
+    }
+    tg.joinAll();
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_bitmapped_block.d b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_bitmapped_block.d
new file mode 100644
index 0000000000000000000000000000000000000000..e3a3865aa1836f79ef2f0cb9953efa30aa85b906
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_bitmapped_block.d
@@ -0,0 +1,139 @@
+@system unittest
+{
+    import std.experimental.allocator.building_blocks.bitmapped_block;
+
+    // Create a block allocator on top of a 10KB stack region.
+    import std.experimental.allocator.building_blocks.region : InSituRegion;
+    import std.traits : hasMember;
+    InSituRegion!(10_240, 64) r;
+    auto a = BitmappedBlock!(64, 64)(cast(ubyte[])(r.allocateAll()));
+    static assert(hasMember!(InSituRegion!(10_240, 64), "allocateAll"));
+    const b = a.allocate(100);
+    assert(b.length == 100);
+}
+
+@system unittest
+{
+    import std.experimental.allocator.building_blocks.bitmapped_block;
+
+    import std.experimental.allocator.mallocator : Mallocator;
+    import std.typecons : Flag, Yes;
+
+    enum blockSize = 64;
+    enum numBlocks = 10;
+
+    // The 'BitmappedBlock' is implicitly instantiated with Yes.multiblock
+    auto a = BitmappedBlock!(blockSize, 8, Mallocator, Yes.multiblock)(numBlocks * blockSize);
+
+    // Instantiated with Yes.multiblock, can allocate more than one block at a time
+    void[] buf = a.allocate(2 * blockSize);
+    assert(buf.length == 2 * blockSize);
+    assert(a.deallocate(buf));
+
+    // Can also allocate less than one block
+    buf = a.allocate(blockSize / 2);
+    assert(buf.length == blockSize / 2);
+
+    // Expands inside the same block
+    assert(a.expand(buf, blockSize / 2));
+    assert(buf.length == blockSize);
+
+    // If Yes.multiblock, can expand past the size of a single block
+    assert(a.expand(buf, 3 * blockSize));
+    assert(buf.length == 4 * blockSize);
+    assert(a.deallocate(buf));
+}
+
+@system unittest
+{
+    import std.experimental.allocator.building_blocks.bitmapped_block;
+
+    import std.experimental.allocator.mallocator : Mallocator;
+    import std.typecons : Flag, No;
+
+    enum blockSize = 64;
+    auto a = BitmappedBlock!(blockSize, 8, Mallocator, No.multiblock)(1024 * blockSize);
+
+    // Since instantiated with No.multiblock, can only allocate at most the block size
+    void[] buf = a.allocate(blockSize + 1);
+    assert(buf is null);
+
+    buf = a.allocate(blockSize);
+    assert(buf.length == blockSize);
+    assert(a.deallocate(buf));
+
+    // This is also fine, because it's less than the block size
+    buf = a.allocate(blockSize / 2);
+    assert(buf.length == blockSize / 2);
+
+    // Can expand the buffer until its length is at most 64
+    assert(a.expand(buf, blockSize / 2));
+    assert(buf.length == blockSize);
+
+    // Cannot expand anymore
+    assert(!a.expand(buf, 1));
+    assert(a.deallocate(buf));
+}
+
+@system unittest
+{
+    import std.experimental.allocator.building_blocks.bitmapped_block;
+
+    import std.experimental.allocator.mallocator : Mallocator;
+    import std.experimental.allocator.common : platformAlignment;
+    import std.typecons : Flag, Yes, No;
+
+    // Create 'numThreads' threads, each allocating in parallel a chunk of memory
+    static void testAlloc(Allocator)(ref Allocator a, size_t allocSize)
+    {
+        import core.thread : ThreadGroup;
+        import std.algorithm.sorting : sort;
+        import core.internal.spinlock : SpinLock;
+
+        SpinLock lock = SpinLock(SpinLock.Contention.brief);
+        enum numThreads = 10;
+        void[][numThreads] buf;
+        size_t count = 0;
+
+        // Each threads allocates 'allocSize'
+        void fun()
+        {
+            void[] b = a.allocate(allocSize);
+            assert(b.length == allocSize);
+
+            lock.lock();
+            scope(exit) lock.unlock();
+
+            buf[count] = b;
+            count++;
+        }
+
+        auto tg = new ThreadGroup;
+        foreach (i; 0 .. numThreads)
+        {
+            tg.create(&fun);
+        }
+        tg.joinAll();
+
+        // Sorting the allocations made by each thread, we expect the buffers to be
+        // adjacent inside the SharedBitmappedBlock
+        sort!((a, b) => a.ptr < b.ptr)(buf[0 .. numThreads]);
+        foreach (i; 0 .. numThreads - 1)
+        {
+            assert(buf[i].ptr + a.goodAllocSize(buf[i].length) <= buf[i + 1].ptr);
+        }
+
+        // Deallocate everything
+        foreach (i; 0 .. numThreads)
+        {
+            assert(a.deallocate(buf[i]));
+        }
+    }
+
+    enum blockSize = 64;
+    auto alloc1 = SharedBitmappedBlock!(blockSize, platformAlignment, Mallocator, Yes.multiblock)(1024 * 1024);
+    auto alloc2 = SharedBitmappedBlock!(blockSize, platformAlignment, Mallocator, No.multiblock)(1024 * 1024);
+    testAlloc(alloc1, 2 * blockSize);
+    testAlloc(alloc2, blockSize);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_bucketizer.d b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_bucketizer.d
new file mode 100644
index 0000000000000000000000000000000000000000..b1282b48abdd838154229ef7f90f6c8c553ac17e
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_bucketizer.d
@@ -0,0 +1,23 @@
+@system unittest
+{
+    import std.experimental.allocator.building_blocks.bucketizer;
+
+    import std.algorithm.comparison : max;
+    import std.experimental.allocator.building_blocks.allocator_list : AllocatorList;
+    import std.experimental.allocator.building_blocks.free_list : FreeList;
+    import std.experimental.allocator.building_blocks.region : Region;
+    import std.experimental.allocator.common : unbounded;
+    import std.experimental.allocator.mallocator : Mallocator;
+    import std.typecons : Ternary;
+    Bucketizer!(
+        FreeList!(
+            AllocatorList!(
+                (size_t n) => Region!Mallocator(max(n, 1024 * 1024))),
+            0, unbounded),
+        65, 512, 64) a;
+    auto b = a.allocate(400);
+    assert(b.length == 400);
+    assert(a.owns(b) == Ternary.yes);
+    a.deallocate(b);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_fallback_allocator.d b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_fallback_allocator.d
new file mode 100644
index 0000000000000000000000000000000000000000..6d89972266ffa25290b71291c86acf7fe5995054
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_fallback_allocator.d
@@ -0,0 +1,16 @@
+@system unittest
+{
+    import std.experimental.allocator.building_blocks.fallback_allocator;
+
+    import std.experimental.allocator.building_blocks.region : Region;
+    import std.experimental.allocator.gc_allocator : GCAllocator;
+    import std.typecons : Ternary;
+    auto a = fallbackAllocator(Region!GCAllocator(1024), GCAllocator.instance);
+    auto b1 = a.allocate(1020);
+    assert(b1.length == 1020);
+    assert(a.primary.owns(b1) == Ternary.yes);
+    auto b2 = a.allocate(10);
+    assert(b2.length == 10);
+    assert(a.primary.owns(b2) == Ternary.no);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_free_list.d b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_free_list.d
new file mode 100644
index 0000000000000000000000000000000000000000..d15e4e562f8a46dd5dbfbb027f7f3386727b1f23
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_free_list.d
@@ -0,0 +1,45 @@
+@safe unittest
+{
+    import std.experimental.allocator.building_blocks.free_list;
+
+    import std.experimental.allocator.building_blocks.allocator_list
+        : AllocatorList;
+    import std.experimental.allocator.gc_allocator : GCAllocator;
+
+    import std.experimental.allocator.common : unbounded;
+
+    alias ScalableFreeList = AllocatorList!((n) =>
+        ContiguousFreeList!(GCAllocator, 0, unbounded)(4096)
+    );
+}
+
+@safe unittest
+{
+    import std.experimental.allocator.building_blocks.free_list;
+
+    import std.experimental.allocator.common : chooseAtRuntime;
+    import std.experimental.allocator.mallocator : Mallocator;
+
+    shared SharedFreeList!(Mallocator, chooseAtRuntime, chooseAtRuntime) a;
+    a.setBounds(64, 128);
+    assert(a.max == 128);
+    assert(a.min == 64);
+}
+
+@safe unittest
+{
+    import std.experimental.allocator.building_blocks.free_list;
+
+    import std.experimental.allocator.common : chooseAtRuntime;
+    import std.experimental.allocator.mallocator : Mallocator;
+
+    shared SharedFreeList!(Mallocator, 50, 50, chooseAtRuntime) a;
+    // Set the maxSize first so setting the minSize doesn't throw
+    a.approxMaxLength = 128;
+    assert(a.approxMaxLength  == 128);
+    a.approxMaxLength = 1024;
+    assert(a.approxMaxLength  == 1024);
+    a.approxMaxLength = 1;
+    assert(a.approxMaxLength  == 1);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_kernighan_ritchie.d b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_kernighan_ritchie.d
new file mode 100644
index 0000000000000000000000000000000000000000..18e63b33577d82be6257fc925fe0eda0387e892d
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_kernighan_ritchie.d
@@ -0,0 +1,40 @@
+@system unittest
+{
+    import std.experimental.allocator.building_blocks.kernighan_ritchie;
+
+        import std.experimental.allocator.gc_allocator : GCAllocator;
+        auto alloc = KRRegion!GCAllocator(1024 * 64);
+        const b1 = alloc.allocate(2048);
+        assert(b1.length == 2048);
+        const b2 = alloc.allocateAll;
+        assert(b2.length == 1024 * 62);
+    
+}
+
+@system unittest
+{
+    import std.experimental.allocator.building_blocks.kernighan_ritchie;
+
+    import std.experimental.allocator.building_blocks.fallback_allocator
+        : fallbackAllocator;
+    import std.experimental.allocator.gc_allocator : GCAllocator;
+    import std.typecons : Ternary;
+    // KRRegion fronting a general-purpose allocator
+    align(KRRegion!().alignment) ubyte[1024 * 128] buf;
+    auto alloc = fallbackAllocator(KRRegion!()(buf), GCAllocator.instance);
+    auto b = alloc.allocate(100);
+    assert(b.length == 100);
+    assert((() pure nothrow @safe @nogc => alloc.primary.owns(b))() == Ternary.yes);
+}
+
+@system unittest
+{
+    import std.experimental.allocator.building_blocks.kernighan_ritchie;
+
+    import std.algorithm.comparison : max;
+    import std.experimental.allocator.building_blocks.allocator_list
+        : AllocatorList;
+    import std.experimental.allocator.mmap_allocator : MmapAllocator;
+    AllocatorList!(n => KRRegion!MmapAllocator(max(n * 16, 1024 * 1024))) alloc;
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_quantizer.d b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_quantizer.d
new file mode 100644
index 0000000000000000000000000000000000000000..deaae0e8c217738448f32e15bfeb7ca3d0e611bb
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_quantizer.d
@@ -0,0 +1,23 @@
+@system unittest
+{
+    import std.experimental.allocator.building_blocks.quantizer;
+
+    import std.experimental.allocator.building_blocks.free_tree : FreeTree;
+    import std.experimental.allocator.gc_allocator : GCAllocator;
+
+    size_t roundUpToMultipleOf(size_t s, uint base)
+    {
+        auto rem = s % base;
+        return rem ? s + base - rem : s;
+    }
+
+    // Quantize small allocations to a multiple of cache line, large ones to a
+    // multiple of page size
+    alias MyAlloc = Quantizer!(
+        FreeTree!GCAllocator,
+        n => roundUpToMultipleOf(n, n <= 16_384 ? 64 : 4096));
+    MyAlloc alloc;
+    const buf = alloc.allocate(256);
+    assert(buf.ptr);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_region.d b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_region.d
new file mode 100644
index 0000000000000000000000000000000000000000..8b4e2be9bc3a92fa90f75846bf74ffc5adde78f0
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_region.d
@@ -0,0 +1,90 @@
+@system nothrow unittest
+{
+    import std.experimental.allocator.building_blocks.region;
+
+    import std.algorithm.comparison : max;
+    import std.experimental.allocator.building_blocks.allocator_list
+        : AllocatorList;
+    import std.experimental.allocator.mallocator : Mallocator;
+    import std.typecons : Ternary;
+    // Create a scalable list of regions. Each gets at least 1MB at a time by
+    // using malloc.
+    auto batchAllocator = AllocatorList!(
+        (size_t n) => Region!Mallocator(max(n, 1024 * 1024))
+    )();
+    assert(batchAllocator.empty ==  Ternary.yes);
+    auto b = batchAllocator.allocate(101);
+    assert(b.length == 101);
+    assert(batchAllocator.empty ==  Ternary.no);
+    // This will cause a second allocation
+    b = batchAllocator.allocate(2 * 1024 * 1024);
+    assert(b.length == 2 * 1024 * 1024);
+    // Destructor will free the memory
+}
+
+@system nothrow @nogc unittest
+{
+    import std.experimental.allocator.building_blocks.region;
+
+    import std.typecons : Ternary;
+
+    ubyte[1024] store;
+    auto myRegion = BorrowedRegion!(1)(store[]);
+
+    assert(myRegion.empty == Ternary.yes);
+    assert(myRegion.available == store.length);
+
+    void[] b = myRegion.allocate(101);
+
+    assert(b.length == 101);
+    assert(myRegion.empty == Ternary.no);
+    assert(myRegion.owns(b) == Ternary.yes);
+    assert(myRegion.available == store.length - b.length);
+
+    void[] b2 = myRegion.allocate(256);
+
+    // Can only free the most recent allocation
+    assert(myRegion.deallocate(b) == false);
+    assert(myRegion.deallocate(b2) == true);
+
+    myRegion.deallocateAll();
+
+    assert(myRegion.empty == Ternary.yes);
+}
+
+@system unittest
+{
+    import std.experimental.allocator.building_blocks.region;
+
+    // 128KB region, allocated to x86's cache line
+    InSituRegion!(128 * 1024, 16) r1;
+    auto a1 = r1.allocate(101);
+    assert(a1.length == 101);
+
+    // 128KB region, with fallback to the garbage collector.
+    import std.experimental.allocator.building_blocks.fallback_allocator
+        : FallbackAllocator;
+    import std.experimental.allocator.building_blocks.free_list
+        : FreeList;
+    import std.experimental.allocator.building_blocks.bitmapped_block
+        : BitmappedBlock;
+    import std.experimental.allocator.gc_allocator : GCAllocator;
+    FallbackAllocator!(InSituRegion!(128 * 1024), GCAllocator) r2;
+    const a2 = r2.allocate(102);
+    assert(a2.length == 102);
+
+    // Reap with GC fallback.
+    InSituRegion!(128 * 1024, 8) tmp3;
+    FallbackAllocator!(BitmappedBlock!(64, 8), GCAllocator) r3;
+    r3.primary = BitmappedBlock!(64, 8)(cast(ubyte[]) (tmp3.allocateAll()));
+    const a3 = r3.allocate(103);
+    assert(a3.length == 103);
+
+    // Reap/GC with a freelist for small objects up to 16 bytes.
+    InSituRegion!(128 * 1024, 64) tmp4;
+    FreeList!(FallbackAllocator!(BitmappedBlock!(64, 64), GCAllocator), 0, 16) r4;
+    r4.parent.primary = BitmappedBlock!(64, 64)(cast(ubyte[]) (tmp4.allocateAll()));
+    const a4 = r4.allocate(104);
+    assert(a4.length == 104);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_scoped_allocator.d b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_scoped_allocator.d
new file mode 100644
index 0000000000000000000000000000000000000000..923f621ca62fde05c6837d44aa76d0859761f8d7
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_scoped_allocator.d
@@ -0,0 +1,13 @@
+@system unittest
+{
+    import std.experimental.allocator.building_blocks.scoped_allocator;
+
+    import std.experimental.allocator.mallocator : Mallocator;
+    import std.typecons : Ternary;
+    ScopedAllocator!Mallocator alloc;
+    assert(alloc.empty == Ternary.yes);
+    const b = alloc.allocate(10);
+    assert(b.length == 10);
+    assert(alloc.empty == Ternary.no);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_segregator.d b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_segregator.d
new file mode 100644
index 0000000000000000000000000000000000000000..49ee6344480aaabe97efa9fd5ad91af6d3c6f97e
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_segregator.d
@@ -0,0 +1,43 @@
+@system unittest
+{
+    import std.experimental.allocator.building_blocks.segregator;
+
+    import std.experimental.allocator.building_blocks.free_list : FreeList;
+    import std.experimental.allocator.gc_allocator : GCAllocator;
+    import std.experimental.allocator.mallocator : Mallocator;
+    alias A =
+        Segregator!(
+            1024 * 4,
+            Segregator!(
+                128, FreeList!(Mallocator, 0, 128),
+                GCAllocator),
+            Segregator!(
+                1024 * 1024, Mallocator,
+                GCAllocator)
+            );
+    A a;
+    auto b = a.allocate(200);
+    assert(b.length == 200);
+    a.deallocate(b);
+}
+
+@system unittest
+{
+    import std.experimental.allocator.building_blocks.segregator;
+
+    import std.experimental.allocator.building_blocks.free_list : FreeList;
+    import std.experimental.allocator.gc_allocator : GCAllocator;
+    import std.experimental.allocator.mallocator : Mallocator;
+    alias A =
+        Segregator!(
+            128, FreeList!(Mallocator, 0, 128),
+            1024 * 4, GCAllocator,
+            1024 * 1024, Mallocator,
+            GCAllocator
+        );
+    A a;
+    auto b = a.allocate(201);
+    assert(b.length == 201);
+    a.deallocate(b);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_stats_collector.d b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_stats_collector.d
new file mode 100644
index 0000000000000000000000000000000000000000..1fbed435e3f7a2217fba084ce6de5f11f67fe49d
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_building_blocks_stats_collector.d
@@ -0,0 +1,33 @@
+@safe unittest
+{
+    import std.experimental.allocator.building_blocks.stats_collector;
+
+    import std.experimental.allocator.gc_allocator : GCAllocator;
+    import std.experimental.allocator.building_blocks.free_list : FreeList;
+    alias Allocator = StatsCollector!(GCAllocator, Options.bytesUsed);
+}
+
+@system unittest
+{
+    import std.experimental.allocator.building_blocks.stats_collector;
+
+    import std.experimental.allocator.building_blocks.free_list : FreeList;
+    import std.experimental.allocator.gc_allocator : GCAllocator;
+    alias Allocator = StatsCollector!(GCAllocator, Options.all, Options.all);
+
+    Allocator alloc;
+    auto b = alloc.allocate(10);
+    alloc.reallocate(b, 20);
+    alloc.deallocate(b);
+
+    import std.file : deleteme, remove;
+    import std.range : walkLength;
+    import std.stdio : File;
+
+    auto f = deleteme ~ "-dlang.std.experimental.allocator.stats_collector.txt";
+    scope(exit) remove(f);
+    Allocator.reportPerCallStatistics(File(f, "w"));
+    alloc.reportStatistics(File(f, "a"));
+    assert(File(f).byLine.walkLength == 24);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_common.d b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_common.d
new file mode 100644
index 0000000000000000000000000000000000000000..4751c010552a1b175aa8c8ffad78bee62ef808d8
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_common.d
@@ -0,0 +1,33 @@
+@safe @nogc nothrow pure unittest
+{
+    import std.experimental.allocator.common;
+
+    import std.experimental.allocator.building_blocks.null_allocator : NullAllocator;
+    import std.experimental.allocator.mallocator : Mallocator;
+    import std.experimental.allocator.gc_allocator : GCAllocator;
+    import std.experimental.allocator.mmap_allocator : MmapAllocator;
+    static assert(isAllocator!NullAllocator);
+    static assert(isAllocator!Mallocator);
+    static assert(isAllocator!GCAllocator);
+    static assert(isAllocator!MmapAllocator);
+    static assert(!isAllocator!int);
+}
+
+@safe @nogc nothrow pure unittest
+{
+    import std.experimental.allocator.common;
+
+    import std.experimental.allocator.building_blocks.null_allocator : NullAllocator;
+    import std.experimental.allocator.mallocator : Mallocator;
+    import std.experimental.allocator.gc_allocator : GCAllocator;
+    import std.experimental.allocator.mmap_allocator : MmapAllocator;
+    struct S
+    {
+        mixin AllocatorState!NullAllocator n;
+        mixin AllocatorState!GCAllocator g;
+        mixin AllocatorState!Mallocator m;
+        mixin AllocatorState!MmapAllocator p;
+    }
+    static assert(S.sizeof == 1);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_gc_allocator.d b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_gc_allocator.d
new file mode 100644
index 0000000000000000000000000000000000000000..a6615b77aa42a789159bce30a6314fd6b5c1a789
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_gc_allocator.d
@@ -0,0 +1,10 @@
+pure @system unittest
+{
+    import std.experimental.allocator.gc_allocator;
+
+    auto buffer = GCAllocator.instance.allocate(1024 * 1024 * 4);
+    // deallocate upon scope's end (alternatively: leave it to collection)
+    scope(exit) GCAllocator.instance.deallocate(buffer);
+    //...
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_mallocator.d b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_mallocator.d
new file mode 100644
index 0000000000000000000000000000000000000000..76fb3ec253950e3eec91e2f998bca8fa7562479f
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_mallocator.d
@@ -0,0 +1,19 @@
+@nogc @system nothrow unittest
+{
+    import std.experimental.allocator.mallocator;
+
+    auto buffer = Mallocator.instance.allocate(1024 * 1024 * 4);
+    scope(exit) Mallocator.instance.deallocate(buffer);
+    //...
+}
+
+pure @nogc @system nothrow unittest
+{
+    import std.experimental.allocator.mallocator;
+
+    auto buffer = AlignedMallocator.instance.alignedAllocate(1024 * 1024 * 4,
+        128);
+    scope(exit) AlignedMallocator.instance.deallocate(buffer);
+    //...
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_package.d b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_package.d
new file mode 100644
index 0000000000000000000000000000000000000000..e19efc54c0d0bd2d8fc0c17e884ca0b5f4310ac4
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_package.d
@@ -0,0 +1,263 @@
+@system unittest
+{
+    import std.experimental.allocator;
+
+    // Install a new allocator that is faster for 128-byte allocations.
+    import std.experimental.allocator.building_blocks.free_list : FreeList;
+    import std.experimental.allocator.gc_allocator : GCAllocator;
+    auto oldAllocator = theAllocator;
+    scope(exit) theAllocator = oldAllocator;
+    theAllocator = allocatorObject(FreeList!(GCAllocator, 128)());
+    // Use the now changed allocator to allocate an array
+    const ubyte[] arr = theAllocator.makeArray!ubyte(128);
+    assert(arr.ptr);
+    //...
+}
+
+@system unittest
+{
+    import std.experimental.allocator;
+
+    // Dynamically allocate one integer
+    const int* p1 = theAllocator.make!int;
+    // It's implicitly initialized with its .init value
+    assert(*p1 == 0);
+    // Dynamically allocate one double, initialize to 42.5
+    const double* p2 = theAllocator.make!double(42.5);
+    assert(*p2 == 42.5);
+
+    // Dynamically allocate a struct
+    static struct Point
+    {
+        int x, y, z;
+    }
+    // Use the generated constructor taking field values in order
+    const Point* p = theAllocator.make!Point(1, 2);
+    assert(p.x == 1 && p.y == 2 && p.z == 0);
+
+    // Dynamically allocate a class object
+    static class Customer
+    {
+        uint id = uint.max;
+        this() {}
+        this(uint id) { this.id = id; }
+        // ...
+    }
+    Customer cust = theAllocator.make!Customer;
+    assert(cust.id == uint.max); // default initialized
+    cust = theAllocator.make!Customer(42);
+    assert(cust.id == 42);
+
+    // explicit passing of outer pointer
+    static class Outer
+    {
+        int x = 3;
+        class Inner
+        {
+            auto getX() { return x; }
+        }
+    }
+    auto outer = theAllocator.make!Outer();
+    auto inner = theAllocator.make!(Outer.Inner)(outer);
+    assert(outer.x == inner.getX);
+}
+
+@system unittest
+{
+    import std.experimental.allocator;
+
+    import std.algorithm.comparison : equal;
+    static void test(T)()
+    {
+        T[] a = theAllocator.makeArray!T(2);
+        assert(a.equal([0, 0]));
+        a = theAllocator.makeArray!T(3, 42);
+        assert(a.equal([42, 42, 42]));
+        import std.range : only;
+        a = theAllocator.makeArray!T(only(42, 43, 44));
+        assert(a.equal([42, 43, 44]));
+    }
+    test!int();
+    test!(shared int)();
+    test!(const int)();
+    test!(immutable int)();
+}
+
+@system unittest
+{
+    import std.experimental.allocator;
+
+    auto arr = theAllocator.makeArray!int([1, 2, 3]);
+    assert(theAllocator.expandArray(arr, 2));
+    assert(arr == [1, 2, 3, 0, 0]);
+    import std.range : only;
+    assert(theAllocator.expandArray(arr, only(4, 5)));
+    assert(arr == [1, 2, 3, 0, 0, 4, 5]);
+}
+
+@system unittest
+{
+    import std.experimental.allocator;
+
+    int[] a = theAllocator.makeArray!int(100, 42);
+    assert(a.length == 100);
+    assert(theAllocator.shrinkArray(a, 98));
+    assert(a.length == 2);
+    assert(a == [42, 42]);
+}
+
+@system unittest
+{
+    import std.experimental.allocator;
+
+    import std.experimental.allocator.mallocator : Mallocator;
+
+    auto mArray = Mallocator.instance.makeMultidimensionalArray!int(2, 3, 6);
+
+    // deallocate when exiting scope
+    scope(exit)
+    {
+        Mallocator.instance.disposeMultidimensionalArray(mArray);
+    }
+
+    assert(mArray.length == 2);
+    foreach (lvl2Array; mArray)
+    {
+        assert(lvl2Array.length == 3);
+        foreach (lvl3Array; lvl2Array)
+            assert(lvl3Array.length == 6);
+    }
+}
+
+@system unittest
+{
+    import std.experimental.allocator;
+
+    struct TestAllocator
+    {
+        import std.experimental.allocator.common : platformAlignment;
+        import std.experimental.allocator.mallocator : Mallocator;
+
+        alias allocator = Mallocator.instance;
+
+        private static struct ByteRange
+        {
+            void* ptr;
+            size_t length;
+        }
+
+        private ByteRange[] _allocations;
+
+        enum uint alignment = platformAlignment;
+
+        void[] allocate(size_t numBytes)
+        {
+             auto ret = allocator.allocate(numBytes);
+             _allocations ~= ByteRange(ret.ptr, ret.length);
+             return ret;
+        }
+
+        bool deallocate(void[] bytes)
+        {
+            import std.algorithm.mutation : remove;
+            import std.algorithm.searching : canFind;
+
+            bool pred(ByteRange other)
+            { return other.ptr == bytes.ptr && other.length == bytes.length; }
+
+            assert(_allocations.canFind!pred);
+
+             _allocations = _allocations.remove!pred;
+             return allocator.deallocate(bytes);
+        }
+
+        ~this()
+        {
+            assert(!_allocations.length);
+        }
+    }
+
+    TestAllocator allocator;
+
+    auto mArray = allocator.makeMultidimensionalArray!int(2, 3, 5, 6, 7, 2);
+
+    allocator.disposeMultidimensionalArray(mArray);
+}
+
+@system unittest
+{
+    import std.experimental.allocator;
+
+    import std.experimental.allocator.mallocator : Mallocator;
+
+    RCIAllocator a = allocatorObject(Mallocator.instance);
+    auto b = a.allocate(100);
+    assert(b.length == 100);
+    assert(a.deallocate(b));
+
+    // The in-situ region must be used by pointer
+    import std.experimental.allocator.building_blocks.region : InSituRegion;
+    auto r = InSituRegion!1024();
+    a = allocatorObject(&r);
+    b = a.allocate(200);
+    assert(b.length == 200);
+    // In-situ regions can deallocate the last allocation
+    assert(a.deallocate(b));
+}
+
+@system unittest
+{
+    import std.experimental.allocator;
+
+    import std.experimental.allocator.building_blocks.free_list : FreeList;
+    import std.experimental.allocator.gc_allocator : GCAllocator;
+    import std.experimental.allocator.mallocator : Mallocator;
+
+    static assert(!is(ThreadLocal!Mallocator));
+    static assert(!is(ThreadLocal!GCAllocator));
+    alias Allocator = ThreadLocal!(FreeList!(GCAllocator, 0, 8));
+    auto b = Allocator.instance.allocate(5);
+    static assert(__traits(hasMember, Allocator, "allocate"));
+}
+
+@system unittest
+{
+    import std.experimental.allocator;
+
+    import std.experimental.allocator.building_blocks.allocator_list : AllocatorList;
+    import std.experimental.allocator.building_blocks.bitmapped_block : BitmappedBlock;
+    import std.experimental.allocator.building_blocks.segregator : Segregator;
+    import std.experimental.allocator.building_blocks.bucketizer : Bucketizer;
+    import std.experimental.allocator.building_blocks.free_list : FreeList;
+    import std.experimental.allocator.gc_allocator : GCAllocator;
+
+    /// Define an allocator bound to the built-in GC.
+    auto alloc = allocatorObject(GCAllocator.instance);
+    auto b = alloc.allocate(42);
+    assert(b.length == 42);
+    assert(alloc.deallocate(b));
+
+    import std.algorithm.comparison : max;
+    // Define an elaborate allocator and bind it to the class API.
+    alias FList = FreeList!(GCAllocator, 0, unbounded);
+    alias A = ThreadLocal!(
+        Segregator!(
+            8, FreeList!(GCAllocator, 0, 8),
+            128, Bucketizer!(FList, 1, 128, 16),
+            256, Bucketizer!(FList, 129, 256, 32),
+            512, Bucketizer!(FList, 257, 512, 64),
+            1024, Bucketizer!(FList, 513, 1024, 128),
+            2048, Bucketizer!(FList, 1025, 2048, 256),
+            3584, Bucketizer!(FList, 2049, 3584, 512),
+            4072 * 1024, AllocatorList!(
+                (n) => BitmappedBlock!(4096)(cast(ubyte[]) GCAllocator.instance.allocate(
+                    max(n, 4072 * 1024)))),
+            GCAllocator
+        )
+    );
+
+    auto alloc2 = allocatorObject(A.instance);
+    b = alloc2.allocate(101);
+    assert(alloc2.deallocate(b));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_showcase.d b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_showcase.d
new file mode 100644
index 0000000000000000000000000000000000000000..3cbb19cda3b2601a2e46c2ebc89d00e4a6df653b
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_showcase.d
@@ -0,0 +1,22 @@
+@system unittest
+{
+    import std.experimental.allocator.showcase;
+
+    StackFront!4096 a;
+    auto b = a.allocate(4000);
+    assert(b.length == 4000);
+    auto c = a.allocate(4000);
+    assert(c.length == 4000);
+    a.deallocate(b);
+    a.deallocate(c);
+}
+
+@system unittest
+{
+    import std.experimental.allocator.showcase;
+
+    auto alloc = mmapRegionList(1024 * 1024);
+    const b = alloc.allocate(100);
+    assert(b.length == 100);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_typed.d b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_typed.d
new file mode 100644
index 0000000000000000000000000000000000000000..d9f0880144ad8d928e08fafc274401208b8bf927
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_experimental_allocator_typed.d
@@ -0,0 +1,34 @@
+@system unittest
+{
+    import std.experimental.allocator.typed;
+
+    import std.experimental.allocator.gc_allocator : GCAllocator;
+    import std.experimental.allocator.mallocator : Mallocator;
+    import std.experimental.allocator.mmap_allocator : MmapAllocator;
+    alias MyAllocator = TypedAllocator!(GCAllocator,
+        AllocFlag.fixedSize | AllocFlag.threadLocal, Mallocator,
+        AllocFlag.fixedSize | AllocFlag.threadLocal
+                | AllocFlag.hasNoIndirections,
+            MmapAllocator,
+    );
+
+    MyAllocator a;
+    auto b = &a.allocatorFor!0();
+    static assert(is(typeof(*b) == shared const(GCAllocator)));
+    enum f1 = AllocFlag.fixedSize | AllocFlag.threadLocal;
+    auto c = &a.allocatorFor!f1();
+    static assert(is(typeof(*c) == Mallocator));
+    enum f2 = AllocFlag.fixedSize | AllocFlag.threadLocal;
+    static assert(is(typeof(a.allocatorFor!f2()) == Mallocator));
+    // Partial match
+    enum f3 = AllocFlag.threadLocal;
+    static assert(is(typeof(a.allocatorFor!f3()) == Mallocator));
+
+    int* p = a.make!int;
+    scope(exit) a.dispose(p);
+    int[] arr = a.makeArray!int(42);
+    scope(exit) a.dispose(arr);
+    assert(a.expandArray(arr, 3));
+    assert(a.shrinkArray(arr, 4));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_file.d b/libphobos/testsuite/libphobos.phobos/std_file.d
new file mode 100644
index 0000000000000000000000000000000000000000..49626d860dae44b53a9aec7c101a74d5096379cb
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_file.d
@@ -0,0 +1,756 @@
+@safe unittest
+{
+    import std.file;
+
+    import std.exception : assertThrown;
+
+    assertThrown!FileException("non.existing.file.".readText);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.utf : byChar;
+    scope(exit)
+    {
+        assert(exists(deleteme));
+        remove(deleteme);
+    }
+
+    std.file.write(deleteme, "1234"); // deleteme is the name of a temporary file
+    assert(read(deleteme, 2) == "12");
+    assert(read(deleteme.byChar) == "1234");
+    assert((cast(const(ubyte)[])read(deleteme)).length == 4);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    write(deleteme, "abc"); // deleteme is the name of a temporary file
+    scope(exit) remove(deleteme);
+    string content = readText(deleteme);
+    assert(content == "abc");
+}
+
+@safe unittest
+{
+    import std.file;
+
+   scope(exit)
+   {
+       assert(exists(deleteme));
+       remove(deleteme);
+   }
+
+   int[] a = [ 0, 1, 1, 2, 3, 5, 8 ];
+   write(deleteme, a); // deleteme is the name of a temporary file
+   const bytes = read(deleteme);
+   const fileInts = () @trusted { return cast(int[]) bytes; }();
+   assert(fileInts == a);
+}
+
+@safe unittest
+{
+    import std.file;
+
+   scope(exit)
+   {
+       assert(exists(deleteme));
+       remove(deleteme);
+   }
+
+   int[] a = [ 0, 1, 1, 2, 3, 5, 8 ];
+   write(deleteme, a); // deleteme is the name of a temporary file
+   int[] b = [ 13, 21 ];
+   append(deleteme, b);
+   const bytes = read(deleteme);
+   const fileInts = () @trusted { return cast(int[]) bytes; }();
+   assert(fileInts == a ~ b);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    auto t1 = deleteme, t2 = deleteme~"2";
+    scope(exit) foreach (t; [t1, t2]) if (t.exists) t.remove();
+
+    t1.write("1");
+    t1.rename(t2);
+    assert(t2.readText == "1");
+
+    t1.write("2");
+    t1.rename(t2);
+    assert(t2.readText == "2");
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.exception : assertThrown;
+
+    deleteme.write("Hello");
+    assert(deleteme.readText == "Hello");
+
+    deleteme.remove;
+    assertThrown!FileException(deleteme.readText);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    scope(exit) deleteme.remove;
+
+    // create a file of size 1
+    write(deleteme, "a");
+    assert(getSize(deleteme) == 1);
+
+    // create a file of size 3
+    write(deleteme, "abc");
+    assert(getSize(deleteme) == 3);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.datetime : abs, SysTime;
+
+    scope(exit) deleteme.remove;
+    write(deleteme, "a");
+
+    SysTime accessTime, modificationTime;
+
+    getTimes(deleteme, accessTime, modificationTime);
+
+    import std.datetime : Clock, seconds;
+    auto currTime = Clock.currTime();
+    enum leeway = 5.seconds;
+
+    auto diffAccess = accessTime - currTime;
+    auto diffModification = modificationTime - currTime;
+    assert(abs(diffAccess) <= leeway);
+    assert(abs(diffModification) <= leeway);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.datetime : DateTime, hnsecs, SysTime;
+
+    scope(exit) deleteme.remove;
+    write(deleteme, "a");
+
+    SysTime accessTime = SysTime(DateTime(2010, 10, 4, 0, 0, 30));
+    SysTime modificationTime = SysTime(DateTime(2018, 10, 4, 0, 0, 30));
+    setTimes(deleteme, accessTime, modificationTime);
+
+    SysTime accessTimeResolved, modificationTimeResolved;
+    getTimes(deleteme, accessTimeResolved, modificationTimeResolved);
+
+    assert(accessTime == accessTimeResolved);
+    assert(modificationTime == modificationTimeResolved);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.datetime : abs, DateTime, hnsecs, SysTime;
+    scope(exit) deleteme.remove;
+
+    import std.datetime : Clock, seconds;
+    auto currTime = Clock.currTime();
+    enum leeway = 5.seconds;
+    deleteme.write("bb");
+    assert(abs(deleteme.timeLastModified - currTime) <= leeway);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.datetime : SysTime;
+
+    assert("file.does.not.exist".timeLastModified(SysTime.min) == SysTime.min);
+
+    auto source = deleteme ~ "source";
+    auto target = deleteme ~ "target";
+    scope(exit) source.remove, target.remove;
+
+    source.write(".");
+    assert(target.timeLastModified(SysTime.min) < source.timeLastModified);
+    target.write(".");
+    assert(target.timeLastModified(SysTime.min) >= source.timeLastModified);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    auto f = deleteme ~ "does.not.exist";
+    assert(!f.exists);
+
+    f.write("hello");
+    assert(f.exists);
+
+    f.remove;
+    assert(!f.exists);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    assert(".".exists);
+    assert(!"this file does not exist".exists);
+    deleteme.write("a\n");
+    scope(exit) deleteme.remove;
+    assert(deleteme.exists);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.exception : assertThrown;
+
+    auto f = deleteme ~ "file";
+    scope(exit) f.remove;
+
+    assert(!f.exists);
+    assertThrown!FileException(f.getAttributes);
+
+    f.write(".");
+    auto attributes = f.getAttributes;
+    assert(!attributes.attrIsDir);
+    assert(attributes.attrIsFile);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.exception : assertThrown;
+
+    auto dir = deleteme ~ "dir";
+    scope(exit) dir.rmdir;
+
+    assert(!dir.exists);
+    assertThrown!FileException(dir.getAttributes);
+
+    dir.mkdir;
+    auto attributes = dir.getAttributes;
+    assert(attributes.attrIsDir);
+    assert(!attributes.attrIsFile);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.exception : assertThrown;
+
+    auto source = deleteme ~ "source";
+    auto target = deleteme ~ "target";
+
+    assert(!source.exists);
+    assertThrown!FileException(source.getLinkAttributes);
+
+    // symlinking isn't available on Windows
+    version (Posix)
+    {
+        scope(exit) source.remove, target.remove;
+
+        target.write("target");
+        target.symlink(source);
+        assert(source.readText == "target");
+        assert(source.isSymlink);
+        assert(source.getLinkAttributes.attrIsSymlink);
+    }
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.exception : assertThrown;
+
+    auto f = deleteme ~ "file";
+    scope(exit) f.remove;
+
+    assert(!f.exists);
+    assertThrown!FileException(f.getLinkAttributes);
+
+    f.write(".");
+    auto attributes = f.getLinkAttributes;
+    assert(!attributes.attrIsDir);
+    assert(attributes.attrIsFile);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.exception : assertThrown;
+
+    auto dir = deleteme ~ "dir";
+    scope(exit) dir.rmdir;
+
+    assert(!dir.exists);
+    assertThrown!FileException(dir.getLinkAttributes);
+
+    dir.mkdir;
+    auto attributes = dir.getLinkAttributes;
+    assert(attributes.attrIsDir);
+    assert(!attributes.attrIsFile);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.exception : assertThrown;
+    import std.conv : octal;
+
+    auto f = deleteme ~ "file";
+    version (Posix)
+    {
+        scope(exit) f.remove;
+
+        assert(!f.exists);
+        assertThrown!FileException(f.setAttributes(octal!777));
+
+        f.write(".");
+        auto attributes = f.getAttributes;
+        assert(!attributes.attrIsDir);
+        assert(attributes.attrIsFile);
+
+        f.setAttributes(octal!777);
+        attributes = f.getAttributes;
+
+        assert((attributes & 1023) == octal!777);
+    }
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.exception : assertThrown;
+    import std.conv : octal;
+
+    auto dir = deleteme ~ "dir";
+    version (Posix)
+    {
+        scope(exit) dir.rmdir;
+
+        assert(!dir.exists);
+        assertThrown!FileException(dir.setAttributes(octal!777));
+
+        dir.mkdir;
+        auto attributes = dir.getAttributes;
+        assert(attributes.attrIsDir);
+        assert(!attributes.attrIsFile);
+
+        dir.setAttributes(octal!777);
+        attributes = dir.getAttributes;
+
+        assert((attributes & 1023) == octal!777);
+    }
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.exception : assertThrown;
+
+    auto dir = deleteme ~ "dir";
+    auto f = deleteme ~ "f";
+    scope(exit) dir.rmdir, f.remove;
+
+    assert(!dir.exists);
+    assertThrown!FileException(dir.isDir);
+
+    dir.mkdir;
+    assert(dir.isDir);
+
+    f.write(".");
+    assert(!f.isDir);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.exception : assertThrown;
+
+    auto dir = deleteme ~ "dir";
+    auto f = deleteme ~ "f";
+    scope(exit) dir.rmdir, f.remove;
+
+    assert(!dir.exists);
+    assertThrown!FileException(dir.getAttributes.attrIsDir);
+
+    dir.mkdir;
+    assert(dir.isDir);
+    assert(dir.getAttributes.attrIsDir);
+
+    f.write(".");
+    assert(!f.isDir);
+    assert(!f.getAttributes.attrIsDir);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.exception : assertThrown;
+
+    auto dir = deleteme ~ "dir";
+    auto f = deleteme ~ "f";
+    scope(exit) dir.rmdir, f.remove;
+
+    dir.mkdir;
+    assert(!dir.isFile);
+
+    assert(!f.exists);
+    assertThrown!FileException(f.isFile);
+
+    f.write(".");
+    assert(f.isFile);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.exception : assertThrown;
+
+    auto dir = deleteme ~ "dir";
+    auto f = deleteme ~ "f";
+    scope(exit) dir.rmdir, f.remove;
+
+    dir.mkdir;
+    assert(!dir.isFile);
+    assert(!dir.getAttributes.attrIsFile);
+
+    assert(!f.exists);
+    assertThrown!FileException(f.getAttributes.attrIsFile);
+
+    f.write(".");
+    assert(f.isFile);
+    assert(f.getAttributes.attrIsFile);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.exception : assertThrown;
+
+    auto source = deleteme ~ "source";
+    auto target = deleteme ~ "target";
+
+    assert(!source.exists);
+    assertThrown!FileException(source.isSymlink);
+
+    // symlinking isn't available on Windows
+    version (Posix)
+    {
+        scope(exit) source.remove, target.remove;
+
+        target.write("target");
+        target.symlink(source);
+        assert(source.readText == "target");
+        assert(source.isSymlink);
+        assert(source.getLinkAttributes.attrIsSymlink);
+    }
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.exception : assertThrown;
+
+    auto source = deleteme ~ "source";
+    auto target = deleteme ~ "target";
+
+    assert(!source.exists);
+    assertThrown!FileException(source.getLinkAttributes.attrIsSymlink);
+
+    // symlinking isn't available on Windows
+    version (Posix)
+    {
+        scope(exit) source.remove, target.remove;
+
+        target.write("target");
+        target.symlink(source);
+        assert(source.readText == "target");
+        assert(source.isSymlink);
+        assert(source.getLinkAttributes.attrIsSymlink);
+    }
+}
+
+@system unittest
+{
+    import std.file;
+
+    import std.algorithm.comparison : equal;
+    import std.algorithm.sorting : sort;
+    import std.array : array;
+    import std.path : buildPath;
+
+    auto cwd = getcwd;
+    auto dir = deleteme ~ "dir";
+    dir.mkdir;
+    scope(exit) cwd.chdir, dir.rmdirRecurse;
+
+    dir.buildPath("a").write(".");
+    dir.chdir; // step into dir
+    "b".write(".");
+    assert(dirEntries(".", SpanMode.shallow).array.sort.equal(
+        [".".buildPath("a"), ".".buildPath("b")]
+    ));
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.file : mkdir;
+
+    auto dir = deleteme ~ "dir";
+    scope(exit) dir.rmdir;
+
+    dir.mkdir;
+    assert(dir.exists);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.exception : assertThrown;
+    assertThrown("a/b/c/d/e".mkdir);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.path : buildPath;
+
+    auto dir = deleteme ~ "dir";
+    scope(exit) dir.rmdirRecurse;
+
+    dir.mkdir;
+    assert(dir.exists);
+    dir.mkdirRecurse; // does nothing
+
+    // creates all parent directories as needed
+    auto nested = dir.buildPath("a", "b", "c");
+    nested.mkdirRecurse;
+    assert(nested.exists);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.exception : assertThrown;
+
+    scope(exit) deleteme.remove;
+    deleteme.write("a");
+
+    // cannot make directory as it's already a file
+    assertThrown!FileException(deleteme.mkdirRecurse);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    auto dir = deleteme ~ "dir";
+
+    dir.mkdir;
+    assert(dir.exists);
+    dir.rmdir;
+    assert(!dir.exists);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    auto s = getcwd();
+    assert(s.length);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.path : isAbsolute;
+    auto path = thisExePath();
+
+    assert(path.exists);
+    assert(path.isAbsolute);
+    assert(path.isFile);
+}
+
+@safe unittest
+{
+    import std.file;
+
+    auto source = deleteme ~ "source";
+    auto target = deleteme ~ "target";
+    auto targetNonExistent = deleteme ~ "target2";
+
+    scope(exit) source.remove, target.remove, targetNonExistent.remove;
+
+    source.write("source");
+    target.write("target");
+
+    assert(target.readText == "target");
+
+    source.copy(target);
+    assert(target.readText == "source");
+
+    source.copy(targetNonExistent);
+    assert(targetNonExistent.readText == "source");
+}
+
+@system unittest
+{
+    import std.file;
+
+    import std.path : buildPath;
+
+    auto dir = deleteme.buildPath("a", "b", "c");
+
+    dir.mkdirRecurse;
+    assert(dir.exists);
+
+    deleteme.rmdirRecurse;
+    assert(!dir.exists);
+    assert(!deleteme.exists);
+}
+
+@system unittest
+{
+    import std.file;
+
+    import std.algorithm.comparison : equal;
+    import std.algorithm.iteration : map;
+    import std.algorithm.sorting : sort;
+    import std.array : array;
+    import std.path : buildPath, relativePath;
+
+    auto root = deleteme ~ "root";
+    scope(exit) root.rmdirRecurse;
+    root.mkdir;
+
+    root.buildPath("animals").mkdir;
+    root.buildPath("animals", "cat").mkdir;
+
+    alias removeRoot = (return scope e) => e.relativePath(root);
+
+    assert(root.dirEntries(SpanMode.depth).map!removeRoot.equal(
+        [buildPath("animals", "cat"), "animals"]));
+
+    assert(root.dirEntries(SpanMode.breadth).map!removeRoot.equal(
+        ["animals", buildPath("animals", "cat")]));
+
+    root.buildPath("plants").mkdir;
+
+    assert(root.dirEntries(SpanMode.shallow).array.sort.map!removeRoot.equal(
+        ["animals", "plants"]));
+}
+
+@safe unittest
+{
+    import std.file;
+
+    string[] listdir(string pathname)
+    {
+        import std.algorithm.iteration : map, filter;
+        import std.array : array;
+        import std.path : baseName;
+
+        return dirEntries(pathname, SpanMode.shallow)
+            .filter!(a => a.isFile)
+            .map!((return a) => baseName(a.name))
+            .array;
+    }
+
+    // Can be safe only with -preview=dip1000
+    @safe void main(string[] args)
+    {
+        import std.stdio : writefln;
+
+        string[] files = listdir(args[1]);
+        writefln("%s", files);
+    }
+}
+
+@system unittest
+{
+    import std.file;
+
+    import std.typecons : tuple;
+
+    scope(exit)
+    {
+        assert(exists(deleteme));
+        remove(deleteme);
+    }
+
+    write(deleteme, "12 12.25\n345 1.125"); // deleteme is the name of a temporary file
+
+    // Load file; each line is an int followed by comma, whitespace and a
+    // double.
+    auto a = slurp!(int, double)(deleteme, "%s %s");
+    assert(a.length == 2);
+    assert(a[0] == tuple(12, 12.25));
+    assert(a[1] == tuple(345, 1.125));
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.ascii : letters;
+    import std.conv : to;
+    import std.path : buildPath;
+    import std.random : randomSample;
+    import std.utf : byCodeUnit;
+
+    // random id with 20 letters
+    auto id = letters.byCodeUnit.randomSample(20).to!string;
+    auto myFile = tempDir.buildPath(id ~ "my_tmp_file");
+    scope(exit) myFile.remove;
+
+    myFile.write("hello");
+    assert(myFile.readText == "hello");
+}
+
+@safe unittest
+{
+    import std.file;
+
+    import std.exception : assertThrown;
+
+    auto space = getAvailableDiskSpace(".");
+    assert(space > 0);
+
+    assertThrown!FileException(getAvailableDiskSpace("ThisFileDoesNotExist123123"));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_format_package.d b/libphobos/testsuite/libphobos.phobos/std_format_package.d
new file mode 100644
index 0000000000000000000000000000000000000000..2287ff2e3f937af0f08a2ff5067b573e4497ffc9
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_format_package.d
@@ -0,0 +1,139 @@
+@safe unittest
+{
+    import std.format;
+
+    // Easiest way is to use `%s` everywhere:
+    assert(format("I got %s %s for %s euros.", 30, "eggs", 5.27) == "I got 30 eggs for 5.27 euros.");
+
+    // Other format characters provide more control:
+    assert(format("I got %b %(%X%) for %f euros.", 30, "eggs", 5.27) == "I got 11110 65676773 for 5.270000 euros.");
+}
+
+@safe unittest
+{
+    import std.format;
+
+/*
+The trailing end of the sub-format string following the specifier for
+each item is interpreted as the array delimiter, and is therefore
+omitted following the last array item:
+ */
+    assert(format("My items are %(%s %).", [1,2,3]) == "My items are 1 2 3.");
+    assert(format("My items are %(%s, %).", [1,2,3]) == "My items are 1, 2, 3.");
+
+/*
+The "%|" delimiter specifier may be used to indicate where the
+delimiter begins, so that the portion of the format string prior to
+it will be retained in the last array element:
+ */
+    assert(format("My items are %(-%s-%|, %).", [1,2,3]) == "My items are -1-, -2-, -3-.");
+
+/*
+These compound format specifiers may be nested in the case of a
+nested array argument:
+ */
+    auto mat = [[1, 2, 3],
+                [4, 5, 6],
+                [7, 8, 9]];
+
+    assert(format("%(%(%d %) - %)", mat), "1 2 3 - 4 5 6 - 7 8 9");
+    assert(format("[%(%(%d %) - %)]", mat), "[1 2 3 - 4 5 6 - 7 8 9]");
+    assert(format("[%([%(%d %)]%| - %)]", mat), "[1 2 3] - [4 5 6] - [7 8 9]");
+
+/*
+Strings and characters are escaped automatically inside compound
+format specifiers. To avoid this behavior, use "%-(" instead of "%(":
+ */
+    assert(format("My friends are %s.", ["John", "Nancy"]) == `My friends are ["John", "Nancy"].`);
+    assert(format("My friends are %(%s, %).", ["John", "Nancy"]) == `My friends are "John", "Nancy".`);
+    assert(format("My friends are %-(%s, %).", ["John", "Nancy"]) == `My friends are John, Nancy.`);
+}
+
+@safe unittest
+{
+    import std.format;
+
+    // Flags can be used to influence to outcome:
+    assert(format("%g != %+#g", 3.14, 3.14) == "3.14 != +3.14000");
+
+    // Width and precision help to arrange the formatted result:
+    assert(format(">%10.2f<", 1234.56789) == ">   1234.57<");
+
+    // Numbers can be grouped:
+    assert(format("%,4d", int.max) == "21,4748,3647");
+
+    // It's possible to specify the position of an argument:
+    assert(format("%3$s %1$s", 3, 17, 5) == "5 3");
+}
+
+@safe unittest
+{
+    import std.format;
+
+    // Width as argument
+    assert(format(">%*s<", 10, "abc") == ">       abc<");
+
+    // Precision as argument
+    assert(format(">%.*f<", 5, 123.2) == ">123.20000<");
+
+    // Grouping as argument
+    assert(format("%,*d", 1, int.max) == "2,1,4,7,4,8,3,6,4,7");
+
+    // Grouping separator as argument
+    assert(format("%,3?d", '_', int.max) == "2_147_483_647");
+
+    // All at once
+    assert(format("%*.*,*?d", 20, 15, 6, '/', int.max) == "   000/002147/483647");
+}
+
+@safe unittest
+{
+    import std.format;
+
+    import std.exception : assertThrown;
+
+    assertThrown!FormatException(format("%d", "foo"));
+}
+
+@safe pure unittest
+{
+    import std.format;
+
+    assert(format("Here are %d %s.", 3, "apples") == "Here are 3 apples.");
+
+    assert("Increase: %7.2f %%".format(17.4285) == "Increase:   17.43 %");
+}
+
+@safe pure unittest
+{
+    import std.format;
+
+    auto s = format!"%s is %s"("Pi", 3.14);
+    assert(s == "Pi is 3.14");
+
+    // This line doesn't compile, because 3.14 cannot be formatted with %d:
+    // s = format!"%s is %d"("Pi", 3.14);
+}
+
+@safe pure unittest
+{
+    import std.format;
+
+    char[20] buf;
+    assert(sformat(buf[], "Here are %d %s.", 3, "apples") == "Here are 3 apples.");
+
+    assert(buf[].sformat("Increase: %7.2f %%", 17.4285) == "Increase:   17.43 %");
+}
+
+@safe pure unittest
+{
+    import std.format;
+
+    char[20] buf;
+
+    assert(sformat!"Here are %d %s."(buf[], 3, "apples") == "Here are 3 apples.");
+
+    // This line doesn't compile, because 3.14 cannot be formatted with %d:
+    // writeln(sformat!"Here are %d %s."(buf[], 3.14, "apples"));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_format_read.d b/libphobos/testsuite/libphobos.phobos/std_format_read.d
new file mode 100644
index 0000000000000000000000000000000000000000..0beb307efb5de0c95a082e66cd52aaeba3726175
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_format_read.d
@@ -0,0 +1,275 @@
+@safe pure unittest
+{
+    import std.format.read;
+
+    import std.format.spec : singleSpec;
+
+    auto str = "false";
+    auto spec = singleSpec("%s");
+    assert(str.unformatValue!bool(spec) == false);
+
+    str = "1";
+    spec = singleSpec("%d");
+    assert(str.unformatValue!bool(spec) == true);
+}
+
+@safe pure unittest
+{
+    import std.format.read;
+
+    import std.format.spec : singleSpec;
+
+    auto str = "null";
+    auto spec = singleSpec("%s");
+    assert(str.unformatValue!(typeof(null))(spec) == null);
+}
+
+@safe pure unittest
+{
+    import std.format.read;
+
+    import std.format.spec : singleSpec;
+
+    // signed decimal values
+    auto str = "123";
+    auto spec = singleSpec("%s");
+    assert(str.unformatValue!int(spec) == 123);
+
+    // hexadecimal values
+    str = "ABC";
+    spec = singleSpec("%X");
+    assert(str.unformatValue!int(spec) == 2748);
+
+    // octal values
+    str = "11610";
+    spec = singleSpec("%o");
+    assert(str.unformatValue!int(spec) == 5000);
+
+    // raw read, depends on endianess
+    str = "\x75\x01";
+    spec = singleSpec("%r");
+    auto result = str.unformatValue!short(spec);
+    assert(result == 373 /* little endian */ || result == 29953 /* big endian */ );
+}
+
+@safe pure unittest
+{
+    import std.format.read;
+
+    import std.format.spec : singleSpec;
+    import std.math.operations : isClose;
+
+    // natural notation
+    auto str = "123.456";
+    auto spec = singleSpec("%s");
+    assert(str.unformatValue!double(spec).isClose(123.456));
+
+    // scientific notation
+    str = "1e17";
+    spec = singleSpec("%e");
+    assert(str.unformatValue!double(spec).isClose(1e17));
+
+    // raw read, depends on endianess
+    str = "\x40\x00\x00\xBF";
+    spec = singleSpec("%r");
+    auto result = str.unformatValue!float(spec);
+    assert(isClose(result, -0.5) /* little endian */ || isClose(result, 2.0) /* big endian */ );
+}
+
+@safe pure unittest
+{
+    import std.format.read;
+
+    import std.format.spec : singleSpec;
+
+    // only the first character is read
+    auto str = "abc";
+    auto spec = singleSpec("%s");
+    assert(str.unformatValue!char(spec) == 'a');
+
+    // using a numerical format character treats the read number as unicode code point
+    str = "65";
+    spec = singleSpec("%d");
+    assert(str.unformatValue!char(spec) == 'A');
+
+    str = "41";
+    spec = singleSpec("%x");
+    assert(str.unformatValue!char(spec) == 'A');
+
+    str = "10003";
+    spec = singleSpec("%d");
+    assert(str.unformatValue!dchar(spec) == '✓');
+}
+
+@safe pure unittest
+{
+    import std.format.read;
+
+    import std.format.spec : singleSpec;
+
+    // string value
+    string str = "aaa";
+    auto spec = singleSpec("%s");
+    assert(str.unformatValue!(dchar[])(spec) == "aaa"d);
+
+    // fixed size array with characters
+    str = "aaa";
+    spec = singleSpec("%s");
+    dchar[3] ret = ['a', 'a', 'a'];
+    assert(str.unformatValue!(dchar[3])(spec) == ret);
+
+    // dynamic array
+    str = "[1, 2, 3, 4]";
+    spec = singleSpec("%s");
+    assert(str.unformatValue!(int[])(spec) == [1, 2, 3, 4]);
+
+    // fixed size array with integers
+    str = "[1, 2, 3, 4]";
+    spec = singleSpec("%s");
+    int[4] ret2 = [1, 2, 3, 4];
+    assert(str.unformatValue!(int[4])(spec) == ret2);
+
+    // compound specifiers can be used for more control
+    str = "1,2,3";
+    spec = singleSpec("%(%s,%)");
+    assert(str.unformatValue!(int[])(spec) == [1, 2, 3]);
+
+    str = "cool";
+    spec = singleSpec("%(%c%)");
+    assert(str.unformatValue!(char[])(spec) == ['c', 'o', 'o', 'l']);
+}
+
+@safe pure unittest
+{
+    import std.format.read;
+
+    import std.format.spec : singleSpec;
+
+    // as single value
+    auto str = `["one": 1, "two": 2]`;
+    auto spec = singleSpec("%s");
+    assert(str.unformatValue!(int[string])(spec) == ["one": 1, "two": 2]);
+
+    // with compound specifier for more control
+    str = "1/1, 2/4, 3/9";
+    spec = singleSpec("%(%d/%d%|, %)");
+    assert(str.unformatValue!(int[int])(spec) == [1: 1, 2: 4, 3: 9]);
+}
+
+@safe pure unittest
+{
+    import std.format.read;
+
+    string object;
+    char cmp;
+    int value;
+
+    assert(formattedRead("angle < 36", "%s %c %d", object, cmp, value) == 3);
+    assert(object == "angle");
+    assert(cmp == '<');
+    assert(value == 36);
+
+    // reading may end early:
+    assert(formattedRead("length >", "%s %c %d", object, cmp, value) == 2);
+    assert(object == "length");
+    assert(cmp == '>');
+    // value is not changed:
+    assert(value == 36);
+}
+
+@safe pure unittest
+{
+    import std.format.read;
+
+    string a;
+    int b;
+    double c;
+
+    assert("hello!124:34.5".formattedRead!"%s!%s:%s"(a, b, c) == 3);
+    assert(a == "hello");
+    assert(b == 124);
+    assert(c == 34.5);
+}
+
+@safe pure unittest
+{
+    import std.format.read;
+
+    string item;
+    double amount;
+
+    assert("orange: (12%) 15.25".formattedRead("%s: (%*d%%) %f", item, amount) == 2);
+    assert(item == "orange");
+    assert(amount == 15.25);
+
+    // can also be used with tuples
+    import std.typecons : Tuple;
+
+    Tuple!(int, float) t;
+    char[] line = "1 7643 2.125".dup;
+    formattedRead(line, "%s %*u %s", t);
+    assert(t[0] == 1 && t[1] == 2.125);
+}
+
+@safe pure unittest
+{
+    import std.format.read;
+
+    import std.exception : assertThrown;
+    import std.format : FormatException;
+    import std.typecons : tuple;
+
+    auto complete = "hello!34.5:124".formattedRead!(string, double, int)("%s!%s:%s");
+    assert(complete == tuple("hello", 34.5, 124));
+
+    // reading ends early
+    assertThrown!FormatException("hello!34.5:".formattedRead!(string, double, int)("%s!%s:%s"));
+}
+
+@safe pure unittest
+{
+    import std.format.read;
+
+    import std.format : FormatException;
+    import std.typecons : tuple;
+
+    auto result = "orange: (12%) 15.25".formattedRead!(string, double)("%s: (%*d%%) %f");
+    assert(result == tuple("orange", 15.25));
+}
+
+@safe pure unittest
+{
+    import std.format.read;
+
+    import std.exception : assertThrown;
+    import std.format : FormatException;
+    import std.typecons : tuple;
+
+    auto expected = tuple("hello", 124, 34.5);
+    auto result = "hello!124:34.5".formattedRead!("%s!%s:%s", string, int, double);
+    assert(result == expected);
+
+    assertThrown!FormatException("hello!34.5:".formattedRead!("%s!%s:%s", string, double, int));
+}
+
+@safe pure unittest
+{
+    import std.format.read;
+
+    import std.format : FormatException;
+    import std.typecons : tuple;
+
+    static assert(!__traits(compiles, "orange: (12%) 15.25".formattedRead!("%s: (%*d%%) %f", string, double)));
+}
+
+@safe pure unittest
+{
+    import std.format.read;
+
+    import std.format.spec : singleSpec;
+
+    string s = "42";
+    auto spec = singleSpec("%s");
+    assert(unformatValue!int(s, spec) == 42);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_format_spec.d b/libphobos/testsuite/libphobos.phobos/std_format_spec.d
new file mode 100644
index 0000000000000000000000000000000000000000..a24c4779b8b5c1ec5c35e13d834ed92d068762fe
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_format_spec.d
@@ -0,0 +1,43 @@
+@safe pure unittest
+{
+    import std.format.spec;
+
+    import std.array : appender;
+
+    auto a = appender!(string)();
+    auto fmt = "Number: %6.4e\nString: %s";
+    auto f = FormatSpec!char(fmt);
+
+    assert(f.writeUpToNextSpec(a));
+
+    assert(a.data == "Number: ");
+    assert(f.trailing == "\nString: %s");
+    assert(f.spec == 'e');
+    assert(f.width == 6);
+    assert(f.precision == 4);
+
+    assert(f.writeUpToNextSpec(a));
+
+    assert(a.data == "Number: \nString: ");
+    assert(f.trailing == "");
+    assert(f.spec == 's');
+
+    assert(!f.writeUpToNextSpec(a));
+
+    assert(a.data == "Number: \nString: ");
+}
+
+@safe pure unittest
+{
+    import std.format.spec;
+
+    import std.array : appender;
+    import std.format.write : formatValue;
+
+    auto spec = singleSpec("%10.3e");
+    auto writer = appender!string();
+    writer.formatValue(42.0, spec);
+
+    assert(writer.data == " 4.200e+01");
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_format_write.d b/libphobos/testsuite/libphobos.phobos/std_format_write.d
new file mode 100644
index 0000000000000000000000000000000000000000..aacd7fb0990c187477537d7f582b125e0e675c2a
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_format_write.d
@@ -0,0 +1,421 @@
+@safe pure unittest
+{
+    import std.format.write;
+
+    import std.array : appender;
+    import std.format.spec : singleSpec;
+
+    auto w1 = appender!string();
+    auto spec1 = singleSpec("%s");
+    formatValue(w1, true, spec1);
+
+    assert(w1.data == "true");
+
+    auto w2 = appender!string();
+    auto spec2 = singleSpec("%#x");
+    formatValue(w2, true, spec2);
+
+    assert(w2.data == "0x1");
+}
+
+@safe pure unittest
+{
+    import std.format.write;
+
+    import std.array : appender;
+    import std.format.spec : singleSpec;
+
+    auto w = appender!string();
+    auto spec = singleSpec("%s");
+    formatValue(w, null, spec);
+
+    assert(w.data == "null");
+}
+
+@safe pure unittest
+{
+    import std.format.write;
+
+    import std.array : appender;
+    import std.format.spec : singleSpec;
+
+    auto w1 = appender!string();
+    auto spec1 = singleSpec("%d");
+    formatValue(w1, -1337, spec1);
+
+    assert(w1.data == "-1337");
+
+    auto w2 = appender!string();
+    auto spec2 = singleSpec("%x");
+    formatValue(w2, -1337, spec2);
+
+    assert(w2.data == "fffffac7");
+}
+
+@safe unittest
+{
+    import std.format.write;
+
+    import std.array : appender;
+    import std.format.spec : singleSpec;
+
+    auto w1 = appender!string();
+    auto spec1 = singleSpec("%.3f");
+    formatValue(w1, 1337.7779, spec1);
+
+    assert(w1.data == "1337.778");
+
+    auto w2 = appender!string();
+    auto spec2 = singleSpec("%.3e");
+    formatValue(w2, 1337.7779, spec2);
+
+    assert(w2.data == "1.338e+03");
+
+    auto w3 = appender!string();
+    auto spec3 = singleSpec("%.3g");
+    formatValue(w3, 1337.7779, spec3);
+
+    assert(w3.data == "1.34e+03");
+
+    auto w4 = appender!string();
+    auto spec4 = singleSpec("%.3a");
+    formatValue(w4, 1337.7779, spec4);
+
+    assert(w4.data == "0x1.4e7p+10");
+}
+
+@safe pure unittest
+{
+    import std.format.write;
+
+    import std.array : appender;
+    import std.format.spec : singleSpec;
+
+    auto w1 = appender!string();
+    auto spec1 = singleSpec("%c");
+    formatValue(w1, 'ì', spec1);
+
+    assert(w1.data == "ì");
+
+    auto w2 = appender!string();
+    auto spec2 = singleSpec("%#x");
+    formatValue(w2, 'ì', spec2);
+
+    assert(w2.data == "0xec");
+}
+
+@safe pure unittest
+{
+    import std.format.write;
+
+    import std.array : appender;
+    import std.format.spec : singleSpec;
+
+    auto w1 = appender!string();
+    auto spec1 = singleSpec("%s");
+    formatValue(w1, "hello", spec1);
+
+    assert(w1.data == "hello");
+
+    auto w2 = appender!string();
+    auto spec2 = singleSpec("%(%#x%|/%)");
+    formatValue(w2, "hello", spec2);
+
+    assert(w2.data == "0x68/0x65/0x6c/0x6c/0x6f");
+}
+
+@safe pure unittest
+{
+    import std.format.write;
+
+    import std.array : appender;
+    import std.format.spec : singleSpec;
+
+    auto w = appender!string();
+    auto spec = singleSpec("%s");
+    int[2] two = [1, 2];
+    formatValue(w, two, spec);
+
+    assert(w.data == "[1, 2]");
+}
+
+@safe pure unittest
+{
+    import std.format.write;
+
+    import std.array : appender;
+    import std.format.spec : singleSpec;
+
+    auto w1 = appender!string();
+    auto spec1 = singleSpec("%s");
+    auto two = [1, 2];
+    formatValue(w1, two, spec1);
+
+    assert(w1.data == "[1, 2]");
+
+    auto w2 = appender!string();
+    auto spec2 = singleSpec("%(%g%|, %)");
+    auto consts = [3.1415926, 299792458, 6.67430e-11];
+    formatValue(w2, consts, spec2);
+
+    assert(w2.data == "3.14159, 2.99792e+08, 6.6743e-11");
+
+    // void[] is treated like ubyte[]
+    auto w3 = appender!string();
+    auto spec3 = singleSpec("%s");
+    void[] val = cast(void[]) cast(ubyte[])[1, 2, 3];
+    formatValue(w3, val, spec3);
+
+    assert(w3.data == "[1, 2, 3]");
+}
+
+@safe pure unittest
+{
+    import std.format.write;
+
+    import std.array : appender;
+    import std.format.spec : singleSpec;
+
+    auto aa = [10:17.5, 20:9.99];
+
+    auto w1 = appender!string();
+    auto spec1 = singleSpec("%s");
+    formatValue(w1, aa, spec1);
+
+    assert(w1.data == "[10:17.5, 20:9.99]" || w1.data == "[20:9.99, 10:17.5]");
+
+    auto w2 = appender!string();
+    auto spec2 = singleSpec("%(%x = %.0e%| # %)");
+    formatValue(w2, aa, spec2);
+
+    assert(w2.data == "a = 2e+01 # 14 = 1e+01" || w2.data == "14 = 1e+01 # a = 2e+01");
+}
+
+@safe pure unittest
+{
+    import std.format.write;
+
+    import std.array : appender;
+    import std.format.spec : singleSpec;
+
+    enum A { first, second, third }
+
+    auto w1 = appender!string();
+    auto spec1 = singleSpec("%s");
+    formatValue(w1, A.second, spec1);
+
+    assert(w1.data == "second");
+
+    auto w2 = appender!string();
+    auto spec2 = singleSpec("%d");
+    formatValue(w2, A.second, spec2);
+
+    assert(w2.data == "1");
+
+    // values of an enum that have no name are formatted with %s using a cast
+    A a = A.third;
+    a++;
+
+    auto w3 = appender!string();
+    auto spec3 = singleSpec("%s");
+    formatValue(w3, a, spec3);
+
+    assert(w3.data == "cast(A)3");
+}
+
+@safe unittest
+{
+    import std.format.write;
+
+    import std.array : appender;
+    import std.format.spec : FormatSpec, singleSpec;
+
+    // Using a `toString` with a writer
+    static struct Point1
+    {
+        import std.range.primitives : isOutputRange, put;
+
+        int x, y;
+
+        void toString(W)(ref W writer, scope const ref FormatSpec!char f)
+        if (isOutputRange!(W, char))
+        {
+            put(writer, "(");
+            formatValue(writer, x, f);
+            put(writer, ",");
+            formatValue(writer, y, f);
+            put(writer, ")");
+        }
+    }
+
+    auto w1 = appender!string();
+    auto spec1 = singleSpec("%s");
+    auto p1 = Point1(16, 11);
+
+    formatValue(w1, p1, spec1);
+    assert(w1.data == "(16,11)");
+
+    // Using a `toString` with a sink
+    static struct Point2
+    {
+        int x, y;
+
+        void toString(scope void delegate(scope const(char)[]) @safe sink,
+                      scope const FormatSpec!char fmt) const
+        {
+            sink("(");
+            sink.formatValue(x, fmt);
+            sink(",");
+            sink.formatValue(y, fmt);
+            sink(")");
+        }
+    }
+
+    auto w2 = appender!string();
+    auto spec2 = singleSpec("%03d");
+    auto p2 = Point2(16,11);
+
+    formatValue(w2, p2, spec2);
+    assert(w2.data == "(016,011)");
+
+    // Using `string toString()`
+    static struct Point3
+    {
+        int x, y;
+
+        string toString()
+        {
+            import std.conv : to;
+
+            return "(" ~ to!string(x) ~ "," ~ to!string(y) ~ ")";
+        }
+    }
+
+    auto w3 = appender!string();
+    auto spec3 = singleSpec("%s"); // has to be %s
+    auto p3 = Point3(16,11);
+
+    formatValue(w3, p3, spec3);
+    assert(w3.data == "(16,11)");
+
+    // without `toString`
+    static struct Point4
+    {
+        int x, y;
+    }
+
+    auto w4 = appender!string();
+    auto spec4 = singleSpec("%s"); // has to be %s
+    auto p4 = Point4(16,11);
+
+    formatValue(w4, p4, spec3);
+    assert(w4.data == "Point4(16, 11)");
+}
+
+@safe pure unittest
+{
+    import std.format.write;
+
+    import std.array : appender;
+    import std.format.spec : singleSpec;
+
+    auto w1 = appender!string();
+    auto spec1 = singleSpec("%s");
+    auto p1 = () @trusted { return cast(void*) 0xFFEECCAA; } ();
+    formatValue(w1, p1, spec1);
+
+    assert(w1.data == "FFEECCAA");
+
+    // null pointers are printed as `"null"` when used with `%s` and as hexadecimal integer else
+    auto w2 = appender!string();
+    auto spec2 = singleSpec("%s");
+    auto p2 = () @trusted { return cast(void*) 0x00000000; } ();
+    formatValue(w2, p2, spec2);
+
+    assert(w2.data == "null");
+
+    auto w3 = appender!string();
+    auto spec3 = singleSpec("%x");
+    formatValue(w3, p2, spec3);
+
+    assert(w3.data == "0");
+}
+
+@safe unittest
+{
+    import std.format.write;
+
+    import core.simd; // cannot be selective, because float4 might not be defined
+    import std.array : appender;
+    import std.format.spec : singleSpec;
+
+    auto w = appender!string();
+    auto spec = singleSpec("%s");
+
+    static if (is(float4))
+    {
+        version (X86) {}
+        else
+        {
+            float4 f4;
+            f4.array[0] = 1;
+            f4.array[1] = 2;
+            f4.array[2] = 3;
+            f4.array[3] = 4;
+
+            formatValue(w, f4, spec);
+            assert(w.data == "[1, 2, 3, 4]");
+        }
+    }
+}
+
+@safe pure unittest
+{
+    import std.format.write;
+
+    import std.array : appender;
+
+    auto writer1 = appender!string();
+    formattedWrite(writer1, "%s is the ultimate %s.", 42, "answer");
+    assert(writer1[] == "42 is the ultimate answer.");
+
+    auto writer2 = appender!string();
+    formattedWrite(writer2, "Increase: %7.2f %%", 17.4285);
+    assert(writer2[] == "Increase:   17.43 %");
+}
+
+@safe pure unittest
+{
+    import std.format.write;
+
+    import std.array : appender;
+
+    auto writer = appender!string();
+    writer.formattedWrite!"%d is the ultimate %s."(42, "answer");
+    assert(writer[] == "42 is the ultimate answer.");
+
+    // This line doesn't compile, because 3.14 cannot be formatted with %d:
+    // writer.formattedWrite!"%d is the ultimate %s."(3.14, "answer");
+}
+
+@safe pure unittest
+{
+    import std.format.write;
+
+    import std.array : appender;
+    import std.format.spec : singleSpec;
+
+    auto writer = appender!string();
+    auto spec = singleSpec("%08b");
+    writer.formatValue(42, spec);
+    assert(writer.data == "00101010");
+
+    spec = singleSpec("%2s");
+    writer.formatValue('=', spec);
+    assert(writer.data == "00101010 =");
+
+    spec = singleSpec("%+14.6e");
+    writer.formatValue(42.0, spec);
+    assert(writer.data == "00101010 = +4.200000e+01");
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_functional.d b/libphobos/testsuite/libphobos.phobos/std_functional.d
new file mode 100644
index 0000000000000000000000000000000000000000..3bfab45658ebe430520554fa3eb8440865007faa
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_functional.d
@@ -0,0 +1,360 @@
+@safe unittest
+{
+    import std.functional;
+
+    // Strings are compiled into functions:
+    alias isEven = unaryFun!("(a & 1) == 0");
+    assert(isEven(2) && !isEven(1));
+}
+
+@safe unittest
+{
+    import std.functional;
+
+    alias less = binaryFun!("a < b");
+    assert(less(1, 2) && !less(2, 1));
+    alias greater = binaryFun!("a > b");
+    assert(!greater("1", "2") && greater("2", "1"));
+}
+
+pure @safe @nogc nothrow unittest
+{
+    import std.functional;
+
+    assert(lessThan(2, 3));
+    assert(lessThan(2U, 3U));
+    assert(lessThan(2, 3.0));
+    assert(lessThan(-2, 3U));
+    assert(lessThan(2, 3U));
+    assert(!lessThan(3U, -2));
+    assert(!lessThan(3U, 2));
+    assert(!lessThan(0, 0));
+    assert(!lessThan(0U, 0));
+    assert(!lessThan(0, 0U));
+}
+
+@safe unittest
+{
+    import std.functional;
+
+    assert(!greaterThan(2, 3));
+    assert(!greaterThan(2U, 3U));
+    assert(!greaterThan(2, 3.0));
+    assert(!greaterThan(-2, 3U));
+    assert(!greaterThan(2, 3U));
+    assert(greaterThan(3U, -2));
+    assert(greaterThan(3U, 2));
+    assert(!greaterThan(0, 0));
+    assert(!greaterThan(0U, 0));
+    assert(!greaterThan(0, 0U));
+}
+
+@safe unittest
+{
+    import std.functional;
+
+    assert(equalTo(0U, 0));
+    assert(equalTo(0, 0U));
+    assert(!equalTo(-1, ~0U));
+}
+
+@safe unittest
+{
+    import std.functional;
+
+    alias gt = reverseArgs!(binaryFun!("a < b"));
+    assert(gt(2, 1) && !gt(1, 1));
+}
+
+@safe unittest
+{
+    import std.functional;
+
+    int x = 42;
+    bool xyz(int a, int b) { return a * x < b / x; }
+    auto foo = &xyz;
+    foo(4, 5);
+    alias zyx = reverseArgs!(foo);
+    assert(zyx(5, 4) == foo(4, 5));
+}
+
+@safe unittest
+{
+    import std.functional;
+
+    alias gt = reverseArgs!(binaryFun!("a < b"));
+    assert(gt(2, 1) && !gt(1, 1));
+    int x = 42;
+    bool xyz(int a, int b) { return a * x < b / x; }
+    auto foo = &xyz;
+    foo(4, 5);
+    alias zyx = reverseArgs!(foo);
+    assert(zyx(5, 4) == foo(4, 5));
+}
+
+@safe unittest
+{
+    import std.functional;
+
+    int abc(int a, int b, int c) { return a * b + c; }
+    alias cba = reverseArgs!abc;
+    assert(abc(91, 17, 32) == cba(32, 17, 91));
+}
+
+@safe unittest
+{
+    import std.functional;
+
+    int a(int a) { return a * 2; }
+    alias _a = reverseArgs!a;
+    assert(a(2) == _a(2));
+}
+
+@safe unittest
+{
+    import std.functional;
+
+    int b() { return 4; }
+    alias _b = reverseArgs!b;
+    assert(b() == _b());
+}
+
+@safe unittest
+{
+    import std.functional;
+
+    import std.algorithm.searching : find;
+    import std.uni : isWhite;
+    string a = "   Hello, world!";
+    assert(find!(not!isWhite)(a) == "Hello, world!");
+}
+
+@safe unittest
+{
+    import std.functional;
+
+    int fun(int a, int b) { return a + b; }
+    alias fun5 = partial!(fun, 5);
+    assert(fun5(6) == 11);
+    // Note that in most cases you'd use an alias instead of a value
+    // assignment. Using an alias allows you to partially evaluate template
+    // functions without committing to a particular type of the function.
+}
+
+@safe unittest
+{
+    import std.functional;
+
+    // Overloads are resolved when the partially applied function is called
+    // with the remaining arguments.
+    struct S
+    {
+        static char fun(int i, string s) { return s[i]; }
+        static int fun(int a, int b) { return a * b; }
+    }
+    alias fun3 = partial!(S.fun, 3);
+    assert(fun3("hello") == 'l');
+    assert(fun3(10) == 30);
+}
+
+pure @safe @nogc nothrow unittest
+{
+    import std.functional;
+
+    int f(int x, int y, int z)
+    {
+        return x + y + z;
+    }
+    auto cf = curry!f;
+    auto cf1 = cf(1);
+    auto cf2 = cf(2);
+
+    assert(cf1(2)(3) == f(1, 2, 3));
+    assert(cf2(2)(3) == f(2, 2, 3));
+}
+
+pure @safe @nogc nothrow unittest
+{
+    import std.functional;
+
+    //works with callable structs too
+    struct S
+    {
+        int w;
+        int opCall(int x, int y, int z)
+        {
+            return w + x + y + z;
+        }
+    }
+
+    S s;
+    s.w = 5;
+
+    auto cs = curry(s);
+    auto cs1 = cs(1);
+    auto cs2 = cs(2);
+
+    assert(cs1(2)(3) == s(1, 2, 3));
+    assert(cs1(2)(3) == (1 + 2 + 3 + 5));
+    assert(cs2(2)(3) ==s(2, 2, 3));
+}
+
+@safe unittest
+{
+    import std.functional;
+
+    import std.typecons : Tuple;
+    static bool f1(int a) { return a != 0; }
+    static int f2(int a) { return a / 2; }
+    auto x = adjoin!(f1, f2)(5);
+    assert(is(typeof(x) == Tuple!(bool, int)));
+    assert(x[0] == true && x[1] == 2);
+}
+
+@safe unittest
+{
+    import std.functional;
+
+    import std.algorithm.comparison : equal;
+    import std.algorithm.iteration : map;
+    import std.array : split;
+    import std.conv : to;
+
+    // First split a string in whitespace-separated tokens and then
+    // convert each token into an integer
+    assert(compose!(map!(to!(int)), split)("1 2 3").equal([1, 2, 3]));
+}
+
+@safe unittest
+{
+    import std.functional;
+
+    import std.conv : to;
+    string foo(int a) { return to!(string)(a); }
+    int bar(string a) { return to!(int)(a) + 1; }
+    double baz(int a) { return a + 0.5; }
+    assert(compose!(baz, bar, foo)(1) == 2.5);
+    assert(pipe!(foo, bar, baz)(1) == 2.5);
+
+    assert(compose!(baz, `to!(int)(a) + 1`, foo)(1) == 2.5);
+    assert(compose!(baz, bar)("1"[]) == 2.5);
+
+    assert(compose!(baz, bar)("1") == 2.5);
+
+    assert(compose!(`a + 0.5`, `to!(int)(a) + 1`, foo)(1) == 2.5);
+}
+
+@safe nothrow unittest
+{
+    import std.functional;
+
+    ulong fib(ulong n) @safe nothrow
+    {
+        return n < 2 ? n : memoize!fib(n - 2) + memoize!fib(n - 1);
+    }
+    assert(fib(10) == 55);
+}
+
+@safe unittest
+{
+    import std.functional;
+
+    ulong fact(ulong n) @safe
+    {
+        return n < 2 ? 1 : n * memoize!fact(n - 1);
+    }
+    assert(fact(10) == 3628800);
+}
+
+@safe unittest
+{
+    import std.functional;
+
+    ulong factImpl(ulong n) @safe
+    {
+        return n < 2 ? 1 : n * factImpl(n - 1);
+    }
+    alias fact = memoize!factImpl;
+    assert(fact(10) == 3628800);
+}
+
+@system unittest
+{
+    import std.functional;
+
+    ulong fact(ulong n)
+    {
+        // Memoize no more than 8 values
+        return n < 2 ? 1 : n * memoize!(fact, 8)(n - 1);
+    }
+    assert(fact(8) == 40320);
+    // using more entries than maxSize will overwrite existing entries
+    assert(fact(10) == 3628800);
+}
+
+@safe unittest
+{
+    import std.functional;
+
+    static int inc(ref uint num) {
+        num++;
+        return 8675309;
+    }
+
+    uint myNum = 0;
+    auto incMyNumDel = toDelegate(&inc);
+    auto returnVal = incMyNumDel(myNum);
+    assert(myNum == 1);
+}
+
+@safe unittest
+{
+    import std.functional;
+
+    import std.typecons : tuple;
+
+    auto name = tuple("John", "Doe");
+    string full = name.bind!((first, last) => first ~ " " ~ last);
+    assert(full == "John Doe");
+}
+
+@safe unittest
+{
+    import std.functional;
+
+    import std.algorithm.comparison : min, max;
+
+    struct Pair
+    {
+        int a;
+        int b;
+    }
+
+    auto p = Pair(123, 456);
+    assert(p.bind!min == 123); // min(p.a, p.b)
+    assert(p.bind!max == 456); // max(p.a, p.b)
+}
+
+@safe unittest
+{
+    import std.functional;
+
+    import std.algorithm.iteration : map, filter;
+    import std.algorithm.comparison : equal;
+    import std.typecons : tuple;
+
+    auto ages = [
+        tuple("Alice", 35),
+        tuple("Bob",   64),
+        tuple("Carol", 21),
+        tuple("David", 39),
+        tuple("Eve",   50)
+    ];
+
+    auto overForty = ages
+        .filter!(bind!((name, age) => age > 40))
+        .map!(bind!((name, age) => name));
+
+    assert(overForty.equal(["Bob", "Eve"]));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_getopt.d b/libphobos/testsuite/libphobos.phobos/std_getopt.d
new file mode 100644
index 0000000000000000000000000000000000000000..12d81f3878099a686a5b0297787e0022ffe6bb9e
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_getopt.d
@@ -0,0 +1,18 @@
+@safe unittest
+{
+    import std.getopt;
+
+    auto args = ["prog", "--foo", "-b"];
+
+    bool foo;
+    bool bar;
+    auto rslt = getopt(args, "foo|f", "Some information about foo.", &foo, "bar|b",
+        "Some help message about bar.", &bar);
+
+    if (rslt.helpWanted)
+    {
+        defaultGetoptPrinter("Some information about the program.",
+            rslt.options);
+    }
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_int128.d b/libphobos/testsuite/libphobos.phobos/std_int128.d
new file mode 100644
index 0000000000000000000000000000000000000000..71548fd124a7bb13f3d72dfa818c3cd808590f19
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_int128.d
@@ -0,0 +1,128 @@
+@safe unittest
+{
+    import std.int128;
+
+        const Int128 a = Int128(0xffff_ffff_ffff_ffffL, 0x0123_4567_89ab_cdefL);
+        assert(cast(long) a == 0x0123_4567_89ab_cdefL);
+        assert(cast(int)  a ==           0x89ab_cdef);
+        assert(cast(byte) a == cast(byte) 0xef);
+    
+}
+
+@safe unittest
+{
+    import std.int128;
+
+        const Int128 a = Int128(-1L << 60);
+        assert(cast(double) a == -(2.0 ^^ 60));
+        assert(cast(double) (a * a) == 2.0 ^^ 120);
+    
+}
+
+@safe unittest
+{
+    import std.int128;
+
+        import std.format : format;
+
+        assert(format("%s", Int128.max) == "170141183460469231731687303715884105727");
+        assert(format("%s", Int128.min) == "-170141183460469231731687303715884105728");
+        assert(format("%x", Int128.max) == "7fffffffffffffffffffffffffffffff");
+        assert(format("%X", Int128.max) == "7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+        assert(format("%032X", Int128(123L)) == "0000000000000000000000000000007B");
+        assert(format("%+ 40d", Int128(123L)) == "                                    +123");
+        assert(format("%+-40d", Int128(123L)) == "+123                                    ");
+    
+}
+
+@safe unittest
+{
+    import std.int128;
+
+        import std.conv : to;
+
+        assert(to!wstring(Int128.max) == "170141183460469231731687303715884105727"w);
+        assert(to!dstring(Int128.max) == "170141183460469231731687303715884105727"d);
+    
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.int128;
+
+    Int128 c = Int128(5, 6);
+    assert(c == c);
+    assert(c == +c);
+    assert(c == - -c);
+    assert(~c == Int128(~5, ~6));
+    ++c;
+    assert(c == Int128(5, 7));
+    assert(--c == Int128(5, 6));
+    assert(!!c);
+    assert(!Int128());
+
+    assert(c + Int128(10, 20) == Int128(15, 26));
+    assert(c - Int128(1, 2)   == Int128(4, 4));
+    assert(c * Int128(100, 2) == Int128(610, 12));
+    assert(c / Int128(3, 2)   == Int128(0, 1));
+    assert(c % Int128(3, 2)   == Int128(2, 4));
+    assert((c & Int128(3, 2)) == Int128(1, 2));
+    assert((c | Int128(3, 2)) == Int128(7, 6));
+    assert((c ^ Int128(3, 2)) == Int128(6, 4));
+
+    assert(c + 15   == Int128(5, 21));
+    assert(c - 15   == Int128(4, -9));
+    assert(c * 15   == Int128(75, 90));
+    assert(c / 15   == Int128(0, 6148914691236517205));
+    assert(c % 15   == Int128(0, 11));
+    assert((c & 15) == Int128(0, 6));
+    assert((c | 15) == Int128(5, 15));
+    assert((c ^ 15) == Int128(5, 9));
+
+    assert(15 + c   == Int128(5, 21));
+    assert(15 - c   == Int128(-5, 9));
+    assert(15 * c   == Int128(75, 90));
+    assert(15 / c   == Int128(0, 0));
+    assert(15 % c   == Int128(0, 15));
+    assert((15 & c) == Int128(0, 6));
+    assert((15 | c) == Int128(5, 15));
+    assert((15 ^ c) == Int128(5, 9));
+
+    assert(c << 1 == Int128(10, 12));
+    assert(-c >> 1 == Int128(-3, 9223372036854775805));
+    assert(-c >>> 1 == Int128(9223372036854775805, 9223372036854775805));
+
+    assert((c += 1) == Int128(5, 7));
+    assert((c -= 1) == Int128(5, 6));
+    assert((c += Int128(0, 1)) == Int128(5, 7));
+    assert((c -= Int128(0, 1)) == Int128(5, 6));
+    assert((c *= 2) == Int128(10, 12));
+    assert((c /= 2) == Int128(5, 6));
+    assert((c %= 2) == Int128());
+    c += Int128(5, 6);
+    assert((c *= Int128(10, 20)) == Int128(160, 120));
+    assert((c /= Int128(10, 20)) == Int128(0, 15));
+    c += Int128(72, 0);
+    assert((c %= Int128(10, 20)) == Int128(1, -125));
+    assert((c &= Int128(3, 20)) == Int128(1, 0));
+    assert((c |= Int128(8, 2)) == Int128(9, 2));
+    assert((c ^= Int128(8, 2)) == Int128(1, 0));
+    c |= Int128(10, 5);
+    assert((c <<= 1) == Int128(11 * 2, 5 * 2));
+    assert((c >>>= 1) == Int128(11, 5));
+    c = Int128(long.min, long.min);
+    assert((c >>= 1) == Int128(long.min >> 1, cast(ulong) long.min >> 1));
+
+    assert(-Int128.min == Int128.min);
+    assert(Int128.max + 1 == Int128.min);
+
+    c = Int128(5, 6);
+    assert(c < Int128(6, 5));
+    assert(c > 10);
+
+    c = Int128(-1UL);
+    assert(c == -1UL);
+    c = Int128(-1L);
+    assert(c == -1L);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_internal_cstring.d b/libphobos/testsuite/libphobos.phobos/std_internal_cstring.d
new file mode 100644
index 0000000000000000000000000000000000000000..8cb8d6187d1889487288919677acfddea8351229
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_internal_cstring.d
@@ -0,0 +1,47 @@
+@safe unittest
+{
+    import std.internal.cstring;
+
+    version (Posix)
+    {
+        import core.stdc.stdlib : free;
+        import core.sys.posix.stdlib : setenv;
+        import std.exception : enforce;
+
+        void setEnvironment(scope const(char)[] name, scope const(char)[] value)
+        { enforce(setenv(name.tempCString(), value.tempCString(), 1) != -1); }
+    }
+
+    version (Windows)
+    {
+        import core.sys.windows.winbase : SetEnvironmentVariableW;
+        import std.exception : enforce;
+
+        void setEnvironment(scope const(char)[] name, scope const(char)[] value)
+        { enforce(SetEnvironmentVariableW(name.tempCStringW(), value.tempCStringW())); }
+    }
+}
+
+nothrow @nogc @system unittest
+{
+    import std.internal.cstring;
+
+    import core.stdc.string;
+
+    string str = "abc";
+
+    // Intended usage
+    assert(strlen(str.tempCString()) == 3);
+
+    // Correct usage
+    auto tmp = str.tempCString();
+    assert(strlen(tmp) == 3); // or `tmp.ptr`, or `tmp.buffPtr`
+
+    // $(RED WARNING): $(RED Incorrect usage)
+    auto pInvalid1 = str.tempCString().ptr;
+    const char* pInvalid2 = str.tempCString();
+    // Both pointers refer to invalid memory here as
+    // returned values aren't assigned to a variable and
+    // both primary expressions are ended.
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_internal_scopebuffer.d b/libphobos/testsuite/libphobos.phobos/std_internal_scopebuffer.d
new file mode 100644
index 0000000000000000000000000000000000000000..adecde1f5029d750c5e79cc7a85b498fc2c71761
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_internal_scopebuffer.d
@@ -0,0 +1,9 @@
+@system unittest
+{
+    import std.internal.scopebuffer;
+
+    ubyte[10] tmpbuf = void;
+    auto sb = scopeBuffer(tmpbuf);
+    scope(exit) sb.free();
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_json.d b/libphobos/testsuite/libphobos.phobos/std_json.d
new file mode 100644
index 0000000000000000000000000000000000000000..5d686fb18d4e1b61749e0cff285898c0fc99fc6e
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_json.d
@@ -0,0 +1,252 @@
+@system unittest
+{
+    import std.json;
+
+    import std.conv : to;
+
+    // parse a file or string of json into a usable structure
+    string s = `{ "language": "D", "rating": 3.5, "code": "42" }`;
+    JSONValue j = parseJSON(s);
+    // j and j["language"] return JSONValue,
+    // j["language"].str returns a string
+    assert(j["language"].str == "D");
+    assert(j["rating"].floating == 3.5);
+
+    // check a type
+    long x;
+    if (const(JSONValue)* code = "code" in j)
+    {
+        if (code.type() == JSONType.integer)
+            x = code.integer;
+        else
+            x = to!int(code.str);
+    }
+
+    // create a json struct
+    JSONValue jj = [ "language": "D" ];
+    // rating doesnt exist yet, so use .object to assign
+    jj.object["rating"] = JSONValue(3.5);
+    // create an array to assign to list
+    jj.object["list"] = JSONValue( ["a", "b", "c"] );
+    // list already exists, so .object optional
+    jj["list"].array ~= JSONValue("D");
+
+    string jjStr = `{"language":"D","list":["a","b","c","D"],"rating":3.5}`;
+    assert(jj.toString == jjStr);
+}
+
+@safe unittest
+{
+    import std.json;
+
+          string s = "{ \"language\": \"D\" }";
+          JSONValue j = parseJSON(s);
+          assert(j.type == JSONType.object);
+          assert(j["language"].type == JSONType.string);
+    
+}
+
+@safe unittest
+{
+    import std.json;
+
+        JSONValue j = [ "language": "D" ];
+
+        // get value
+        assert(j["language"].str == "D");
+
+        // change existing key to new string
+        j["language"].str = "Perl";
+        assert(j["language"].str == "Perl");
+    
+}
+
+@safe unittest
+{
+    import std.json;
+
+        JSONValue j = true;
+        assert(j.boolean == true);
+
+        j.boolean = false;
+        assert(j.boolean == false);
+
+        j.integer = 12;
+        import std.exception : assertThrown;
+        assertThrown!JSONException(j.boolean);
+    
+}
+
+@safe unittest
+{
+    import std.json;
+
+        import std.exception;
+        import std.conv;
+        string s =
+        `{
+            "a": 123,
+            "b": 3.1415,
+            "c": "text",
+            "d": true,
+            "e": [1, 2, 3],
+            "f": { "a": 1 },
+            "g": -45,
+            "h": ` ~ ulong.max.to!string ~ `,
+         }`;
+
+        struct a { }
+
+        immutable json = parseJSON(s);
+        assert(json["a"].get!double == 123.0);
+        assert(json["a"].get!int == 123);
+        assert(json["a"].get!uint == 123);
+        assert(json["b"].get!double == 3.1415);
+        assertThrown!JSONException(json["b"].get!int);
+        assert(json["c"].get!string == "text");
+        assert(json["d"].get!bool == true);
+        assertNotThrown(json["e"].get!(JSONValue[]));
+        assertNotThrown(json["f"].get!(JSONValue[string]));
+        static assert(!__traits(compiles, json["a"].get!a));
+        assertThrown!JSONException(json["e"].get!float);
+        assertThrown!JSONException(json["d"].get!(JSONValue[string]));
+        assertThrown!JSONException(json["f"].get!(JSONValue[]));
+        assert(json["g"].get!int == -45);
+        assertThrown!ConvException(json["g"].get!uint);
+        assert(json["h"].get!ulong == ulong.max);
+        assertThrown!ConvException(json["h"].get!uint);
+        assertNotThrown(json["h"].get!float);
+    
+}
+
+@safe unittest
+{
+    import std.json;
+
+        JSONValue j = JSONValue( "a string" );
+        j = JSONValue(42);
+
+        j = JSONValue( [1, 2, 3] );
+        assert(j.type == JSONType.array);
+
+        j = JSONValue( ["language": "D"] );
+        assert(j.type == JSONType.object);
+    
+}
+
+@system unittest
+{
+    import std.json;
+
+        JSONValue obj1 = JSONValue.emptyObject;
+        assert(obj1.type == JSONType.object);
+        obj1.object["a"] = JSONValue(1);
+        assert(obj1.object["a"] == JSONValue(1));
+
+        JSONValue obj2 = JSONValue.emptyObject;
+        assert("a" !in obj2.object);
+        obj2.object["b"] = JSONValue(5);
+        assert(obj1 != obj2);
+    
+}
+
+@system unittest
+{
+    import std.json;
+
+        JSONValue obj = JSONValue.emptyOrderedObject;
+        assert(obj.type == JSONType.object);
+        assert(obj.isOrdered);
+        obj["b"] = JSONValue(2);
+        obj["a"] = JSONValue(1);
+        assert(obj["a"] == JSONValue(1));
+        assert(obj["b"] == JSONValue(2));
+
+        string[] keys;
+        foreach (string k, JSONValue v; obj)
+            keys ~= k;
+        assert(keys == ["b", "a"]);
+    
+}
+
+@system unittest
+{
+    import std.json;
+
+        JSONValue arr1 = JSONValue.emptyArray;
+        assert(arr1.type == JSONType.array);
+        assert(arr1.array.length == 0);
+        arr1.array ~= JSONValue("Hello");
+        assert(arr1.array.length == 1);
+        assert(arr1.array[0] == JSONValue("Hello"));
+
+        JSONValue arr2 = JSONValue.emptyArray;
+        assert(arr2.array.length == 0);
+        assert(arr1 != arr2);
+    
+}
+
+@safe unittest
+{
+    import std.json;
+
+        JSONValue j = JSONValue( [42, 43, 44] );
+        assert( j[0].integer == 42 );
+        assert( j[1].integer == 43 );
+    
+}
+
+@safe unittest
+{
+    import std.json;
+
+        JSONValue j = JSONValue( ["language": "D"] );
+        assert( j["language"].str == "D" );
+    
+}
+
+@safe unittest
+{
+    import std.json;
+
+            JSONValue j = JSONValue( ["language": "D"] );
+            j["language"].str = "Perl";
+            assert( j["language"].str == "Perl" );
+    
+}
+
+@safe unittest
+{
+    import std.json;
+
+            JSONValue j = JSONValue( ["Perl", "C"] );
+            j[1].str = "D";
+            assert( j[1].str == "D" );
+    
+}
+
+@safe unittest
+{
+    import std.json;
+
+        JSONValue j = [ "language": "D", "author": "walter" ];
+        string a = ("author" in j).str;
+        *("author" in j) = "Walter";
+        assert(j["author"].str == "Walter");
+    
+}
+
+@safe unittest
+{
+    import std.json;
+
+        assert(JSONValue(10).opEquals(JSONValue(10.0)));
+        assert(JSONValue(10) != (JSONValue(10.5)));
+
+        assert(JSONValue(1) != JSONValue(true));
+        assert(JSONValue.emptyArray != JSONValue.emptyObject);
+
+        assert(parseJSON(`{"a": 1, "b": 2}`).opEquals(parseJSON(`{"b": 2, "a": 1}`)));
+    
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_logger_core.d b/libphobos/testsuite/libphobos.phobos/std_logger_core.d
new file mode 100644
index 0000000000000000000000000000000000000000..f8206db199ae673bba1546c76512db3d59192984
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_logger_core.d
@@ -0,0 +1,22 @@
+@safe unittest
+{
+    import std.logger.core;
+
+    auto nl1 = new StdForwardLogger(LogLevel.all);
+}
+
+@system unittest
+{
+    import std.logger.core;
+
+    import std.logger.filelogger : FileLogger;
+    import std.file : deleteme, remove;
+    Logger l = stdThreadLocalLog;
+    stdThreadLocalLog = new FileLogger(deleteme ~ "-someFile.log");
+    scope(exit) remove(deleteme ~ "-someFile.log");
+
+    auto tempLog = stdThreadLocalLog;
+    stdThreadLocalLog = l;
+    destroy(tempLog);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_logger_nulllogger.d b/libphobos/testsuite/libphobos.phobos/std_logger_nulllogger.d
new file mode 100644
index 0000000000000000000000000000000000000000..6c4444312f9079c11d5fbced692ca55cf613a03c
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_logger_nulllogger.d
@@ -0,0 +1,10 @@
+@safe unittest
+{
+    import std.logger.nulllogger;
+
+    import std.logger.core : LogLevel;
+    auto nl1 = new NullLogger(LogLevel.all);
+    nl1.info("You will never read this.");
+    nl1.fatal("You will never read this, either and it will not throw");
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_math_algebraic.d b/libphobos/testsuite/libphobos.phobos/std_math_algebraic.d
new file mode 100644
index 0000000000000000000000000000000000000000..7b20a13ca26e02017b028cc48bdd5a0ab70cac5f
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_math_algebraic.d
@@ -0,0 +1,163 @@
+@safe pure nothrow @nogc unittest
+{
+    import std.math.algebraic;
+
+    import std.math.traits : isIdentical, isNaN;
+
+    assert(isIdentical(abs(-0.0L), 0.0L));
+    assert(isNaN(abs(real.nan)));
+    assert(abs(-real.infinity) == real.infinity);
+    assert(abs(-56) == 56);
+    assert(abs(2321312L)  == 2321312L);
+    assert(abs(23u) == 23u);
+}
+
+@safe unittest
+{
+    import std.math.algebraic;
+
+    import std.math.traits : isIdentical;
+
+    assert(isIdentical(fabs(0.0f), 0.0f));
+    assert(isIdentical(fabs(-0.0f), 0.0f));
+    assert(fabs(-10.0f) == 10.0f);
+
+    assert(isIdentical(fabs(0.0), 0.0));
+    assert(isIdentical(fabs(-0.0), 0.0));
+    assert(fabs(-10.0) == 10.0);
+
+    assert(isIdentical(fabs(0.0L), 0.0L));
+    assert(isIdentical(fabs(-0.0L), 0.0L));
+    assert(fabs(-10.0L) == 10.0L);
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.algebraic;
+
+    import std.math.operations : feqrel;
+    import std.math.traits : isNaN;
+
+    assert(sqrt(2.0).feqrel(1.4142) > 16);
+    assert(sqrt(9.0).feqrel(3.0) > 16);
+
+    assert(isNaN(sqrt(-1.0f)));
+    assert(isNaN(sqrt(-1.0)));
+    assert(isNaN(sqrt(-1.0L)));
+}
+
+@safe unittest
+{
+    import std.math.algebraic;
+
+    import std.math.operations : feqrel;
+
+    assert(cbrt(1.0).feqrel(1.0) > 16);
+    assert(cbrt(27.0).feqrel(3.0) > 16);
+    assert(cbrt(15.625).feqrel(2.5) > 16);
+}
+
+@safe unittest
+{
+    import std.math.algebraic;
+
+    import std.math.operations : feqrel;
+
+    assert(hypot(1.0, 1.0).feqrel(1.4142) > 16);
+    assert(hypot(3.0, 4.0).feqrel(5.0) > 16);
+    assert(hypot(real.infinity, 1.0L) == real.infinity);
+    assert(hypot(real.infinity, real.nan) == real.infinity);
+}
+
+@safe unittest
+{
+    import std.math.algebraic;
+
+    import std.math.operations : isClose;
+
+    assert(isClose(hypot(1.0, 2.0, 2.0), 3.0));
+    assert(isClose(hypot(2.0, 3.0, 6.0), 7.0));
+    assert(isClose(hypot(1.0, 4.0, 8.0), 9.0));
+}
+
+@safe nothrow @nogc unittest
+{
+    import std.math.algebraic;
+
+    real x = 3.1L;
+    static real[] pp = [56.1L, 32.7L, 6];
+
+    assert(poly(x, pp) == (56.1L + (32.7L + 6.0L * x) * x));
+}
+
+@safe @nogc pure nothrow unittest
+{
+    import std.math.algebraic;
+
+    assert(nextPow2(2) == 4);
+    assert(nextPow2(10) == 16);
+    assert(nextPow2(4000) == 4096);
+
+    assert(nextPow2(-2) == -4);
+    assert(nextPow2(-10) == -16);
+
+    assert(nextPow2(uint.max) == 0);
+    assert(nextPow2(uint.min) == 0);
+    assert(nextPow2(size_t.max) == 0);
+    assert(nextPow2(size_t.min) == 0);
+
+    assert(nextPow2(int.max) == 0);
+    assert(nextPow2(int.min) == 0);
+    assert(nextPow2(long.max) == 0);
+    assert(nextPow2(long.min) == 0);
+}
+
+@safe @nogc pure nothrow unittest
+{
+    import std.math.algebraic;
+
+    assert(nextPow2(2.1) == 4.0);
+    assert(nextPow2(-2.0) == -4.0);
+    assert(nextPow2(0.25) == 0.5);
+    assert(nextPow2(-4.0) == -8.0);
+
+    assert(nextPow2(double.max) == 0.0);
+    assert(nextPow2(double.infinity) == double.infinity);
+}
+
+@safe @nogc pure nothrow unittest
+{
+    import std.math.algebraic;
+
+    assert(truncPow2(3) == 2);
+    assert(truncPow2(4) == 4);
+    assert(truncPow2(10) == 8);
+    assert(truncPow2(4000) == 2048);
+
+    assert(truncPow2(-5) == -4);
+    assert(truncPow2(-20) == -16);
+
+    assert(truncPow2(uint.max) == int.max + 1);
+    assert(truncPow2(uint.min) == 0);
+    assert(truncPow2(ulong.max) == long.max + 1);
+    assert(truncPow2(ulong.min) == 0);
+
+    assert(truncPow2(int.max) == (int.max / 2) + 1);
+    assert(truncPow2(int.min) == int.min);
+    assert(truncPow2(long.max) == (long.max / 2) + 1);
+    assert(truncPow2(long.min) == long.min);
+}
+
+@safe @nogc pure nothrow unittest
+{
+    import std.math.algebraic;
+
+    assert(truncPow2(2.1) == 2.0);
+    assert(truncPow2(7.0) == 4.0);
+    assert(truncPow2(-1.9) == -1.0);
+    assert(truncPow2(0.24) == 0.125);
+    assert(truncPow2(-7.0) == -4.0);
+
+    assert(truncPow2(double.infinity) == double.infinity);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_math_exponential.d b/libphobos/testsuite/libphobos.phobos/std_math_exponential.d
new file mode 100644
index 0000000000000000000000000000000000000000..3ba0a828fa2ba6e0344f8bd4627d17a050138da9
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_math_exponential.d
@@ -0,0 +1,279 @@
+@safe pure nothrow @nogc unittest
+{
+    import std.math.exponential;
+
+    import std.math.operations : feqrel;
+
+    assert(pow(2.0, 5) == 32.0);
+    assert(pow(1.5, 9).feqrel(38.4433) > 16);
+    assert(pow(real.nan, 2) is real.nan);
+    assert(pow(real.infinity, 2) == real.infinity);
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.exponential;
+
+    assert(pow(2, 3) == 8);
+    assert(pow(3, 2) == 9);
+
+    assert(pow(2, 10) == 1_024);
+    assert(pow(2, 20) == 1_048_576);
+    assert(pow(2, 30) == 1_073_741_824);
+
+    assert(pow(0, 0) == 1);
+
+    assert(pow(1, -5) == 1);
+    assert(pow(1, -6) == 1);
+    assert(pow(-1, -5) == -1);
+    assert(pow(-1, -6) == 1);
+
+    assert(pow(-2, 5) == -32);
+    assert(pow(-2, -5) == 0);
+    assert(pow(cast(double) -2, -5) == -0.03125);
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.exponential;
+
+    assert(pow(2, 5.0) == 32.0);
+    assert(pow(7, 3.0) == 343.0);
+    assert(pow(2, real.nan) is real.nan);
+    assert(pow(2, real.infinity) == real.infinity);
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.exponential;
+
+    import std.math.operations : isClose;
+
+    assert(isClose(pow(2.0, 3.0), 8.0));
+    assert(isClose(pow(1.5, 10.0), 57.6650390625));
+
+    // square root of 9
+    assert(isClose(pow(9.0, 0.5), 3.0));
+    // 10th root of 1024
+    assert(isClose(pow(1024.0, 0.1), 2.0));
+
+    assert(isClose(pow(-4.0, 3.0), -64.0));
+
+    // reciprocal of 4 ^^ 2
+    assert(isClose(pow(4.0, -2.0), 0.0625));
+    // reciprocal of (-2) ^^ 3
+    assert(isClose(pow(-2.0, -3.0), -0.125));
+
+    assert(isClose(pow(-2.5, 3.0), -15.625));
+    // reciprocal of 2.5 ^^ 3
+    assert(isClose(pow(2.5, -3.0), 0.064));
+    // reciprocal of (-2.5) ^^ 3
+    assert(isClose(pow(-2.5, -3.0), -0.064));
+
+    // reciprocal of square root of 4
+    assert(isClose(pow(4.0, -0.5), 0.5));
+
+    // per definition
+    assert(isClose(pow(0.0, 0.0), 1.0));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.exponential;
+
+    import std.math.operations : isClose;
+
+    // the result is a complex number
+    // which cannot be represented as floating point number
+    import std.math.traits : isNaN;
+    assert(isNaN(pow(-2.5, -1.5)));
+
+    // use the ^^-operator of std.complex instead
+    import std.complex : complex;
+    auto c1 = complex(-2.5, 0.0);
+    auto c2 = complex(-1.5, 0.0);
+    auto result = c1 ^^ c2;
+    // exact result apparently depends on `real` precision => increased tolerance
+    assert(isClose(result.re, -4.64705438e-17, 2e-4));
+    assert(isClose(result.im, 2.52982e-1, 2e-4));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.exponential;
+
+    assert(powmod(1U, 10U, 3U) == 1);
+    assert(powmod(3U, 2U, 6U) == 3);
+    assert(powmod(5U, 5U, 15U) == 5);
+    assert(powmod(2U, 3U, 5U) == 3);
+    assert(powmod(2U, 4U, 5U) == 1);
+    assert(powmod(2U, 5U, 5U) == 2);
+}
+
+@safe unittest
+{
+    import std.math.exponential;
+
+    import std.math.operations : feqrel;
+    import std.math.constants : E;
+
+    assert(exp(0.0) == 1.0);
+    assert(exp(3.0).feqrel(E * E * E) > 16);
+}
+
+@safe unittest
+{
+    import std.math.exponential;
+
+    import std.math.traits : isIdentical;
+    import std.math.operations : feqrel;
+
+    assert(isIdentical(expm1(0.0), 0.0));
+    assert(expm1(1.0).feqrel(1.71828) > 16);
+    assert(expm1(2.0).feqrel(6.3890) > 16);
+}
+
+@safe unittest
+{
+    import std.math.exponential;
+
+    import std.math.traits : isIdentical;
+    import std.math.operations : feqrel;
+
+    assert(isIdentical(exp2(0.0), 1.0));
+    assert(exp2(2.0).feqrel(4.0) > 16);
+    assert(exp2(8.0).feqrel(256.0) > 16);
+}
+
+@safe unittest
+{
+    import std.math.exponential;
+
+    import std.math.operations : isClose;
+
+    int exp;
+    real mantissa = frexp(123.456L, exp);
+
+    assert(isClose(mantissa * pow(2.0L, cast(real) exp), 123.456L));
+
+    assert(frexp(-real.nan, exp) && exp == int.min);
+    assert(frexp(real.nan, exp) && exp == int.min);
+    assert(frexp(-real.infinity, exp) == -real.infinity && exp == int.min);
+    assert(frexp(real.infinity, exp) == real.infinity && exp == int.max);
+    assert(frexp(-0.0, exp) == -0.0 && exp == 0);
+    assert(frexp(0.0, exp) == 0.0 && exp == 0);
+}
+
+@safe pure unittest
+{
+    import std.math.exponential;
+
+    assert(ilogb(1) == 0);
+    assert(ilogb(3) == 1);
+    assert(ilogb(3.0) == 1);
+    assert(ilogb(100_000_000) == 26);
+
+    assert(ilogb(0) == FP_ILOGB0);
+    assert(ilogb(0.0) == FP_ILOGB0);
+    assert(ilogb(double.nan) == FP_ILOGBNAN);
+    assert(ilogb(double.infinity) == int.max);
+}
+
+@safe pure unittest
+{
+    import std.math.exponential;
+
+    assert(ilogb(0) == FP_ILOGB0);
+    assert(ilogb(0.0) == FP_ILOGB0);
+    assert(ilogb(double.nan) == FP_ILOGBNAN);
+}
+
+@nogc @safe pure nothrow unittest
+{
+    import std.math.exponential;
+
+    import std.meta : AliasSeq;
+    static foreach (T; AliasSeq!(float, double, real))
+    {{
+        T r;
+
+        r = ldexp(3.0L, 3);
+        assert(r == 24);
+
+        r = ldexp(cast(T) 3.0, cast(int) 3);
+        assert(r == 24);
+
+        T n = 3.0;
+        int exp = 3;
+        r = ldexp(n, exp);
+        assert(r == 24);
+    }}
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.exponential;
+
+    import std.math.operations : feqrel;
+    import std.math.constants : E;
+
+    assert(feqrel(log(E), 1) >= real.mant_dig - 1);
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.exponential;
+
+    import std.math.algebraic : fabs;
+
+    assert(fabs(log10(1000.0L) - 3) < .000001);
+}
+
+@safe pure unittest
+{
+    import std.math.exponential;
+
+    import std.math.traits : isIdentical, isNaN;
+    import std.math.operations : feqrel;
+
+    assert(isIdentical(log1p(0.0), 0.0));
+    assert(log1p(1.0).feqrel(0.69314) > 16);
+
+    assert(log1p(-1.0) == -real.infinity);
+    assert(isNaN(log1p(-2.0)));
+    assert(log1p(real.nan) is real.nan);
+    assert(log1p(-real.nan) is -real.nan);
+    assert(log1p(real.infinity) == real.infinity);
+}
+
+@safe unittest
+{
+    import std.math.exponential;
+
+    import std.math.operations : isClose;
+
+    assert(isClose(log2(1024.0L), 10));
+}
+
+@safe @nogc nothrow unittest
+{
+    import std.math.exponential;
+
+    assert(logb(1.0) == 0);
+    assert(logb(100.0) == 6);
+
+    assert(logb(0.0) == -real.infinity);
+    assert(logb(real.infinity) == real.infinity);
+    assert(logb(-real.infinity) == real.infinity);
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.exponential;
+
+    assert(scalbn(0x1.2345678abcdefp0L, 999) == 0x1.2345678abcdefp999L);
+    assert(scalbn(-real.infinity, 5) == -real.infinity);
+    assert(scalbn(2.0,10) == 2048.0);
+    assert(scalbn(2048.0f,-10) == 2.0f);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_math_hardware.d b/libphobos/testsuite/libphobos.phobos/std_math_hardware.d
new file mode 100644
index 0000000000000000000000000000000000000000..c88f59f104195c2a64dec4eef67db1f4d47afab1
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_math_hardware.d
@@ -0,0 +1,18 @@
+@safe unittest
+{
+    import std.math.hardware;
+
+    import std.math.rounding : lrint;
+
+    FloatingPointControl fpctrl;
+
+    fpctrl.rounding = FloatingPointControl.roundDown;
+    assert(lrint(1.5) == 1.0);
+
+    fpctrl.rounding = FloatingPointControl.roundUp;
+    assert(lrint(1.4) == 2.0);
+
+    fpctrl.rounding = FloatingPointControl.roundToNearest;
+    assert(lrint(1.5) == 2.0);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_math_operations.d b/libphobos/testsuite/libphobos.phobos/std_math_operations.d
new file mode 100644
index 0000000000000000000000000000000000000000..b0678f10c364de9002fcedf90263ebe0eea0dd99
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_math_operations.d
@@ -0,0 +1,217 @@
+@safe @nogc pure nothrow unittest
+{
+    import std.math.operations;
+
+    import std.math.traits : isNaN;
+
+    real a = NaN(1_000_000);
+    assert(isNaN(a));
+    assert(getNaNPayload(a) == 1_000_000);
+}
+
+@safe @nogc pure nothrow unittest
+{
+    import std.math.operations;
+
+    import std.math.traits : isNaN;
+
+    real a = NaN(1_000_000);
+    assert(isNaN(a));
+    assert(getNaNPayload(a) == 1_000_000);
+}
+
+@safe @nogc pure nothrow unittest
+{
+    import std.math.operations;
+
+    assert(nextUp(1.0 - 1.0e-6).feqrel(0.999999) > 16);
+    assert(nextUp(1.0 - real.epsilon).feqrel(1.0) > 16);
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.operations;
+
+    assert( nextDown(1.0 + real.epsilon) == 1.0);
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.operations;
+
+    import std.math.traits : isNaN;
+
+    float a = 1;
+    assert(is(typeof(nextafter(a, a)) == float));
+    assert(nextafter(a, a.infinity) > a);
+    assert(isNaN(nextafter(a, a.nan)));
+    assert(isNaN(nextafter(a.nan, a)));
+
+    double b = 2;
+    assert(is(typeof(nextafter(b, b)) == double));
+    assert(nextafter(b, b.infinity) > b);
+    assert(isNaN(nextafter(b, b.nan)));
+    assert(isNaN(nextafter(b.nan, b)));
+
+    real c = 3;
+    assert(is(typeof(nextafter(c, c)) == real));
+    assert(nextafter(c, c.infinity) > c);
+    assert(isNaN(nextafter(c, c.nan)));
+    assert(isNaN(nextafter(c.nan, c)));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.operations;
+
+    import std.math.traits : isNaN;
+
+    assert(fdim(2.0, 0.0) == 2.0);
+    assert(fdim(-2.0, 0.0) == 0.0);
+    assert(fdim(real.infinity, 2.0) == real.infinity);
+    assert(isNaN(fdim(real.nan, 2.0)));
+    assert(isNaN(fdim(2.0, real.nan)));
+    assert(isNaN(fdim(real.nan, real.nan)));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.operations;
+
+    import std.meta : AliasSeq;
+    static foreach (F; AliasSeq!(float, double, real))
+    {
+        assert(fmax(F(0.0), F(2.0)) == 2.0);
+        assert(fmax(F(-2.0), 0.0) == F(0.0));
+        assert(fmax(F.infinity, F(2.0)) == F.infinity);
+        assert(fmax(F.nan, F(2.0)) == F(2.0));
+        assert(fmax(F(2.0), F.nan) == F(2.0));
+    }
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.operations;
+
+    import std.meta : AliasSeq;
+    static foreach (F; AliasSeq!(float, double, real))
+    {
+        assert(fmin(F(0.0), F(2.0)) == 0.0);
+        assert(fmin(F(-2.0), F(0.0)) == -2.0);
+        assert(fmin(F.infinity, F(2.0)) == 2.0);
+        assert(fmin(F.nan, F(2.0)) == 2.0);
+        assert(fmin(F(2.0), F.nan) == 2.0);
+    }
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.operations;
+
+    assert(fma(0.0, 2.0, 2.0) == 2.0);
+    assert(fma(2.0, 2.0, 2.0) == 6.0);
+    assert(fma(real.infinity, 2.0, 2.0) == real.infinity);
+    assert(fma(real.nan, 2.0, 2.0) is real.nan);
+    assert(fma(2.0, 2.0, real.nan) is real.nan);
+}
+
+@safe pure unittest
+{
+    import std.math.operations;
+
+    assert(feqrel(2.0, 2.0) == 53);
+    assert(feqrel(2.0f, 2.0f) == 24);
+    assert(feqrel(2.0, double.nan) == 0);
+
+    // Test that numbers are within n digits of each
+    // other by testing if feqrel > n * log2(10)
+
+    // five digits
+    assert(feqrel(2.0, 2.00001) > 16);
+    // ten digits
+    assert(feqrel(2.0, 2.00000000001) > 33);
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.operations;
+
+    assert(isClose(1.0,0.999_999_999));
+    assert(isClose(0.001, 0.000_999_999_999));
+    assert(isClose(1_000_000_000.0,999_999_999.0));
+
+    assert(isClose(17.123_456_789, 17.123_456_78));
+    assert(!isClose(17.123_456_789, 17.123_45));
+
+    // use explicit 3rd parameter for less (or more) accuracy
+    assert(isClose(17.123_456_789, 17.123_45, 1e-6));
+    assert(!isClose(17.123_456_789, 17.123_45, 1e-7));
+
+    // use 4th parameter when comparing close to zero
+    assert(!isClose(1e-100, 0.0));
+    assert(isClose(1e-100, 0.0, 0.0, 1e-90));
+    assert(!isClose(1e-10, -1e-10));
+    assert(isClose(1e-10, -1e-10, 0.0, 1e-9));
+    assert(!isClose(1e-300, 1e-298));
+    assert(isClose(1e-300, 1e-298, 0.0, 1e-200));
+
+    // different default limits for different floating point types
+    assert(isClose(1.0f, 0.999_99f));
+    assert(!isClose(1.0, 0.999_99));
+    static if (real.sizeof > double.sizeof)
+        assert(!isClose(1.0L, 0.999_999_999L));
+}
+
+@safe pure nothrow unittest
+{
+    import std.math.operations;
+
+    assert(isClose([1.0, 2.0, 3.0], [0.999_999_999, 2.000_000_001, 3.0]));
+    assert(!isClose([1.0, 2.0], [0.999_999_999, 2.000_000_001, 3.0]));
+    assert(!isClose([1.0, 2.0, 3.0], [0.999_999_999, 2.000_000_001]));
+
+    assert(isClose([2.0, 1.999_999_999, 2.000_000_001], 2.0));
+    assert(isClose(2.0, [2.0, 1.999_999_999, 2.000_000_001]));
+}
+
+@safe unittest
+{
+    import std.math.operations;
+
+    assert(cmp(-double.infinity, -double.max) < 0);
+    assert(cmp(-double.max, -100.0) < 0);
+    assert(cmp(-100.0, -0.5) < 0);
+    assert(cmp(-0.5, 0.0) < 0);
+    assert(cmp(0.0, 0.5) < 0);
+    assert(cmp(0.5, 100.0) < 0);
+    assert(cmp(100.0, double.max) < 0);
+    assert(cmp(double.max, double.infinity) < 0);
+
+    assert(cmp(1.0, 1.0) == 0);
+}
+
+@safe unittest
+{
+    import std.math.operations;
+
+    assert(cmp(-0.0, +0.0) < 0);
+    assert(cmp(+0.0, -0.0) > 0);
+}
+
+@safe unittest
+{
+    import std.math.operations;
+
+    assert(cmp(-double.nan, -double.infinity) < 0);
+    assert(cmp(double.infinity, double.nan) < 0);
+    assert(cmp(-double.nan, double.nan) < 0);
+}
+
+@safe unittest
+{
+    import std.math.operations;
+
+    assert(cmp(NaN(10), NaN(20)) < 0);
+    assert(cmp(-NaN(20), -NaN(10)) < 0);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_math_remainder.d b/libphobos/testsuite/libphobos.phobos/std_math_remainder.d
new file mode 100644
index 0000000000000000000000000000000000000000..114b5d546f3204823484c0a40bc604dce314c3ad
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_math_remainder.d
@@ -0,0 +1,54 @@
+@safe unittest
+{
+    import std.math.remainder;
+
+    import std.math.operations : feqrel;
+    import std.math.traits : isIdentical, isNaN;
+
+    assert(isIdentical(fmod(0.0, 1.0), 0.0));
+    assert(fmod(5.0, 3.0).feqrel(2.0) > 16);
+    assert(isNaN(fmod(5.0, 0.0)));
+}
+
+@safe unittest
+{
+    import std.math.remainder;
+
+    import std.math.operations : feqrel;
+
+    real frac;
+    real intpart;
+
+    frac = modf(3.14159, intpart);
+    assert(intpart.feqrel(3.0) > 16);
+    assert(frac.feqrel(0.14159) > 16);
+}
+
+@safe @nogc nothrow unittest
+{
+    import std.math.remainder;
+
+    import std.math.operations : feqrel;
+    import std.math.traits : isNaN;
+
+    assert(remainder(5.1, 3.0).feqrel(-0.9) > 16);
+    assert(remainder(-5.1, 3.0).feqrel(0.9) > 16);
+    assert(remainder(0.0, 3.0) == 0.0);
+
+    assert(isNaN(remainder(1.0, 0.0)));
+    assert(isNaN(remainder(-1.0, 0.0)));
+}
+
+@safe @nogc nothrow unittest
+{
+    import std.math.remainder;
+
+    import std.math.operations : feqrel;
+
+    int n;
+
+    assert(remquo(5.1, 3.0, n).feqrel(-0.9) > 16 && n == 2);
+    assert(remquo(-5.1, 3.0, n).feqrel(0.9) > 16 && n == -2);
+    assert(remquo(0.0, 3.0, n) == 0.0 && n == 0);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_math_rounding.d b/libphobos/testsuite/libphobos.phobos/std_math_rounding.d
new file mode 100644
index 0000000000000000000000000000000000000000..8c8704550fd7c5191d931ae38b0758922638e734
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_math_rounding.d
@@ -0,0 +1,171 @@
+@safe pure nothrow @nogc unittest
+{
+    import std.math.rounding;
+
+    import std.math.traits : isNaN;
+
+    assert(ceil(+123.456L) == +124);
+    assert(ceil(-123.456L) == -123);
+    assert(ceil(-1.234L) == -1);
+    assert(ceil(-0.123L) == 0);
+    assert(ceil(0.0L) == 0);
+    assert(ceil(+0.123L) == 1);
+    assert(ceil(+1.234L) == 2);
+    assert(ceil(real.infinity) == real.infinity);
+    assert(isNaN(ceil(real.nan)));
+    assert(isNaN(ceil(real.init)));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.rounding;
+
+    import std.math.traits : isNaN;
+
+    assert(floor(+123.456L) == +123);
+    assert(floor(-123.456L) == -124);
+    assert(floor(+123.0L) == +123);
+    assert(floor(-124.0L) == -124);
+    assert(floor(-1.234L) == -2);
+    assert(floor(-0.123L) == -1);
+    assert(floor(0.0L) == 0);
+    assert(floor(+0.123L) == 0);
+    assert(floor(+1.234L) == 1);
+    assert(floor(real.infinity) == real.infinity);
+    assert(isNaN(floor(real.nan)));
+    assert(isNaN(floor(real.init)));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.rounding;
+
+    import std.math.operations : isClose;
+
+    assert(isClose(12345.6789L.quantize(0.01L), 12345.68L));
+    assert(isClose(12345.6789L.quantize!floor(0.01L), 12345.67L));
+    assert(isClose(12345.6789L.quantize(22.0L), 12342.0L));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.rounding;
+
+    import std.math.operations : isClose;
+    import std.math.traits : isNaN;
+
+    assert(isClose(12345.6789L.quantize(0), 12345.6789L));
+    assert(12345.6789L.quantize(real.infinity).isNaN);
+    assert(12345.6789L.quantize(real.nan).isNaN);
+    assert(real.infinity.quantize(0.01L) == real.infinity);
+    assert(real.infinity.quantize(real.nan).isNaN);
+    assert(real.nan.quantize(0.01L).isNaN);
+    assert(real.nan.quantize(real.infinity).isNaN);
+    assert(real.nan.quantize(real.nan).isNaN);
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.rounding;
+
+    import std.math.operations : isClose;
+
+    assert(isClose(12345.6789L.quantize!10(-2), 12345.68L));
+    assert(isClose(12345.6789L.quantize!(10, -2), 12345.68L));
+    assert(isClose(12345.6789L.quantize!(10, floor)(-2), 12345.67L));
+    assert(isClose(12345.6789L.quantize!(10, -2, floor), 12345.67L));
+
+    assert(isClose(12345.6789L.quantize!22(1), 12342.0L));
+    assert(isClose(12345.6789L.quantize!22, 12342.0L));
+}
+
+@safe pure unittest
+{
+    import std.math.rounding;
+
+    import std.math.traits : isNaN;
+
+    assert(nearbyint(0.4) == 0);
+    assert(nearbyint(0.5) == 0);
+    assert(nearbyint(0.6) == 1);
+    assert(nearbyint(100.0) == 100);
+
+    assert(isNaN(nearbyint(real.nan)));
+    assert(nearbyint(real.infinity) == real.infinity);
+    assert(nearbyint(-real.infinity) == -real.infinity);
+}
+
+@safe unittest
+{
+    import std.math.rounding;
+
+    import std.math.traits : isNaN;
+
+    version (IeeeFlagsSupport) resetIeeeFlags();
+    assert(rint(0.4) == 0);
+    version (GNU) { /* inexact bit not set with enabled optimizations */ } else
+    version (IeeeFlagsSupport) assert(ieeeFlags.inexact);
+
+    assert(rint(0.5) == 0);
+    assert(rint(0.6) == 1);
+    assert(rint(100.0) == 100);
+
+    assert(isNaN(rint(real.nan)));
+    assert(rint(real.infinity) == real.infinity);
+    assert(rint(-real.infinity) == -real.infinity);
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.rounding;
+
+    assert(lrint(4.5) == 4);
+    assert(lrint(5.5) == 6);
+    assert(lrint(-4.5) == -4);
+    assert(lrint(-5.5) == -6);
+
+    assert(lrint(int.max - 0.5) == 2147483646L);
+    assert(lrint(int.max + 0.5) == 2147483648L);
+    assert(lrint(int.min - 0.5) == -2147483648L);
+    assert(lrint(int.min + 0.5) == -2147483648L);
+}
+
+@safe nothrow @nogc unittest
+{
+    import std.math.rounding;
+
+    assert(round(4.5) == 5);
+    assert(round(5.4) == 5);
+    assert(round(-4.5) == -5);
+    assert(round(-5.1) == -5);
+}
+
+@safe nothrow @nogc unittest
+{
+    import std.math.rounding;
+
+    assert(lround(0.49) == 0);
+    assert(lround(0.5) == 1);
+    assert(lround(1.5) == 2);
+}
+
+@safe pure unittest
+{
+    import std.math.rounding;
+
+    assert(trunc(0.01) == 0);
+    assert(trunc(0.49) == 0);
+    assert(trunc(0.5) == 0);
+    assert(trunc(1.5) == 1);
+}
+
+@safe unittest
+{
+    import std.math.rounding;
+
+    assert(rndtol(1.0) == 1L);
+    assert(rndtol(1.2) == 1L);
+    assert(rndtol(1.7) == 2L);
+    assert(rndtol(1.0001) == 1L);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_math_traits.d b/libphobos/testsuite/libphobos.phobos/std_math_traits.d
new file mode 100644
index 0000000000000000000000000000000000000000..952de85c98d775f0f6fc86e26bdcc18b6f9322e7
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_math_traits.d
@@ -0,0 +1,189 @@
+@safe pure nothrow @nogc unittest
+{
+    import std.math.traits;
+
+    assert( isNaN(float.init));
+    assert( isNaN(-double.init));
+    assert( isNaN(real.nan));
+    assert( isNaN(-real.nan));
+    assert(!isNaN(cast(float) 53.6));
+    assert(!isNaN(cast(real)-53.6));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.traits;
+
+    assert( isFinite(1.23f));
+    assert( isFinite(float.max));
+    assert( isFinite(float.min_normal));
+    assert(!isFinite(float.nan));
+    assert(!isFinite(float.infinity));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.traits;
+
+    float f = 3;
+    double d = 500;
+    real e = 10e+48;
+
+    assert(isNormal(f));
+    assert(isNormal(d));
+    assert(isNormal(e));
+    f = d = e = 0;
+    assert(!isNormal(f));
+    assert(!isNormal(d));
+    assert(!isNormal(e));
+    assert(!isNormal(real.infinity));
+    assert(isNormal(-real.max));
+    assert(!isNormal(real.min_normal/4));
+
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.traits;
+
+    import std.meta : AliasSeq;
+
+    static foreach (T; AliasSeq!(float, double, real))
+    {{
+        T f;
+        for (f = 1.0; !isSubnormal(f); f /= 2)
+            assert(f != 0);
+    }}
+}
+
+@nogc @safe pure nothrow unittest
+{
+    import std.math.traits;
+
+    assert(!isInfinity(float.init));
+    assert(!isInfinity(-float.init));
+    assert(!isInfinity(float.nan));
+    assert(!isInfinity(-float.nan));
+    assert(isInfinity(float.infinity));
+    assert(isInfinity(-float.infinity));
+    assert(isInfinity(-1.0f / 0.0f));
+}
+
+@safe @nogc pure nothrow unittest
+{
+    import std.math.traits;
+
+    // We're forcing the CTFE to run by assigning the result of the function to an enum
+    enum test1 = isIdentical(1.0,1.0);
+    enum test2 = isIdentical(real.nan,real.nan);
+    enum test3 = isIdentical(real.infinity, real.infinity);
+    enum test4 = isIdentical(real.infinity, real.infinity);
+    enum test5 = isIdentical(0.0, 0.0);
+
+    assert(test1);
+    assert(test2);
+    assert(test3);
+    assert(test4);
+    assert(test5);
+
+    enum test6 = !isIdentical(0.0, -0.0);
+    enum test7 = !isIdentical(real.nan, -real.nan);
+    enum test8 = !isIdentical(real.infinity, -real.infinity);
+
+    assert(test6);
+    assert(test7);
+    assert(test8);
+}
+
+@nogc @safe pure nothrow unittest
+{
+    import std.math.traits;
+
+    assert(!signbit(float.nan));
+    assert(signbit(-float.nan));
+    assert(!signbit(168.1234f));
+    assert(signbit(-168.1234f));
+    assert(!signbit(0.0f));
+    assert(signbit(-0.0f));
+    assert(signbit(-float.max));
+    assert(!signbit(float.max));
+
+    assert(!signbit(double.nan));
+    assert(signbit(-double.nan));
+    assert(!signbit(168.1234));
+    assert(signbit(-168.1234));
+    assert(!signbit(0.0));
+    assert(signbit(-0.0));
+    assert(signbit(-double.max));
+    assert(!signbit(double.max));
+
+    assert(!signbit(real.nan));
+    assert(signbit(-real.nan));
+    assert(!signbit(168.1234L));
+    assert(signbit(-168.1234L));
+    assert(!signbit(0.0L));
+    assert(signbit(-0.0L));
+    assert(signbit(-real.max));
+    assert(!signbit(real.max));
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.traits;
+
+    assert(copysign(1.0, 1.0) == 1.0);
+    assert(copysign(1.0, -0.0) == -1.0);
+    assert(copysign(1UL, -1.0) == -1.0);
+    assert(copysign(-1.0, -1.0) == -1.0);
+
+    assert(copysign(real.infinity, -1.0) == -real.infinity);
+    assert(copysign(real.nan, 1.0) is real.nan);
+    assert(copysign(-real.nan, 1.0) is real.nan);
+    assert(copysign(real.nan, -1.0) is -real.nan);
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.math.traits;
+
+    assert(sgn(168.1234) == 1);
+    assert(sgn(-168.1234) == -1);
+    assert(sgn(0.0) == 0);
+    assert(sgn(-0.0) == 0);
+}
+
+@safe unittest
+{
+    import std.math.traits;
+
+    import std.math.exponential : pow;
+
+    assert( isPowerOf2(1.0L));
+    assert( isPowerOf2(2.0L));
+    assert( isPowerOf2(0.5L));
+    assert( isPowerOf2(pow(2.0L, 96)));
+    assert( isPowerOf2(pow(2.0L, -77)));
+
+    assert(!isPowerOf2(-2.0L));
+    assert(!isPowerOf2(-0.5L));
+    assert(!isPowerOf2(0.0L));
+    assert(!isPowerOf2(4.315));
+    assert(!isPowerOf2(1.0L / 3.0L));
+
+    assert(!isPowerOf2(real.nan));
+    assert(!isPowerOf2(real.infinity));
+}
+
+@safe unittest
+{
+    import std.math.traits;
+
+    assert( isPowerOf2(1));
+    assert( isPowerOf2(2));
+    assert( isPowerOf2(1uL << 63));
+
+    assert(!isPowerOf2(-4));
+    assert(!isPowerOf2(0));
+    assert(!isPowerOf2(1337u));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_math_trigonometry.d b/libphobos/testsuite/libphobos.phobos/std_math_trigonometry.d
new file mode 100644
index 0000000000000000000000000000000000000000..88a43976e968665bb4970ab0cfc229386907d7c5
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_math_trigonometry.d
@@ -0,0 +1,168 @@
+@safe unittest
+{
+    import std.math.trigonometry;
+
+    import std.math.operations : isClose;
+
+    assert(cos(0.0) == 1.0);
+    assert(cos(1.0).isClose(0.5403023059));
+    assert(cos(3.0).isClose(-0.9899924966));
+}
+
+@safe unittest
+{
+    import std.math.trigonometry;
+
+    import std.math.constants : PI;
+    import std.stdio : writefln;
+
+    void someFunc()
+    {
+      real x = 30.0;
+      auto result = sin(x * (PI / 180)); // convert degrees to radians
+      writefln("The sine of %s degrees is %s", x, result);
+    }
+}
+
+@safe unittest
+{
+    import std.math.trigonometry;
+
+    import std.math.operations : isClose;
+    import std.math.traits : isIdentical;
+    import std.math.constants : PI;
+    import std.math.algebraic : sqrt;
+
+    assert(isIdentical(tan(0.0), 0.0));
+    assert(tan(PI).isClose(0, 0.0, 1e-10));
+    assert(tan(PI / 3).isClose(sqrt(3.0)));
+}
+
+@safe unittest
+{
+    import std.math.trigonometry;
+
+    import std.math.operations : isClose;
+    import std.math.traits : isNaN;
+    import std.math.constants : PI;
+
+    assert(acos(0.0).isClose(1.570796327));
+    assert(acos(0.5).isClose(PI / 3));
+    assert(acos(PI).isNaN);
+}
+
+@safe unittest
+{
+    import std.math.trigonometry;
+
+    import std.math.operations : isClose;
+    import std.math.traits : isIdentical, isNaN;
+    import std.math.constants : PI;
+
+    assert(isIdentical(asin(0.0), 0.0));
+    assert(asin(0.5).isClose(PI / 6));
+    assert(asin(PI).isNaN);
+}
+
+@safe unittest
+{
+    import std.math.trigonometry;
+
+    import std.math.operations : isClose;
+    import std.math.traits : isIdentical;
+    import std.math.constants : PI;
+    import std.math.algebraic : sqrt;
+
+    assert(isIdentical(atan(0.0), 0.0));
+    assert(atan(sqrt(3.0)).isClose(PI / 3));
+}
+
+@safe unittest
+{
+    import std.math.trigonometry;
+
+    import std.math.operations : isClose;
+    import std.math.constants : PI;
+    import std.math.algebraic : sqrt;
+
+    assert(atan2(1.0, sqrt(3.0)).isClose(PI / 6));
+}
+
+@safe unittest
+{
+    import std.math.trigonometry;
+
+    import std.math.constants : E;
+    import std.math.operations : isClose;
+
+    assert(cosh(0.0) == 1.0);
+    assert(cosh(1.0).isClose((E + 1.0 / E) / 2));
+}
+
+@safe unittest
+{
+    import std.math.trigonometry;
+
+    import std.math.constants : E;
+    import std.math.operations : isClose;
+    import std.math.traits : isIdentical;
+
+    enum sinh1 = (E - 1.0 / E) / 2;
+    import std.meta : AliasSeq;
+    static foreach (F; AliasSeq!(float, double, real))
+    {
+        assert(isIdentical(sinh(F(0.0)), F(0.0)));
+        assert(sinh(F(1.0)).isClose(F(sinh1)));
+    }
+}
+
+@safe unittest
+{
+    import std.math.trigonometry;
+
+    import std.math.operations : isClose;
+    import std.math.traits : isIdentical;
+
+    assert(isIdentical(tanh(0.0), 0.0));
+    assert(tanh(1.0).isClose(sinh(1.0) / cosh(1.0)));
+}
+
+@safe @nogc nothrow unittest
+{
+    import std.math.trigonometry;
+
+    import std.math.traits : isIdentical, isNaN;
+
+    assert(isNaN(acosh(0.9)));
+    assert(isNaN(acosh(real.nan)));
+    assert(isIdentical(acosh(1.0), 0.0));
+    assert(acosh(real.infinity) == real.infinity);
+    assert(isNaN(acosh(0.5)));
+}
+
+@safe @nogc nothrow unittest
+{
+    import std.math.trigonometry;
+
+    import std.math.traits : isIdentical, isNaN;
+
+    assert(isIdentical(asinh(0.0), 0.0));
+    assert(isIdentical(asinh(-0.0), -0.0));
+    assert(asinh(real.infinity) == real.infinity);
+    assert(asinh(-real.infinity) == -real.infinity);
+    assert(isNaN(asinh(real.nan)));
+}
+
+@safe @nogc nothrow unittest
+{
+    import std.math.trigonometry;
+
+    import std.math.traits : isIdentical, isNaN;
+
+    assert(isIdentical(atanh(0.0), 0.0));
+    assert(isIdentical(atanh(-0.0),-0.0));
+    assert(isNaN(atanh(real.nan)));
+    assert(isNaN(atanh(-real.infinity)));
+    assert(atanh(0.0) == 0);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_meta.d b/libphobos/testsuite/libphobos.phobos/std_meta.d
new file mode 100644
index 0000000000000000000000000000000000000000..d5928462f1eda15f45405a89a153647cfcb5f43c
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_meta.d
@@ -0,0 +1,525 @@
+@safe unittest
+{
+    import std.meta;
+
+    import std.meta;
+    alias TL = AliasSeq!(int, double);
+
+    int foo(TL td)  // same as int foo(int, double);
+    {
+        return td[0] + cast(int) td[1];
+    }
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    alias TL = AliasSeq!(int, double);
+
+    alias Types = AliasSeq!(TL, char);
+    static assert(is(Types == AliasSeq!(int, double, char)));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    // Creates a compile-time sequence of function call expressions
+    // that each call `func` with the next variadic template argument
+    template Map(alias func, args...)
+    {
+        auto ref lazyItem() {return func(args[0]);}
+
+        static if (args.length == 1)
+        {
+            alias Map = lazyItem;
+        }
+        else
+        {
+            // recurse
+            alias Map = AliasSeq!(lazyItem, Map!(func, args[1 .. $]));
+        }
+    }
+
+    static void test(int a, int b)
+    {
+        assert(a == 4);
+        assert(b == 16);
+    }
+
+    static int a = 2;
+    static int b = 4;
+
+    test(Map!(i => i ^^ 2, a, b));
+    assert(a == 2);
+    assert(b == 4);
+
+    test(Map!((ref i) => i *= i, a, b));
+    assert(a == 4);
+    assert(b == 16);
+
+    static void testRef(ref int a, ref int b)
+    {
+        assert(a++ == 16);
+        assert(b++ == 256);
+    }
+
+    testRef(Map!(function ref(ref i) => i *= i, a, b));
+    assert(a == 17);
+    assert(b == 257);
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    // Without Alias this would fail if Args[0] was e.g. a value and
+    // some logic would be needed to detect when to use enum instead
+    alias Head(Args...) = Alias!(Args[0]);
+    alias Tail(Args...) = Args[1 .. $];
+
+    alias Blah = AliasSeq!(3, int, "hello");
+    static assert(Head!Blah == 3);
+    static assert(is(Head!(Tail!Blah) == int));
+    static assert((Tail!Blah)[1] == "hello");
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    alias a = Alias!(123);
+    static assert(a == 123);
+
+    enum abc = 1;
+    alias b = Alias!(abc);
+    static assert(b == 1);
+
+    alias c = Alias!(3 + 4);
+    static assert(c == 7);
+
+    alias concat = (s0, s1) => s0 ~ s1;
+    alias d = Alias!(concat("Hello", " World!"));
+    static assert(d == "Hello World!");
+
+    alias e = Alias!(int);
+    static assert(is(e == int));
+
+    alias f = Alias!(AliasSeq!(int));
+    static assert(!is(typeof(f[0]))); //not an AliasSeq
+    static assert(is(f == int));
+
+    auto g = 6;
+    alias h = Alias!g;
+    ++h;
+    assert(g == 7);
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    import std.stdio;
+
+    void foo()
+    {
+        writefln("The index of long is %s",
+                 staticIndexOf!(long, AliasSeq!(int, long, double)));
+        // prints: The index of long is 1
+    }
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    alias Types = AliasSeq!(int, long, double, char);
+    alias TL = Erase!(long, Types);
+    static assert(is(TL == AliasSeq!(int, double, char)));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    alias Types = AliasSeq!(int, long, long, int);
+    static assert(is(EraseAll!(long, Types) == AliasSeq!(int, int)));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    alias Types = AliasSeq!(int, long, long, int, float);
+
+    alias TL = NoDuplicates!(Types);
+    static assert(is(TL == AliasSeq!(int, long, float)));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    alias Types = AliasSeq!(int, long, long, int, float);
+
+    alias TL = Replace!(long, char, Types);
+    static assert(is(TL == AliasSeq!(int, char, long, int, float)));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    alias Types = AliasSeq!(int, long, long, int, float);
+
+    alias TL = ReplaceAll!(long, char, Types);
+    static assert(is(TL == AliasSeq!(int, char, char, int, float)));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    alias Types = AliasSeq!(int, long, long, int, float, byte, ubyte, short, ushort, uint);
+
+    alias TL = Reverse!(Types);
+    static assert(is(TL == AliasSeq!(uint, ushort, short, ubyte, byte, float, int, long, long, int)));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    class A { }
+    class B : A { }
+    class C : B { }
+    alias Types = AliasSeq!(A, C, B);
+
+    MostDerived!(Object, Types) x;  // x is declared as type C
+    static assert(is(typeof(x) == C));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    class A { }
+    class B : A { }
+    class C : B { }
+    alias Types = AliasSeq!(A, C, B);
+
+    alias TL = DerivedToFront!(Types);
+    static assert(is(TL == AliasSeq!(C, B, A)));
+
+    alias TL2 = DerivedToFront!(A, A, A, B, B, B, C, C, C);
+    static assert(is(TL2 == AliasSeq!(C, C, C, B, B, B, A, A, A)));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    import std.traits : Unqual;
+    alias TL = staticMap!(Unqual, int, const int, immutable int, uint, ubyte, byte, short, ushort);
+    static assert(is(TL == AliasSeq!(int, int, int, uint, ubyte, byte, short, ushort)));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    import std.traits : isIntegral;
+
+    static assert(!allSatisfy!(isIntegral, int, double));
+    static assert( allSatisfy!(isIntegral, int, long));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    import std.traits : isIntegral;
+
+    static assert(!anySatisfy!(isIntegral, string, double));
+    static assert( anySatisfy!(isIntegral, int, double));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    import std.traits : isNarrowString, isUnsigned;
+
+    alias Types1 = AliasSeq!(string, wstring, dchar[], char[], dstring, int);
+    alias TL1 = Filter!(isNarrowString, Types1);
+    static assert(is(TL1 == AliasSeq!(string, wstring, char[])));
+
+    alias Types2 = AliasSeq!(int, byte, ubyte, dstring, dchar, uint, ulong);
+    alias TL2 = Filter!(isUnsigned, Types2);
+    static assert(is(TL2 == AliasSeq!(ubyte, uint, ulong)));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    import std.traits : isPointer;
+
+    alias isNoPointer = templateNot!isPointer;
+    static assert(!isNoPointer!(int*));
+    static assert(allSatisfy!(isNoPointer, string, char, float));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    import std.traits : isNumeric, isUnsigned;
+
+    alias storesNegativeNumbers = templateAnd!(isNumeric, templateNot!isUnsigned);
+    static assert(storesNegativeNumbers!int);
+    static assert(!storesNegativeNumbers!string && !storesNegativeNumbers!uint);
+
+    // An empty sequence of predicates always yields true.
+    alias alwaysTrue = templateAnd!();
+    static assert(alwaysTrue!int);
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    import std.traits : isPointer, isUnsigned;
+
+    alias isPtrOrUnsigned = templateOr!(isPointer, isUnsigned);
+    static assert( isPtrOrUnsigned!uint &&  isPtrOrUnsigned!(short*));
+    static assert(!isPtrOrUnsigned!int  && !isPtrOrUnsigned!(string));
+
+    // An empty sequence of predicates never yields true.
+    alias alwaysFalse = templateOr!();
+    static assert(!alwaysFalse!int);
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    import std.algorithm.iteration : map;
+    import std.algorithm.sorting : sort;
+    import std.string : capitalize;
+
+    struct S
+    {
+        int a;
+        int c;
+        int b;
+    }
+
+    alias capMembers = aliasSeqOf!([__traits(allMembers, S)].sort().map!capitalize());
+    static assert(capMembers[0] == "A");
+    static assert(capMembers[1] == "B");
+    static assert(capMembers[2] == "C");
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    static immutable REF = [0, 1, 2, 3];
+    foreach (I, V; aliasSeqOf!([0, 1, 2, 3]))
+    {
+        static assert(V == I);
+        static assert(V == REF[I]);
+    }
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    // enum bool isImplicitlyConvertible(From, To)
+    import std.traits : isImplicitlyConvertible;
+
+    static assert(allSatisfy!(
+        ApplyLeft!(isImplicitlyConvertible, ubyte),
+        short, ushort, int, uint, long, ulong));
+
+    static assert(is(Filter!(ApplyRight!(isImplicitlyConvertible, short),
+        ubyte, string, short, float, int) == AliasSeq!(ubyte, short)));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    import std.traits : hasMember, ifTestable;
+
+    struct T1
+    {
+        bool foo;
+    }
+
+    struct T2
+    {
+        struct Test
+        {
+            bool opCast(T : bool)() { return true; }
+        }
+
+        Test foo;
+    }
+
+    static assert(allSatisfy!(ApplyRight!(hasMember, "foo"), T1, T2));
+    static assert(allSatisfy!(ApplyRight!(ifTestable, a => a.foo), T1, T2));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    import std.traits : Largest;
+
+    alias Types = AliasSeq!(byte, short, int, long);
+
+    static assert(is(staticMap!(ApplyLeft!(Largest, short), Types) ==
+                AliasSeq!(short, short, int, long)));
+    static assert(is(staticMap!(ApplyLeft!(Largest, int), Types) ==
+                AliasSeq!(int, int, int, long)));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    import std.traits : FunctionAttribute, SetFunctionAttributes;
+
+    static void foo() @system;
+    static int bar(int) @system;
+
+    alias SafeFunctions = AliasSeq!(
+        void function() @safe,
+        int function(int) @safe);
+
+    static assert(is(staticMap!(ApplyRight!(
+        SetFunctionAttributes, "D", FunctionAttribute.safe),
+        typeof(&foo), typeof(&bar)) == SafeFunctions));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    alias ImInt0 = Repeat!(0, int);
+    static assert(is(ImInt0 == AliasSeq!()));
+
+    alias ImInt1 = Repeat!(1, immutable(int));
+    static assert(is(ImInt1 == AliasSeq!(immutable(int))));
+
+    alias Real3 = Repeat!(3, real);
+    static assert(is(Real3 == AliasSeq!(real, real, real)));
+
+    alias Real12 = Repeat!(4, Real3);
+    static assert(is(Real12 == AliasSeq!(real, real, real, real, real, real,
+        real, real, real, real, real, real)));
+
+    alias Composite = AliasSeq!(uint, int);
+    alias Composite2 = Repeat!(2, Composite);
+    static assert(is(Composite2 == AliasSeq!(uint, int, uint, int)));
+
+    alias ImInt10 = Repeat!(10, int);
+    static assert(is(ImInt10 == AliasSeq!(int, int, int, int, int, int, int, int, int, int)));
+
+    alias Big = Repeat!(1_000_000, int);
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    auto staticArray(T, size_t n)(Repeat!(n, T) elems)
+    {
+        T[n] a = [elems];
+        return a;
+    }
+
+    auto a = staticArray!(long, 3)(3, 1, 4);
+    assert(is(typeof(a) == long[3]));
+    assert(a == [3, 1, 4]);
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    alias Nums = AliasSeq!(7, 2, 3, 23);
+    enum Comp(int N1, int N2) = N1 < N2;
+    static assert(AliasSeq!(2, 3, 7, 23) == staticSort!(Comp, Nums));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    alias Types = AliasSeq!(uint, short, ubyte, long, ulong);
+    enum Comp(T1, T2) = __traits(isUnsigned, T2) - __traits(isUnsigned, T1);
+    static assert(is(AliasSeq!(uint, ubyte, ulong, short, long) == staticSort!(Comp,
+        Types)));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    enum Comp(int N1, int N2) = N1 < N2;
+    static assert( staticIsSorted!(Comp, 2, 2));
+    static assert( staticIsSorted!(Comp, 2, 3, 7, 23));
+    static assert(!staticIsSorted!(Comp, 7, 2, 3, 23));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    enum Comp(T1, T2) = __traits(isUnsigned, T2) - __traits(isUnsigned, T1);
+    static assert( staticIsSorted!(Comp, uint, ubyte, ulong, short, long));
+    static assert(!staticIsSorted!(Comp, uint, short, ubyte, long, ulong));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    static assert(is(Stride!(1, short, int, long) == AliasSeq!(short, int, long)));
+    static assert(is(Stride!(2, short, int, long) == AliasSeq!(short, long)));
+    static assert(is(Stride!(-1, short, int, long) == AliasSeq!(long, int, short)));
+    static assert(is(Stride!(-2, short, int, long) == AliasSeq!(long, short)));
+
+    alias attribs = AliasSeq!(short, int, long, ushort, uint, ulong);
+    static assert(is(Stride!(3, attribs) == AliasSeq!(short, ushort)));
+    static assert(is(Stride!(3, attribs[1 .. $]) == AliasSeq!(int, uint)));
+    static assert(is(Stride!(-3, attribs) == AliasSeq!(ulong, long)));
+}
+
+@safe unittest
+{
+    import std.meta;
+
+    // ApplyRight combined with Instantiate can be used to apply various
+    // templates to the same parameters.
+    import std.string : leftJustify, center, rightJustify;
+    alias functions = staticMap!(ApplyRight!(Instantiate, string),
+                                 leftJustify, center, rightJustify);
+    string result = "";
+    static foreach (f; functions)
+    {
+        {
+            auto x = &f; // not a template, but a function instantiation
+            result ~= x("hello", 7);
+            result ~= ";";
+        }
+    }
+
+    assert(result == "hello  ; hello ;  hello;");
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_mmfile.d b/libphobos/testsuite/libphobos.phobos/std_mmfile.d
new file mode 100644
index 0000000000000000000000000000000000000000..73f2291e136db9b9a8ed671bb3494ff6aea2d376
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_mmfile.d
@@ -0,0 +1,43 @@
+@system unittest
+{
+    import std.mmfile;
+
+    import std.file;
+    std.file.write(deleteme, "hello"); // deleteme is a temporary filename
+    scope(exit) remove(deleteme);
+
+    // Use a scope class so the file will be closed at the end of this function
+    scope mmfile = new MmFile(deleteme);
+
+    assert(mmfile.length == "hello".length);
+
+    // Access file contents with the slice operator
+    // This is typed as `void[]`, so cast to `char[]` or `ubyte[]` to use it
+    const data = cast(const(char)[]) mmfile[];
+
+    // At this point, the file content may not have been read yet.
+    // In that case, the following memory access will intentionally
+    // trigger a page fault, causing the kernel to load the file contents
+    assert(data[0 .. 5] == "hello");
+}
+
+@system unittest
+{
+    import std.mmfile;
+
+    import std.file;
+    scope(exit) remove(deleteme);
+
+    scope mmfile = new MmFile(deleteme, MmFile.Mode.readWriteNew, 5, null);
+    assert(mmfile.length == 5);
+
+    auto data = cast(ubyte[]) mmfile[];
+
+    // This write to memory will be reflected in the file contents
+    data[] = '\n';
+
+    mmfile.flush();
+
+    assert(std.file.read(deleteme) == "\n\n\n\n\n");
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_numeric.d b/libphobos/testsuite/libphobos.phobos/std_numeric.d
new file mode 100644
index 0000000000000000000000000000000000000000..e88b5e5b2d5f767de9611271dfd1b632c82b7a34
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_numeric.d
@@ -0,0 +1,203 @@
+@safe unittest
+{
+    import std.numeric;
+
+    import std.math.trigonometry : sin, cos;
+
+    // Define a 16-bit floating point values
+    CustomFloat!16                                x;     // Using the number of bits
+    CustomFloat!(10, 5)                           y;     // Using the precision and exponent width
+    CustomFloat!(10, 5,CustomFloatFlags.ieee)     z;     // Using the precision, exponent width and format flags
+    CustomFloat!(10, 5,CustomFloatFlags.ieee, 15) w;     // Using the precision, exponent width, format flags and exponent offset bias
+
+    // Use the 16-bit floats mostly like normal numbers
+    w = x*y - 1;
+
+    // Functions calls require conversion
+    z = sin(+x)           + cos(+y);                     // Use unary plus to concisely convert to a real
+    z = sin(x.get!float)  + cos(y.get!float);            // Or use get!T
+    z = sin(cast(float) x) + cos(cast(float) y);           // Or use cast(T) to explicitly convert
+
+    // Define a 8-bit custom float for storing probabilities
+    alias Probability = CustomFloat!(4, 4, CustomFloatFlags.ieee^CustomFloatFlags.probability^CustomFloatFlags.signed );
+    auto p = Probability(0.5);
+}
+
+@safe unittest
+{
+    import std.numeric;
+
+    import std.math.operations : isClose;
+
+    // Average numbers in an array
+    double avg(in double[] a)
+    {
+        if (a.length == 0) return 0;
+        FPTemporary!double result = 0;
+        foreach (e; a) result += e;
+        return result / a.length;
+    }
+
+    auto a = [1.0, 2.0, 3.0];
+    assert(isClose(avg(a), 2));
+}
+
+@safe unittest
+{
+    import std.numeric;
+
+    import std.math.operations : isClose;
+    import std.math.trigonometry : cos;
+
+    float f(float x)
+    {
+        return cos(x) - x*x*x;
+    }
+    auto x = secantMethod!(f)(0f, 1f);
+    assert(isClose(x, 0.865474));
+}
+
+@safe unittest
+{
+    import std.numeric;
+
+    import std.math.operations : isClose;
+
+    auto ret = findLocalMin((double x) => (x-4)^^2, -1e7, 1e7);
+    assert(ret.x.isClose(4.0));
+    assert(ret.y.isClose(0.0, 0.0, 1e-10));
+}
+
+@safe unittest
+{
+    import std.numeric;
+
+    double[] a = [];
+    assert(!normalize(a));
+    a = [ 1.0, 3.0 ];
+    assert(normalize(a));
+    assert(a == [ 0.25, 0.75 ]);
+    assert(normalize!(typeof(a))(a, 50)); // a = [12.5, 37.5]
+    a = [ 0.0, 0.0 ];
+    assert(!normalize(a));
+    assert(a == [ 0.5, 0.5 ]);
+}
+
+@safe unittest
+{
+    import std.numeric;
+
+    import std.math.traits : isNaN;
+
+    assert(sumOfLog2s(new double[0]) == 0);
+    assert(sumOfLog2s([0.0L]) == -real.infinity);
+    assert(sumOfLog2s([-0.0L]) == -real.infinity);
+    assert(sumOfLog2s([2.0L]) == 1);
+    assert(sumOfLog2s([-2.0L]).isNaN());
+    assert(sumOfLog2s([real.nan]).isNaN());
+    assert(sumOfLog2s([-real.nan]).isNaN());
+    assert(sumOfLog2s([real.infinity]) == real.infinity);
+    assert(sumOfLog2s([-real.infinity]).isNaN());
+    assert(sumOfLog2s([ 0.25, 0.25, 0.25, 0.125 ]) == -9);
+}
+
+@safe unittest
+{
+    import std.numeric;
+
+    import std.math.operations : isClose;
+
+    double[] p = [ 0.0, 0, 0, 1 ];
+    assert(kullbackLeiblerDivergence(p, p) == 0);
+    double[] p1 = [ 0.25, 0.25, 0.25, 0.25 ];
+    assert(kullbackLeiblerDivergence(p1, p1) == 0);
+    assert(kullbackLeiblerDivergence(p, p1) == 2);
+    assert(kullbackLeiblerDivergence(p1, p) == double.infinity);
+    double[] p2 = [ 0.2, 0.2, 0.2, 0.4 ];
+    assert(isClose(kullbackLeiblerDivergence(p1, p2), 0.0719281, 1e-5));
+    assert(isClose(kullbackLeiblerDivergence(p2, p1), 0.0780719, 1e-5));
+}
+
+@safe unittest
+{
+    import std.numeric;
+
+    import std.math.operations : isClose;
+
+    double[] p = [ 0.0, 0, 0, 1 ];
+    assert(jensenShannonDivergence(p, p) == 0);
+    double[] p1 = [ 0.25, 0.25, 0.25, 0.25 ];
+    assert(jensenShannonDivergence(p1, p1) == 0);
+    assert(isClose(jensenShannonDivergence(p1, p), 0.548795, 1e-5));
+    double[] p2 = [ 0.2, 0.2, 0.2, 0.4 ];
+    assert(isClose(jensenShannonDivergence(p1, p2), 0.0186218, 1e-5));
+    assert(isClose(jensenShannonDivergence(p2, p1), 0.0186218, 1e-5));
+    assert(isClose(jensenShannonDivergence(p2, p1, 0.005), 0.00602366, 1e-5));
+}
+
+@system unittest
+{
+    import std.numeric;
+
+    import std.math.operations : isClose;
+    import std.math.algebraic : sqrt;
+
+    string[] s = ["Hello", "brave", "new", "world"];
+    string[] t = ["Hello", "new", "world"];
+    assert(gapWeightedSimilarity(s, s, 1) == 15);
+    assert(gapWeightedSimilarity(t, t, 1) == 7);
+    assert(gapWeightedSimilarity(s, t, 1) == 7);
+    assert(isClose(gapWeightedSimilarityNormalized(s, t, 1),
+                    7.0 / sqrt(15.0 * 7), 0.01));
+}
+
+@system unittest
+{
+    import std.numeric;
+
+    string[] s = ["Hello", "brave", "new", "world"];
+    string[] t = ["Hello", "new", "world"];
+    auto simIter = gapWeightedSimilarityIncremental(s, t, 1.0);
+    assert(simIter.front == 3); // three 1-length matches
+    simIter.popFront();
+    assert(simIter.front == 3); // three 2-length matches
+    simIter.popFront();
+    assert(simIter.front == 1); // one 3-length match
+    simIter.popFront();
+    assert(simIter.empty);     // no more match
+}
+
+@safe unittest
+{
+    import std.numeric;
+
+    assert(gcd(2 * 5 * 7 * 7, 5 * 7 * 11) == 5 * 7);
+    const int a = 5 * 13 * 23 * 23, b = 13 * 59;
+    assert(gcd(a, b) == 13);
+}
+
+@safe unittest
+{
+    import std.numeric;
+
+    assert(lcm(1, 2) == 2);
+    assert(lcm(3, 4) == 12);
+    assert(lcm(5, 6) == 30);
+}
+
+@safe pure @nogc unittest
+{
+    import std.numeric;
+
+    ubyte[21] fac;
+    size_t idx = decimalToFactorial(2982, fac);
+
+    assert(fac[0] == 4);
+    assert(fac[1] == 0);
+    assert(fac[2] == 4);
+    assert(fac[3] == 1);
+    assert(fac[4] == 0);
+    assert(fac[5] == 0);
+    assert(fac[6] == 0);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_outbuffer.d b/libphobos/testsuite/libphobos.phobos/std_outbuffer.d
new file mode 100644
index 0000000000000000000000000000000000000000..fd0d9b7cf756112e0fd478b35e2e0358f31fde9a
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_outbuffer.d
@@ -0,0 +1,95 @@
+@safe unittest
+{
+    import std.outbuffer;
+
+        OutBuffer buf = new OutBuffer();
+        buf.write(cast(ubyte) 1);
+        buf.align2();
+        assert(buf.toBytes() == "\x01\x00");
+        buf.write(cast(ubyte) 2);
+        buf.align4();
+        assert(buf.toBytes() == "\x01\x00\x02\x00");
+        buf.write(cast(ubyte) 3);
+        buf.alignSize(8);
+        assert(buf.toBytes() == "\x01\x00\x02\x00\x03\x00\x00\x00");
+    
+}
+
+@safe unittest
+{
+    import std.outbuffer;
+
+        OutBuffer buf = new OutBuffer();
+        buf.write(cast(ubyte) 1);
+        buf.align2(0x55);
+        assert(buf.toBytes() == "\x01\x55");
+        buf.write(cast(ubyte) 2);
+        buf.align4(0x55);
+        assert(buf.toBytes() == "\x01\x55\x02\x55");
+        buf.write(cast(ubyte) 3);
+        buf.alignSize(8, 0x55);
+        assert(buf.toBytes() == "\x01\x55\x02\x55\x03\x55\x55\x55");
+    
+}
+
+@safe unittest
+{
+    import std.outbuffer;
+
+        OutBuffer b = new OutBuffer();
+        b.writef("a%sb", 16);
+        assert(b.toString() == "a16b");
+    
+}
+
+@safe unittest
+{
+    import std.outbuffer;
+
+        OutBuffer b = new OutBuffer();
+        b.writef!"a%sb"(16);
+        assert(b.toString() == "a16b");
+    
+}
+
+@safe unittest
+{
+    import std.outbuffer;
+
+        OutBuffer b = new OutBuffer();
+        b.writefln("a%sb", 16);
+        assert(b.toString() == "a16b\n");
+    
+}
+
+@safe unittest
+{
+    import std.outbuffer;
+
+        OutBuffer b = new OutBuffer();
+        b.writefln!"a%sb"(16);
+        assert(b.toString() == "a16b\n");
+    
+}
+
+@safe unittest
+{
+    import std.outbuffer;
+
+    import std.string : cmp;
+
+    OutBuffer buf = new OutBuffer();
+
+    assert(buf.offset == 0);
+    buf.write("hello");
+    buf.write(cast(byte) 0x20);
+    buf.write("world");
+    buf.writef(" %d", 62665);
+    assert(cmp(buf.toString(), "hello world 62665") == 0);
+
+    buf.clear();
+    assert(cmp(buf.toString(), "") == 0);
+    buf.write("New data");
+    assert(cmp(buf.toString(),"New data") == 0);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_package.d b/libphobos/testsuite/libphobos.phobos/std_package.d
new file mode 100644
index 0000000000000000000000000000000000000000..46a4f866484cda49db46bafb05358ae9b76b254f
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_package.d
@@ -0,0 +1,27 @@
+@safe unittest
+{
+    import std;
+
+    import std;
+
+    int len;
+    const r = 6.iota
+              .filter!(a => a % 2) // 1 3 5
+              .map!(a => a * 2) // 2 6 10
+              .tee!(_ => len++)
+              .substitute(6, -6) // 2 -6 10
+              .sum
+              .reverseArgs!format("Sum: %d");
+
+    assert(len == 3);
+    assert(r == "Sum: 6");
+}
+
+@safe unittest
+{
+    import std;
+
+    import std;
+    assert(10.iota.map!(a => pow(2, a)).sum == 1023);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_parallelism.d b/libphobos/testsuite/libphobos.phobos/std_parallelism.d
new file mode 100644
index 0000000000000000000000000000000000000000..18418ac2a43ceb6a17405532245c8da3d655a779
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_parallelism.d
@@ -0,0 +1,37 @@
+@system unittest
+{
+    import std.parallelism;
+
+    import std.algorithm.iteration : map;
+    import std.math.operations : isClose;
+    import std.parallelism : taskPool;
+    import std.range : iota;
+
+    // Parallel reduce can be combined with
+    // std.algorithm.iteration.map to interesting effect.
+    // The following example (thanks to Russel Winder)
+    // calculates pi by quadrature  using
+    // std.algorithm.map and TaskPool.reduce.
+    // getTerm is evaluated in parallel as needed by
+    // TaskPool.reduce.
+    //
+    // Timings on an Intel i5-3450 quad core machine
+    // for n = 1_000_000_000:
+    //
+    // TaskPool.reduce:       1.067 s
+    // std.algorithm.reduce:  4.011 s
+
+    enum n = 1_000_000;
+    enum delta = 1.0 / n;
+
+    alias getTerm = (int i)
+    {
+        immutable x = ( i - 0.5 ) * delta;
+        return delta / ( 1.0 + x * x ) ;
+    };
+
+    immutable pi = 4.0 * taskPool.reduce!"a + b"(n.iota.map!getTerm);
+
+    assert(pi.isClose(3.14159, 1e-5));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_path.d b/libphobos/testsuite/libphobos.phobos/std_path.d
new file mode 100644
index 0000000000000000000000000000000000000000..33ca98755190dfddbe4a8f214bd384a9a7ecf61d
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_path.d
@@ -0,0 +1,611 @@
+@safe pure nothrow @nogc unittest
+{
+    import std.path;
+
+    version (Windows)
+    {
+        assert( '/'.isDirSeparator);
+        assert( '\\'.isDirSeparator);
+    }
+    else
+    {
+        assert( '/'.isDirSeparator);
+        assert(!'\\'.isDirSeparator);
+    }
+}
+
+@safe unittest
+{
+    import std.path;
+
+    assert(baseName!(CaseSensitive.no)("dir/file.EXT", ".ext") == "file");
+    assert(baseName!(CaseSensitive.yes)("dir/file.EXT", ".ext") != "file");
+
+    version (Posix)
+        assert(relativePath!(CaseSensitive.no)("/FOO/bar", "/foo/baz") == "../bar");
+    else
+        assert(relativePath!(CaseSensitive.no)(`c:\FOO\bar`, `c:\foo\baz`) == `..\bar`);
+}
+
+@safe unittest
+{
+    import std.path;
+
+    assert(baseName("dir/file.ext") == "file.ext");
+    assert(baseName("dir/file.ext", ".ext") == "file");
+    assert(baseName("dir/file.ext", ".xyz") == "file.ext");
+    assert(baseName("dir/filename", "name") == "file");
+    assert(baseName("dir/subdir/") == "subdir");
+
+    version (Windows)
+    {
+        assert(baseName(`d:file.ext`) == "file.ext");
+        assert(baseName(`d:\dir\file.ext`) == "file.ext");
+    }
+}
+
+@safe unittest
+{
+    import std.path;
+
+    assert(dirName("") == ".");
+    assert(dirName("file"w) == ".");
+    assert(dirName("dir/"d) == ".");
+    assert(dirName("dir///") == ".");
+    assert(dirName("dir/file"w.dup) == "dir");
+    assert(dirName("dir///file"d.dup) == "dir");
+    assert(dirName("dir/subdir/") == "dir");
+    assert(dirName("/dir/file"w) == "/dir");
+    assert(dirName("/file"d) == "/");
+    assert(dirName("/") == "/");
+    assert(dirName("///") == "/");
+
+    version (Windows)
+    {
+        assert(dirName(`dir\`) == `.`);
+        assert(dirName(`dir\\\`) == `.`);
+        assert(dirName(`dir\file`) == `dir`);
+        assert(dirName(`dir\\\file`) == `dir`);
+        assert(dirName(`dir\subdir\`) == `dir`);
+        assert(dirName(`\dir\file`) == `\dir`);
+        assert(dirName(`\file`) == `\`);
+        assert(dirName(`\`) == `\`);
+        assert(dirName(`\\\`) == `\`);
+        assert(dirName(`d:`) == `d:`);
+        assert(dirName(`d:file`) == `d:`);
+        assert(dirName(`d:\`) == `d:\`);
+        assert(dirName(`d:\file`) == `d:\`);
+        assert(dirName(`d:\dir\file`) == `d:\dir`);
+        assert(dirName(`\\server\share\dir\file`) == `\\server\share\dir`);
+        assert(dirName(`\\server\share\file`) == `\\server\share`);
+        assert(dirName(`\\server\share\`) == `\\server\share`);
+        assert(dirName(`\\server\share`) == `\\server\share`);
+    }
+}
+
+@safe unittest
+{
+    import std.path;
+
+    assert(rootName("") is null);
+    assert(rootName("foo") is null);
+    assert(rootName("/") == "/");
+    assert(rootName("/foo/bar") == "/");
+
+    version (Windows)
+    {
+        assert(rootName("d:foo") is null);
+        assert(rootName(`d:\foo`) == `d:\`);
+        assert(rootName(`\\server\share\foo`) == `\\server\share`);
+        assert(rootName(`\\server\share`) == `\\server\share`);
+    }
+}
+
+@safe unittest
+{
+    import std.path;
+
+    import std.range : empty;
+    version (Posix)  assert(driveName("c:/foo").empty);
+    version (Windows)
+    {
+        assert(driveName(`dir\file`).empty);
+        assert(driveName(`d:file`) == "d:");
+        assert(driveName(`d:\file`) == "d:");
+        assert(driveName("d:") == "d:");
+        assert(driveName(`\\server\share\file`) == `\\server\share`);
+        assert(driveName(`\\server\share\`) == `\\server\share`);
+        assert(driveName(`\\server\share`) == `\\server\share`);
+
+        static assert(driveName(`d:\file`) == "d:");
+    }
+}
+
+@safe unittest
+{
+    import std.path;
+
+    version (Windows)
+    {
+        assert(stripDrive(`d:\dir\file`) == `\dir\file`);
+        assert(stripDrive(`\\server\share\dir\file`) == `\dir\file`);
+    }
+}
+
+@safe unittest
+{
+    import std.path;
+
+    import std.range : empty;
+    assert(extension("file").empty);
+    assert(extension("file.") == ".");
+    assert(extension("file.ext"w) == ".ext");
+    assert(extension("file.ext1.ext2"d) == ".ext2");
+    assert(extension(".foo".dup).empty);
+    assert(extension(".foo.ext"w.dup) == ".ext");
+
+    static assert(extension("file").empty);
+    static assert(extension("file.ext") == ".ext");
+}
+
+@safe unittest
+{
+    import std.path;
+
+    assert(stripExtension("file")           == "file");
+    assert(stripExtension("file.ext")       == "file");
+    assert(stripExtension("file.ext1.ext2") == "file.ext1");
+    assert(stripExtension("file.")          == "file");
+    assert(stripExtension(".file")          == ".file");
+    assert(stripExtension(".file.ext")      == ".file");
+    assert(stripExtension("dir/file.ext")   == "dir/file");
+}
+
+@safe unittest
+{
+    import std.path;
+
+    assert(setExtension("file", "ext") == "file.ext");
+    assert(setExtension("file"w, ".ext"w) == "file.ext");
+    assert(setExtension("file."d, "ext"d) == "file.ext");
+    assert(setExtension("file.", ".ext") == "file.ext");
+    assert(setExtension("file.old"w, "new"w) == "file.new");
+    assert(setExtension("file.old"d, ".new"d) == "file.new");
+}
+
+@safe unittest
+{
+    import std.path;
+
+    import std.array;
+    assert(withExtension("file", "ext").array == "file.ext");
+    assert(withExtension("file"w, ".ext"w).array == "file.ext");
+    assert(withExtension("file.ext"w, ".").array == "file.");
+
+    import std.utf : byChar, byWchar;
+    assert(withExtension("file".byChar, "ext").array == "file.ext");
+    assert(withExtension("file"w.byWchar, ".ext"w).array == "file.ext"w);
+    assert(withExtension("file.ext"w.byWchar, ".").array == "file."w);
+}
+
+@safe unittest
+{
+    import std.path;
+
+    assert(defaultExtension("file", "ext") == "file.ext");
+    assert(defaultExtension("file", ".ext") == "file.ext");
+    assert(defaultExtension("file.", "ext")     == "file.");
+    assert(defaultExtension("file.old", "new") == "file.old");
+    assert(defaultExtension("file.old", ".new") == "file.old");
+}
+
+@safe unittest
+{
+    import std.path;
+
+    import std.array;
+    assert(withDefaultExtension("file", "ext").array == "file.ext");
+    assert(withDefaultExtension("file"w, ".ext").array == "file.ext"w);
+    assert(withDefaultExtension("file.", "ext").array == "file.");
+    assert(withDefaultExtension("file", "").array == "file.");
+
+    import std.utf : byChar, byWchar;
+    assert(withDefaultExtension("file".byChar, "ext").array == "file.ext");
+    assert(withDefaultExtension("file"w.byWchar, ".ext").array == "file.ext"w);
+    assert(withDefaultExtension("file.".byChar, "ext"d).array == "file.");
+    assert(withDefaultExtension("file".byChar, "").array == "file.");
+}
+
+@safe unittest
+{
+    import std.path;
+
+    version (Posix)
+    {
+        assert(buildPath("foo", "bar", "baz") == "foo/bar/baz");
+        assert(buildPath("/foo/", "bar/baz")  == "/foo/bar/baz");
+        assert(buildPath("/foo", "/bar")      == "/bar");
+    }
+
+    version (Windows)
+    {
+        assert(buildPath("foo", "bar", "baz") == `foo\bar\baz`);
+        assert(buildPath(`c:\foo`, `bar\baz`) == `c:\foo\bar\baz`);
+        assert(buildPath("foo", `d:\bar`)     == `d:\bar`);
+        assert(buildPath("foo", `\bar`)       == `\bar`);
+        assert(buildPath(`c:\foo`, `\bar`)    == `c:\bar`);
+    }
+}
+
+@safe unittest
+{
+    import std.path;
+
+    import std.array;
+    version (Posix)
+    {
+        assert(chainPath("foo", "bar", "baz").array == "foo/bar/baz");
+        assert(chainPath("/foo/", "bar/baz").array  == "/foo/bar/baz");
+        assert(chainPath("/foo", "/bar").array      == "/bar");
+    }
+
+    version (Windows)
+    {
+        assert(chainPath("foo", "bar", "baz").array == `foo\bar\baz`);
+        assert(chainPath(`c:\foo`, `bar\baz`).array == `c:\foo\bar\baz`);
+        assert(chainPath("foo", `d:\bar`).array     == `d:\bar`);
+        assert(chainPath("foo", `\bar`).array       == `\bar`);
+        assert(chainPath(`c:\foo`, `\bar`).array    == `c:\bar`);
+    }
+
+    import std.utf : byChar;
+    version (Posix)
+    {
+        assert(chainPath("foo", "bar", "baz").array == "foo/bar/baz");
+        assert(chainPath("/foo/".byChar, "bar/baz").array  == "/foo/bar/baz");
+        assert(chainPath("/foo", "/bar".byChar).array      == "/bar");
+    }
+
+    version (Windows)
+    {
+        assert(chainPath("foo", "bar", "baz").array == `foo\bar\baz`);
+        assert(chainPath(`c:\foo`.byChar, `bar\baz`).array == `c:\foo\bar\baz`);
+        assert(chainPath("foo", `d:\bar`).array     == `d:\bar`);
+        assert(chainPath("foo", `\bar`.byChar).array       == `\bar`);
+        assert(chainPath(`c:\foo`, `\bar`w).array    == `c:\bar`);
+    }
+}
+
+@safe unittest
+{
+    import std.path;
+
+    assert(buildNormalizedPath("foo", "..") == ".");
+
+    version (Posix)
+    {
+        assert(buildNormalizedPath("/foo/./bar/..//baz/") == "/foo/baz");
+        assert(buildNormalizedPath("../foo/.") == "../foo");
+        assert(buildNormalizedPath("/foo", "bar/baz/") == "/foo/bar/baz");
+        assert(buildNormalizedPath("/foo", "/bar/..", "baz") == "/baz");
+        assert(buildNormalizedPath("foo/./bar", "../../", "../baz") == "../baz");
+        assert(buildNormalizedPath("/foo/./bar", "../../baz") == "/baz");
+    }
+
+    version (Windows)
+    {
+        assert(buildNormalizedPath(`c:\foo\.\bar/..\\baz\`) == `c:\foo\baz`);
+        assert(buildNormalizedPath(`..\foo\.`) == `..\foo`);
+        assert(buildNormalizedPath(`c:\foo`, `bar\baz\`) == `c:\foo\bar\baz`);
+        assert(buildNormalizedPath(`c:\foo`, `bar/..`) == `c:\foo`);
+        assert(buildNormalizedPath(`\\server\share\foo`, `..\bar`) ==
+                `\\server\share\bar`);
+    }
+}
+
+@safe unittest
+{
+    import std.path;
+
+    import std.array;
+    assert(asNormalizedPath("foo/..").array == ".");
+
+    version (Posix)
+    {
+        assert(asNormalizedPath("/foo/./bar/..//baz/").array == "/foo/baz");
+        assert(asNormalizedPath("../foo/.").array == "../foo");
+        assert(asNormalizedPath("/foo/bar/baz/").array == "/foo/bar/baz");
+        assert(asNormalizedPath("/foo/./bar/../../baz").array == "/baz");
+    }
+
+    version (Windows)
+    {
+        assert(asNormalizedPath(`c:\foo\.\bar/..\\baz\`).array == `c:\foo\baz`);
+        assert(asNormalizedPath(`..\foo\.`).array == `..\foo`);
+        assert(asNormalizedPath(`c:\foo\bar\baz\`).array == `c:\foo\bar\baz`);
+        assert(asNormalizedPath(`c:\foo\bar/..`).array == `c:\foo`);
+        assert(asNormalizedPath(`\\server\share\foo\..\bar`).array ==
+                `\\server\share\bar`);
+    }
+}
+
+@safe unittest
+{
+    import std.path;
+
+    import std.algorithm.comparison : equal;
+    import std.conv : to;
+
+    assert(equal(pathSplitter("/"), ["/"]));
+    assert(equal(pathSplitter("/foo/bar"), ["/", "foo", "bar"]));
+    assert(equal(pathSplitter("foo/../bar//./"), ["foo", "..", "bar", "."]));
+
+    version (Posix)
+    {
+        assert(equal(pathSplitter("//foo/bar"), ["/", "foo", "bar"]));
+    }
+
+    version (Windows)
+    {
+        assert(equal(pathSplitter(`foo\..\bar\/.\`), ["foo", "..", "bar", "."]));
+        assert(equal(pathSplitter("c:"), ["c:"]));
+        assert(equal(pathSplitter(`c:\foo\bar`), [`c:\`, "foo", "bar"]));
+        assert(equal(pathSplitter(`c:foo\bar`), ["c:foo", "bar"]));
+    }
+}
+
+@safe unittest
+{
+    import std.path;
+
+    version (Posix)
+    {
+        assert( isRooted("/"));
+        assert( isRooted("/foo"));
+        assert(!isRooted("foo"));
+        assert(!isRooted("../foo"));
+    }
+
+    version (Windows)
+    {
+        assert( isRooted(`\`));
+        assert( isRooted(`\foo`));
+        assert( isRooted(`d:\foo`));
+        assert( isRooted(`\\foo\bar`));
+        assert(!isRooted("foo"));
+        assert(!isRooted("d:foo"));
+    }
+}
+
+@safe unittest
+{
+    import std.path;
+
+    version (Posix)
+    {
+        assert(absolutePath("some/file", "/foo/bar")  == "/foo/bar/some/file");
+        assert(absolutePath("../file", "/foo/bar")    == "/foo/bar/../file");
+        assert(absolutePath("/some/file", "/foo/bar") == "/some/file");
+    }
+
+    version (Windows)
+    {
+        assert(absolutePath(`some\file`, `c:\foo\bar`)    == `c:\foo\bar\some\file`);
+        assert(absolutePath(`..\file`, `c:\foo\bar`)      == `c:\foo\bar\..\file`);
+        assert(absolutePath(`c:\some\file`, `c:\foo\bar`) == `c:\some\file`);
+        assert(absolutePath(`\`, `c:\`)                   == `c:\`);
+        assert(absolutePath(`\some\file`, `c:\foo\bar`)   == `c:\some\file`);
+    }
+}
+
+@system unittest
+{
+    import std.path;
+
+    import std.array;
+    assert(asAbsolutePath(cast(string) null).array == "");
+    version (Posix)
+    {
+        assert(asAbsolutePath("/foo").array == "/foo");
+    }
+    version (Windows)
+    {
+        assert(asAbsolutePath("c:/foo").array == "c:/foo");
+    }
+    asAbsolutePath("foo");
+}
+
+@safe unittest
+{
+    import std.path;
+
+    assert(relativePath("foo") == "foo");
+
+    version (Posix)
+    {
+        assert(relativePath("foo", "/bar") == "foo");
+        assert(relativePath("/foo/bar", "/foo/bar") == ".");
+        assert(relativePath("/foo/bar", "/foo/baz") == "../bar");
+        assert(relativePath("/foo/bar/baz", "/foo/woo/wee") == "../../bar/baz");
+        assert(relativePath("/foo/bar/baz", "/foo/bar") == "baz");
+    }
+    version (Windows)
+    {
+        assert(relativePath("foo", `c:\bar`) == "foo");
+        assert(relativePath(`c:\foo\bar`, `c:\foo\bar`) == ".");
+        assert(relativePath(`c:\foo\bar`, `c:\foo\baz`) == `..\bar`);
+        assert(relativePath(`c:\foo\bar\baz`, `c:\foo\woo\wee`) == `..\..\bar\baz`);
+        assert(relativePath(`c:\foo\bar\baz`, `c:\foo\bar`) == "baz");
+        assert(relativePath(`c:\foo\bar`, `d:\foo`) == `c:\foo\bar`);
+    }
+}
+
+@safe unittest
+{
+    import std.path;
+
+    import std.array;
+    version (Posix)
+    {
+        assert(asRelativePath("foo", "/bar").array == "foo");
+        assert(asRelativePath("/foo/bar", "/foo/bar").array == ".");
+        assert(asRelativePath("/foo/bar", "/foo/baz").array == "../bar");
+        assert(asRelativePath("/foo/bar/baz", "/foo/woo/wee").array == "../../bar/baz");
+        assert(asRelativePath("/foo/bar/baz", "/foo/bar").array == "baz");
+    }
+    else version (Windows)
+    {
+        assert(asRelativePath("foo", `c:\bar`).array == "foo");
+        assert(asRelativePath(`c:\foo\bar`, `c:\foo\bar`).array == ".");
+        assert(asRelativePath(`c:\foo\bar`, `c:\foo\baz`).array == `..\bar`);
+        assert(asRelativePath(`c:\foo\bar\baz`, `c:\foo\woo\wee`).array == `..\..\bar\baz`);
+        assert(asRelativePath(`c:/foo/bar/baz`, `c:\foo\woo\wee`).array == `..\..\bar\baz`);
+        assert(asRelativePath(`c:\foo\bar\baz`, `c:\foo\bar`).array == "baz");
+        assert(asRelativePath(`c:\foo\bar`, `d:\foo`).array == `c:\foo\bar`);
+        assert(asRelativePath(`\\foo\bar`, `c:\foo`).array == `\\foo\bar`);
+    }
+    else
+        static assert(0);
+}
+
+@safe unittest
+{
+    import std.path;
+
+    assert(filenameCharCmp('a', 'a') == 0);
+    assert(filenameCharCmp('a', 'b') < 0);
+    assert(filenameCharCmp('b', 'a') > 0);
+
+    version (linux)
+    {
+        // Same as calling filenameCharCmp!(CaseSensitive.yes)(a, b)
+        assert(filenameCharCmp('A', 'a') < 0);
+        assert(filenameCharCmp('a', 'A') > 0);
+    }
+    version (Windows)
+    {
+        // Same as calling filenameCharCmp!(CaseSensitive.no)(a, b)
+        assert(filenameCharCmp('a', 'A') == 0);
+        assert(filenameCharCmp('a', 'B') < 0);
+        assert(filenameCharCmp('A', 'b') < 0);
+    }
+}
+
+@safe unittest
+{
+    import std.path;
+
+    assert(filenameCmp("abc", "abc") == 0);
+    assert(filenameCmp("abc", "abd") < 0);
+    assert(filenameCmp("abc", "abb") > 0);
+    assert(filenameCmp("abc", "abcd") < 0);
+    assert(filenameCmp("abcd", "abc") > 0);
+
+    version (linux)
+    {
+        // Same as calling filenameCmp!(CaseSensitive.yes)(filename1, filename2)
+        assert(filenameCmp("Abc", "abc") < 0);
+        assert(filenameCmp("abc", "Abc") > 0);
+    }
+    version (Windows)
+    {
+        // Same as calling filenameCmp!(CaseSensitive.no)(filename1, filename2)
+        assert(filenameCmp("Abc", "abc") == 0);
+        assert(filenameCmp("abc", "Abc") == 0);
+        assert(filenameCmp("Abc", "abD") < 0);
+        assert(filenameCmp("abc", "AbB") > 0);
+    }
+}
+
+@safe @nogc unittest
+{
+    import std.path;
+
+    assert(globMatch("foo.bar", "*"));
+    assert(globMatch("foo.bar", "*.*"));
+    assert(globMatch(`foo/foo\bar`, "f*b*r"));
+    assert(globMatch("foo.bar", "f???bar"));
+    assert(globMatch("foo.bar", "[fg]???bar"));
+    assert(globMatch("foo.bar", "[!gh]*bar"));
+    assert(globMatch("bar.fooz", "bar.{foo,bif}z"));
+    assert(globMatch("bar.bifz", "bar.{foo,bif}z"));
+
+    version (Windows)
+    {
+        // Same as calling globMatch!(CaseSensitive.no)(path, pattern)
+        assert(globMatch("foo", "Foo"));
+        assert(globMatch("Goo.bar", "[fg]???bar"));
+    }
+    version (linux)
+    {
+        // Same as calling globMatch!(CaseSensitive.yes)(path, pattern)
+        assert(!globMatch("foo", "Foo"));
+        assert(!globMatch("Goo.bar", "[fg]???bar"));
+    }
+}
+
+@safe pure @nogc nothrow unittest
+{
+    import std.path;
+
+    import std.utf : byCodeUnit;
+
+    assert(isValidFilename("hello.exe".byCodeUnit));
+}
+
+@safe pure @nogc nothrow unittest
+{
+    import std.path;
+
+    assert(isValidPath("/foo/bar"));
+    assert(!isValidPath("/foo\0/bar"));
+    assert(isValidPath("/"));
+    assert(isValidPath("a"));
+
+    version (Windows)
+    {
+        assert(isValidPath(`c:\`));
+        assert(isValidPath(`c:\foo`));
+        assert(isValidPath(`c:\foo\.\bar\\\..\`));
+        assert(!isValidPath(`!:\foo`));
+        assert(!isValidPath(`c::\foo`));
+        assert(!isValidPath(`c:\foo?`));
+        assert(!isValidPath(`c:\foo.`));
+
+        assert(isValidPath(`\\server\share`));
+        assert(isValidPath(`\\server\share\foo`));
+        assert(isValidPath(`\\server\share\\foo`));
+        assert(!isValidPath(`\\\server\share\foo`));
+        assert(!isValidPath(`\\server\\share\foo`));
+        assert(!isValidPath(`\\ser*er\share\foo`));
+        assert(!isValidPath(`\\server\sha?e\foo`));
+        assert(!isValidPath(`\\server\share\|oo`));
+
+        assert(isValidPath(`\\?\<>:"?*|/\..\.`));
+        assert(!isValidPath("\\\\?\\foo\0bar"));
+
+        assert(!isValidPath(`\\.\PhysicalDisk1`));
+        assert(!isValidPath(`\\`));
+    }
+
+    import std.utf : byCodeUnit;
+    assert(isValidPath("/foo/bar".byCodeUnit));
+}
+
+@safe unittest
+{
+    import std.path;
+
+    version (Posix)
+    {
+        import std.process : environment;
+
+        auto oldHome = environment["HOME"];
+        scope(exit) environment["HOME"] = oldHome;
+
+        environment["HOME"] = "dmd/test";
+        assert(expandTilde("~/") == "dmd/test/");
+        assert(expandTilde("~") == "dmd/test");
+    }
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_random.d b/libphobos/testsuite/libphobos.phobos/std_random.d
new file mode 100644
index 0000000000000000000000000000000000000000..89147d3129554a41b12f7a77d84c2f470330c16e
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_random.d
@@ -0,0 +1,578 @@
+@safe unittest
+{
+    import std.random;
+
+    import std.algorithm.comparison : among, equal;
+    import std.range : iota;
+
+    // seed a random generator with a constant
+    auto rnd = Random(42);
+
+    // Generate a uniformly-distributed integer in the range [0, 14]
+    // If no random generator is passed, the global `rndGen` would be used
+    auto i = uniform(0, 15, rnd);
+    assert(i >= 0 && i < 15);
+
+    // Generate a uniformly-distributed real in the range [0, 100)
+    auto r = uniform(0.0L, 100.0L, rnd);
+    assert(r >= 0 && r < 100);
+
+    // Sample from a custom type
+    enum Fruit { apple, mango, pear }
+    auto f = rnd.uniform!Fruit;
+    with(Fruit)
+    assert(f.among(apple, mango, pear));
+
+    // Generate a 32-bit random number
+    auto u = uniform!uint(rnd);
+    static assert(is(typeof(u) == uint));
+
+    // Generate a random number in the range in the range [0, 1)
+    auto u2 = uniform01(rnd);
+    assert(u2 >= 0 && u2 < 1);
+
+    // Select an element randomly
+    auto el = 10.iota.choice(rnd);
+    assert(0 <= el && el < 10);
+
+    // Throw a dice with custom proportions
+    // 0: 20%, 1: 10%, 2: 60%
+    auto val = rnd.dice(0.2, 0.1, 0.6);
+    assert(0 <= val && val <= 2);
+
+    auto rnd2 = MinstdRand0(42);
+
+    // Select a random subsample from a range
+    assert(10.iota.randomSample(3, rnd2).equal([7, 8, 9]));
+
+    // Cover all elements in an array in random order
+    version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147
+        assert(10.iota.randomCover(rnd2).equal([7, 4, 2, 0, 1, 6, 8, 3, 9, 5]));
+    else
+        assert(10.iota.randomCover(rnd2).equal([4, 8, 7, 3, 5, 9, 2, 6, 0, 1]));
+
+    // Shuffle an array
+    version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147
+        assert([0, 1, 2, 4, 5].randomShuffle(rnd2).equal([2, 0, 4, 5, 1]));
+    else
+        assert([0, 1, 2, 4, 5].randomShuffle(rnd2).equal([4, 2, 5, 0, 1]));
+}
+
+@safe unittest
+{
+    import std.random;
+
+    struct NoRng
+    {
+        @property uint front() {return 0;}
+        @property bool empty() {return false;}
+        void popFront() {}
+    }
+    static assert(!isUniformRNG!(NoRng));
+
+    struct validRng
+    {
+        @property uint front() {return 0;}
+        @property bool empty() {return false;}
+        void popFront() {}
+
+        enum isUniformRandom = true;
+    }
+    static assert(isUniformRNG!(validRng, uint));
+    static assert(isUniformRNG!(validRng));
+}
+
+@safe unittest
+{
+    import std.random;
+
+    struct validRng
+    {
+        @property uint front() {return 0;}
+        @property bool empty() {return false;}
+        void popFront() {}
+
+        enum isUniformRandom = true;
+    }
+    static assert(!isSeedable!(validRng, uint));
+    static assert(!isSeedable!(validRng));
+
+    struct seedRng
+    {
+        @property uint front() {return 0;}
+        @property bool empty() {return false;}
+        void popFront() {}
+        void seed(uint val){}
+        enum isUniformRandom = true;
+    }
+    static assert(isSeedable!(seedRng, uint));
+    static assert(!isSeedable!(seedRng, ulong));
+    static assert(isSeedable!(seedRng));
+}
+
+@safe unittest
+{
+    import std.random;
+
+    alias CPP11LCG = LinearCongruentialEngine!(uint, 48271, 0, 2_147_483_647);
+
+    // seed with a constant
+    auto rnd = CPP11LCG(42);
+    auto n = rnd.front; // same for each run
+    assert(n == 2027382);
+}
+
+@safe unittest
+{
+    import std.random;
+
+    // glibc's LCG
+    alias GLibcLCG = LinearCongruentialEngine!(uint, 1103515245, 12345, 2_147_483_648);
+
+    // Seed with an unpredictable value
+    auto rnd = GLibcLCG(unpredictableSeed);
+    auto n = rnd.front; // different across runs
+}
+
+@safe unittest
+{
+    import std.random;
+
+    // Visual C++'s LCG
+    alias MSVCLCG = LinearCongruentialEngine!(uint, 214013, 2531011, 0);
+
+    // seed with a constant
+    auto rnd = MSVCLCG(1);
+    auto n = rnd.front; // same for each run
+    assert(n == 2745024);
+}
+
+@safe @nogc unittest
+{
+    import std.random;
+
+    // seed with a constant
+    auto rnd0 = MinstdRand0(1);
+    auto n = rnd0.front;
+     // same for each run
+    assert(n == 16807);
+
+    // Seed with an unpredictable value
+    rnd0.seed(unpredictableSeed);
+    n = rnd0.front; // different across runs
+}
+
+@safe unittest
+{
+    import std.random;
+
+    // seed with a constant
+    Mt19937 gen;
+    auto n = gen.front; // same for each run
+    assert(n == 3499211612);
+
+    // Seed with an unpredictable value
+    gen.seed(unpredictableSeed);
+    n = gen.front; // different across runs
+}
+
+@safe @nogc unittest
+{
+    import std.random;
+
+    // seed with a constant
+    Mt19937 gen;
+    auto n = gen.front; // same for each run
+    assert(n == 3499211612);
+
+    // Seed with an unpredictable value
+    gen.seed(unpredictableSeed);
+    n = gen.front; // different across runs
+}
+
+@safe @nogc unittest
+{
+    import std.random;
+
+    // Seed with a constant
+    auto gen = Mt19937_64(12345);
+    auto n = gen.front; // same for each run
+    assert(n == 6597103971274460346);
+
+    // Seed with an unpredictable value
+    gen.seed(unpredictableSeed!ulong);
+    n = gen.front; // different across runs
+}
+
+@safe unittest
+{
+    import std.random;
+
+    alias Xorshift96  = XorshiftEngine!(uint, 96,  10, 5,  26);
+    auto rnd = Xorshift96(42);
+    auto num = rnd.front;  // same for each run
+    assert(num == 2704588748);
+}
+
+@safe @nogc unittest
+{
+    import std.random;
+
+    // Seed with a constant
+    auto rnd = Xorshift(1);
+    auto num = rnd.front;  // same for each run
+    assert(num == 1405313047);
+
+    // Seed with an unpredictable value
+    rnd.seed(unpredictableSeed);
+    num = rnd.front; // different across rnd
+}
+
+@safe @nogc unittest
+{
+    import std.random;
+
+    auto rnd = Random(unpredictableSeed);
+    auto n = rnd.front;
+    static assert(is(typeof(n) == uint));
+}
+
+@safe nothrow @nogc unittest
+{
+    import std.random;
+
+    import std.algorithm.iteration : sum;
+    import std.range : take;
+    auto rnd = rndGen;
+    assert(rnd.take(3).sum > 0);
+}
+
+@safe unittest
+{
+    import std.random;
+
+    auto rnd = Random(unpredictableSeed);
+
+    // Generate an integer in [0, 1023]
+    auto a = uniform(0, 1024, rnd);
+    assert(0 <= a && a < 1024);
+
+    // Generate a float in [0, 1)
+    auto b = uniform(0.0f, 1.0f, rnd);
+    assert(0 <= b && b < 1);
+
+    // Generate a float in [0, 1]
+    b = uniform!"[]"(0.0f, 1.0f, rnd);
+    assert(0 <= b && b <= 1);
+
+    // Generate a float in (0, 1)
+    b = uniform!"()"(0.0f, 1.0f, rnd);
+    assert(0 < b && b < 1);
+}
+
+@safe unittest
+{
+    import std.random;
+
+    import std.array : array;
+    import std.range : generate, takeExactly;
+
+    int[] arr = generate!(() => uniform(0, 100)).takeExactly(10).array;
+    assert(arr.length == 10);
+    assert(arr[0] >= 0 && arr[0] < 100);
+}
+
+@safe unittest
+{
+    import std.random;
+
+    import std.conv : to;
+    import std.meta : AliasSeq;
+    import std.range.primitives : isForwardRange;
+    import std.traits : isIntegral, isSomeChar;
+
+    auto gen = Mt19937(123_456_789);
+    static assert(isForwardRange!(typeof(gen)));
+
+    auto a = uniform(0, 1024, gen);
+    assert(0 <= a && a <= 1024);
+    auto b = uniform(0.0f, 1.0f, gen);
+    assert(0 <= b && b < 1, to!string(b));
+    auto c = uniform(0.0, 1.0);
+    assert(0 <= c && c < 1);
+
+    static foreach (T; AliasSeq!(char, wchar, dchar, byte, ubyte, short, ushort,
+                          int, uint, long, ulong, float, double, real))
+    {{
+        T lo = 0, hi = 100;
+
+        // Try tests with each of the possible bounds
+        {
+            T init = uniform(lo, hi);
+            size_t i = 50;
+            while (--i && uniform(lo, hi) == init) {}
+            assert(i > 0);
+        }
+        {
+            T init = uniform!"[)"(lo, hi);
+            size_t i = 50;
+            while (--i && uniform(lo, hi) == init) {}
+            assert(i > 0);
+        }
+        {
+            T init = uniform!"(]"(lo, hi);
+            size_t i = 50;
+            while (--i && uniform(lo, hi) == init) {}
+            assert(i > 0);
+        }
+        {
+            T init = uniform!"()"(lo, hi);
+            size_t i = 50;
+            while (--i && uniform(lo, hi) == init) {}
+            assert(i > 0);
+        }
+        {
+            T init = uniform!"[]"(lo, hi);
+            size_t i = 50;
+            while (--i && uniform(lo, hi) == init) {}
+            assert(i > 0);
+        }
+
+        /* Test case with closed boundaries covering whole range
+         * of integral type
+         */
+        static if (isIntegral!T || isSomeChar!T)
+        {
+            foreach (immutable _; 0 .. 100)
+            {
+                auto u = uniform!"[]"(T.min, T.max);
+                static assert(is(typeof(u) == T));
+                assert(T.min <= u, "Lower bound violation for uniform!\"[]\" with " ~ T.stringof);
+                assert(u <= T.max, "Upper bound violation for uniform!\"[]\" with " ~ T.stringof);
+            }
+        }
+    }}
+
+    auto reproRng = Xorshift(239842);
+
+    static foreach (T; AliasSeq!(char, wchar, dchar, byte, ubyte, short,
+                          ushort, int, uint, long, ulong))
+    {{
+        T lo = T.min + 10, hi = T.max - 10;
+        T init = uniform(lo, hi, reproRng);
+        size_t i = 50;
+        while (--i && uniform(lo, hi, reproRng) == init) {}
+        assert(i > 0);
+    }}
+
+    {
+        bool sawLB = false, sawUB = false;
+        foreach (i; 0 .. 50)
+        {
+            auto x = uniform!"[]"('a', 'd', reproRng);
+            if (x == 'a') sawLB = true;
+            if (x == 'd') sawUB = true;
+            assert('a' <= x && x <= 'd');
+        }
+        assert(sawLB && sawUB);
+    }
+
+    {
+        bool sawLB = false, sawUB = false;
+        foreach (i; 0 .. 50)
+        {
+            auto x = uniform('a', 'd', reproRng);
+            if (x == 'a') sawLB = true;
+            if (x == 'c') sawUB = true;
+            assert('a' <= x && x < 'd');
+        }
+        assert(sawLB && sawUB);
+    }
+
+    {
+        bool sawLB = false, sawUB = false;
+        foreach (i; 0 .. 50)
+        {
+            immutable int lo = -2, hi = 2;
+            auto x = uniform!"()"(lo, hi, reproRng);
+            if (x == (lo+1)) sawLB = true;
+            if (x == (hi-1)) sawUB = true;
+            assert(lo < x && x < hi);
+        }
+        assert(sawLB && sawUB);
+    }
+
+    {
+        bool sawLB = false, sawUB = false;
+        foreach (i; 0 .. 50)
+        {
+            immutable ubyte lo = 0, hi = 5;
+            auto x = uniform(lo, hi, reproRng);
+            if (x == lo) sawLB = true;
+            if (x == (hi-1)) sawUB = true;
+            assert(lo <= x && x < hi);
+        }
+        assert(sawLB && sawUB);
+    }
+
+    {
+        foreach (i; 0 .. 30)
+        {
+            assert(i == uniform(i, i+1, reproRng));
+        }
+    }
+}
+
+@safe unittest
+{
+    import std.random;
+
+    auto rnd = MinstdRand0(42);
+
+    assert(rnd.uniform!ubyte == 102);
+    assert(rnd.uniform!ulong == 4838462006927449017);
+
+    enum Fruit { apple, mango, pear }
+    version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147
+    assert(rnd.uniform!Fruit == Fruit.mango);
+}
+
+@safe @nogc unittest
+{
+    import std.random;
+
+    import std.math.operations : feqrel;
+
+    auto rnd = MinstdRand0(42);
+
+    // Generate random numbers in the range in the range [0, 1)
+    auto u1 = uniform01(rnd);
+    assert(u1 >= 0 && u1 < 1);
+
+    auto u2 = rnd.uniform01!float;
+    assert(u2 >= 0 && u2 < 1);
+
+    // Confirm that the random values with the initial seed 42 are 0.000328707 and 0.524587
+    assert(u1.feqrel(0.000328707) > 20);
+    assert(u2.feqrel(0.524587) > 20);
+}
+
+@safe unittest
+{
+    import std.random;
+
+    import std.algorithm.iteration : reduce;
+    import std.math.operations : isClose;
+
+    auto a = uniformDistribution(5);
+    assert(a.length == 5);
+    assert(isClose(reduce!"a + b"(a), 1));
+
+    a = uniformDistribution(10, a);
+    assert(a.length == 10);
+    assert(isClose(reduce!"a + b"(a), 1));
+}
+
+@safe unittest
+{
+    import std.random;
+
+    auto rnd = MinstdRand0(42);
+
+    auto elem  = [1, 2, 3, 4, 5].choice(rnd);
+    version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147
+    assert(elem == 3);
+}
+
+@safe unittest
+{
+    import std.random;
+
+    auto rnd = MinstdRand0(42);
+
+    auto arr = [1, 2, 3, 4, 5].randomShuffle(rnd);
+    version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147
+    assert(arr == [3, 5, 2, 4, 1]);
+}
+
+@safe unittest
+{
+    import std.random;
+
+    auto rnd = MinstdRand0(42);
+
+    auto arr = [1, 2, 3, 4, 5, 6];
+    arr = arr.dup.partialShuffle(1, rnd);
+
+    version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147
+    assert(arr == [2, 1, 3, 4, 5, 6]); // 1<->2
+
+    arr = arr.dup.partialShuffle(2, rnd);
+    version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147
+    assert(arr == [1, 4, 3, 2, 5, 6]); // 1<->2, 2<->4
+
+    arr = arr.dup.partialShuffle(3, rnd);
+    version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147
+    assert(arr == [5, 4, 6, 2, 1, 3]); // 1<->5, 2<->4, 3<->6
+}
+
+@safe unittest
+{
+    import std.random;
+
+    auto d6  = 1 + dice(1, 1, 1, 1, 1, 1); // fair dice roll
+    auto d6b = 1 + dice(2, 1, 1, 1, 1, 1); // double the chance to roll '1'
+
+    auto x = dice(0.5, 0.5);   // x is 0 or 1 in equal proportions
+    auto y = dice(50, 50);     // y is 0 or 1 in equal proportions
+    auto z = dice(70, 20, 10); // z is 0 70% of the time, 1 20% of the time,
+                               // and 2 10% of the time
+}
+
+@safe unittest
+{
+    import std.random;
+
+    auto rnd = MinstdRand0(42);
+    auto z = rnd.dice(70, 20, 10);
+    assert(z == 0);
+    z = rnd.dice(30, 20, 40, 10);
+    assert(z == 2);
+}
+
+@safe unittest
+{
+    import std.random;
+
+    auto rnd = Xorshift(123_456_789);
+    auto i = dice(rnd, 0.0, 100.0);
+    assert(i == 1);
+    i = dice(rnd, 100.0, 0.0);
+    assert(i == 0);
+
+    i = dice(100U, 0U);
+    assert(i == 0);
+}
+
+@safe unittest
+{
+    import std.random;
+
+    import std.algorithm.comparison : equal;
+    import std.range : iota;
+    auto rnd = MinstdRand0(42);
+
+    version (D_LP64) // https://issues.dlang.org/show_bug.cgi?id=15147
+    assert(10.iota.randomCover(rnd).equal([7, 4, 2, 0, 1, 6, 8, 3, 9, 5]));
+}
+
+@safe unittest
+{
+    import std.random;
+
+    import std.algorithm.comparison : equal;
+    import std.range : iota;
+    auto rnd = MinstdRand0(42);
+    assert(10.iota.randomSample(3, rnd).equal([7, 8, 9]));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_range_interfaces.d b/libphobos/testsuite/libphobos.phobos/std_range_interfaces.d
new file mode 100644
index 0000000000000000000000000000000000000000..8d91401930c2bedc973b0ce3c44990fd040f10d2
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_range_interfaces.d
@@ -0,0 +1,32 @@
+@safe unittest
+{
+    import std.range.interfaces;
+
+    import std.algorithm.iteration : map;
+    import std.range : iota;
+
+    void useRange(InputRange!int range) {
+        // Function body.
+    }
+
+    // Create a range type.
+    auto squares = map!"a * a"(iota(10));
+
+    // Wrap it in an interface.
+    auto squaresWrapped = inputRangeObject(squares);
+
+    // Use it.
+    useRange(squaresWrapped);
+}
+
+@safe unittest
+{
+    import std.range.interfaces;
+
+     import std.array;
+     auto app = appender!(uint[])();
+     auto appWrapped = outputRangeObject!(uint, uint[])(app);
+     static assert(is(typeof(appWrapped) : OutputRange!(uint[])));
+     static assert(is(typeof(appWrapped) : OutputRange!(uint)));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_range_package.d b/libphobos/testsuite/libphobos.phobos/std_range_package.d
new file mode 100644
index 0000000000000000000000000000000000000000..03378ce3d668be11824e1b33142adfda68290ed2
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_range_package.d
@@ -0,0 +1,1346 @@
+pure @safe nothrow @nogc unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    int[5] a = [ 1, 2, 3, 4, 5 ];
+    int[5] b = [ 5, 4, 3, 2, 1 ];
+    assert(equal(retro(a[]), b[]));
+    assert(retro(a[]).source is a[]);
+    assert(retro(retro(a[])) is a[]);
+}
+
+pure @safe nothrow unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+
+    int[] a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ];
+    assert(equal(stride(a, 3), [ 1, 4, 7, 10 ][]));
+    assert(stride(stride(a, 2), 3) == stride(a, 6));
+}
+
+pure @safe nothrow unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+
+    int[] arr1 = [ 1, 2, 3, 4 ];
+    int[] arr2 = [ 5, 6 ];
+    int[] arr3 = [ 7 ];
+    auto s = chain(arr1, arr2, arr3);
+    assert(s.length == 7);
+    assert(s[5] == 6);
+    assert(equal(s, [1, 2, 3, 4, 5, 6, 7][]));
+}
+
+pure @safe nothrow unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    import std.algorithm.sorting : sort;
+
+    int[] arr1 = [5, 2, 8];
+    int[] arr2 = [3, 7, 9];
+    int[] arr3 = [1, 4, 6];
+
+    // in-place sorting across all of the arrays
+    auto s = arr1.chain(arr2, arr3).sort;
+
+    assert(s.equal([1, 2, 3, 4, 5, 6, 7, 8, 9]));
+    assert(arr1.equal([1, 2, 3]));
+    assert(arr2.equal([4, 5, 6]));
+    assert(arr3.equal([7, 8, 9]));
+}
+
+pure @safe nothrow unittest
+{
+    import std.range;
+
+    import std.utf : byChar, byCodeUnit;
+
+    auto s1 = "string one";
+    auto s2 = "string two";
+    // s1 and s2 front is dchar because of auto-decoding
+    static assert(is(typeof(s1.front) == dchar) && is(typeof(s2.front) == dchar));
+
+    auto r1 = s1.chain(s2);
+    // chains of ranges of the same character type give that same type
+    static assert(is(typeof(r1.front) == dchar));
+
+    auto s3 = "string three".byCodeUnit;
+    static assert(is(typeof(s3.front) == immutable char));
+    auto r2 = s1.chain(s3);
+    // chaining ranges of mixed character types gives `dchar`
+    static assert(is(typeof(r2.front) == dchar));
+
+    // use byChar on character ranges to correctly convert them to UTF-8
+    auto r3 = s1.byChar.chain(s3);
+    static assert(is(typeof(r3.front) == immutable char));
+}
+
+@safe nothrow pure @nogc unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    import std.algorithm.iteration : filter, map;
+
+    auto data1 = only(1, 2, 3, 4).filter!(a => a != 3);
+    auto data2 = only(5, 6, 7, 8).map!(a => a + 1);
+
+    // choose() is primarily useful when you need to select one of two ranges
+    // with different types at runtime.
+    static assert(!is(typeof(data1) == typeof(data2)));
+
+    auto chooseRange(bool pickFirst)
+    {
+        // The returned range is a common wrapper type that can be used for
+        // returning or storing either range without running into a type error.
+        return choose(pickFirst, data1, data2);
+
+        // Simply returning the chosen range without using choose() does not
+        // work, because map() and filter() return different types.
+        //return pickFirst ? data1 : data2; // does not compile
+    }
+
+    auto result = chooseRange(true);
+    assert(result.equal(only(1, 2, 4)));
+
+    result = chooseRange(false);
+    assert(result.equal(only(6, 7, 8, 9)));
+}
+
+@safe nothrow pure @nogc unittest
+{
+    import std.range;
+
+    auto test()
+    {
+        import std.algorithm.comparison : equal;
+
+        int[4] sarr1 = [1, 2, 3, 4];
+        int[2] sarr2 = [5, 6];
+        int[1] sarr3 = [7];
+        auto arr1 = sarr1[];
+        auto arr2 = sarr2[];
+        auto arr3 = sarr3[];
+
+        {
+            auto s = chooseAmong(0, arr1, arr2, arr3);
+            auto t = s.save;
+            assert(s.length == 4);
+            assert(s[2] == 3);
+            s.popFront();
+            assert(equal(t, only(1, 2, 3, 4)));
+        }
+        {
+            auto s = chooseAmong(1, arr1, arr2, arr3);
+            assert(s.length == 2);
+            s.front = 8;
+            assert(equal(s, only(8, 6)));
+        }
+        {
+            auto s = chooseAmong(1, arr1, arr2, arr3);
+            assert(s.length == 2);
+            s[1] = 9;
+            assert(equal(s, only(8, 9)));
+        }
+        {
+            auto s = chooseAmong(1, arr2, arr1, arr3)[1 .. 3];
+            assert(s.length == 2);
+            assert(equal(s, only(2, 3)));
+        }
+        {
+            auto s = chooseAmong(0, arr1, arr2, arr3);
+            assert(s.length == 4);
+            assert(s.back == 4);
+            s.popBack();
+            s.back = 5;
+            assert(equal(s, only(1, 2, 5)));
+            s.back = 3;
+            assert(equal(s, only(1, 2, 3)));
+        }
+        {
+            uint[5] foo = [1, 2, 3, 4, 5];
+            uint[5] bar = [6, 7, 8, 9, 10];
+            auto c = chooseAmong(1, foo[], bar[]);
+            assert(c[3] == 9);
+            c[3] = 42;
+            assert(c[3] == 42);
+            assert(c.moveFront() == 6);
+            assert(c.moveBack() == 10);
+            assert(c.moveAt(4) == 10);
+        }
+        {
+            import std.range : cycle;
+            auto s = chooseAmong(0, cycle(arr2), cycle(arr3));
+            assert(isInfinite!(typeof(s)));
+            assert(!s.empty);
+            assert(s[100] == 8);
+            assert(s[101] == 9);
+            assert(s[0 .. 3].equal(only(8, 9, 8)));
+        }
+        return 0;
+    }
+    // works at runtime
+    auto a = test();
+    // and at compile time
+    static b = test();
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+
+    int[] a = [ 1, 2, 3 ];
+    int[] b = [ 10, 20, 30, 40 ];
+    auto r = roundRobin(a, b);
+    assert(equal(r, [ 1, 10, 2, 20, 3, 30, 40 ]));
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+
+    auto interleave(R, E)(R range, E element)
+    if ((isInputRange!R && hasLength!R) || isForwardRange!R)
+    {
+        static if (hasLength!R)
+            immutable len = range.length;
+        else
+            immutable len = range.save.walkLength;
+
+        return roundRobin(
+            range,
+            element.repeat(len - 1)
+        );
+    }
+
+    assert(interleave([1, 2, 3], 0).equal([1, 0, 2, 0, 3]));
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    int[] a = [ 1, 2, 3, 4, 5 ];
+    assert(equal(radial(a), [ 3, 4, 2, 5, 1 ]));
+    a = [ 1, 2, 3, 4 ];
+    assert(equal(radial(a), [ 2, 3, 1, 4 ]));
+
+    // If the left end is reached first, the remaining elements on the right
+    // are concatenated in order:
+    a = [ 0, 1, 2, 3, 4, 5 ];
+    assert(equal(radial(a, 1), [ 1, 2, 0, 3, 4, 5 ]));
+
+    // If the right end is reached first, the remaining elements on the left
+    // are concatenated in reverse order:
+    assert(equal(radial(a, 4), [ 4, 5, 3, 2, 1, 0 ]));
+}
+
+pure @safe nothrow unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+
+    int[] arr1 = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];
+    auto s = take(arr1, 5);
+    assert(s.length == 5);
+    assert(s[4] == 5);
+    assert(equal(s, [ 1, 2, 3, 4, 5 ][]));
+}
+
+pure @safe nothrow unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+
+    int[] arr2 = [ 1, 2, 3 ];
+    auto t = take(arr2, 5);
+    assert(t.length == 3);
+    assert(equal(t, [ 1, 2, 3 ]));
+}
+
+pure @safe nothrow unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+
+    auto a = [ 1, 2, 3, 4, 5 ];
+
+    auto b = takeExactly(a, 3);
+    assert(equal(b, [1, 2, 3]));
+    static assert(is(typeof(b.length) == size_t));
+    assert(b.length == 3);
+    assert(b.front == 1);
+    assert(b.back == 3);
+}
+
+pure @safe nothrow unittest
+{
+    import std.range;
+
+    auto s = takeOne([42, 43, 44]);
+    static assert(isRandomAccessRange!(typeof(s)));
+    assert(s.length == 1);
+    assert(!s.empty);
+    assert(s.front == 42);
+    s.front = 43;
+    assert(s.front == 43);
+    assert(s.back == 43);
+    assert(s[0] == 43);
+    s.popFront();
+    assert(s.length == 0);
+    assert(s.empty);
+}
+
+pure @safe nothrow @nogc unittest
+{
+    import std.range;
+
+    auto range = takeNone!(int[])();
+    assert(range.length == 0);
+    assert(range.empty);
+}
+
+pure @safe nothrow unittest
+{
+    import std.range;
+
+    import std.algorithm.iteration : filter;
+    assert(takeNone([42, 27, 19]).empty);
+    assert(takeNone("dlang.org").empty);
+    assert(takeNone(filter!"true"([42, 27, 19])).empty);
+}
+
+pure @safe nothrow unittest
+{
+    import std.range;
+
+    // tail -c n
+    assert([1, 2, 3].tail(1) == [3]);
+    assert([1, 2, 3].tail(2) == [2, 3]);
+    assert([1, 2, 3].tail(3) == [1, 2, 3]);
+    assert([1, 2, 3].tail(4) == [1, 2, 3]);
+    assert([1, 2, 3].tail(0).length == 0);
+
+    // tail --lines=n
+    import std.algorithm.comparison : equal;
+    import std.algorithm.iteration : joiner;
+    import std.exception : assumeWontThrow;
+    import std.string : lineSplitter;
+    assert("one\ntwo\nthree"
+        .lineSplitter
+        .tail(2)
+        .joiner("\n")
+        .equal("two\nthree")
+        .assumeWontThrow);
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+
+    assert([0, 2, 1, 5, 0, 3].drop(3) == [5, 0, 3]);
+    assert("hello world".drop(6) == "world");
+    assert("hello world".drop(50).empty);
+    assert("hello world".take(6).drop(3).equal("lo "));
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+
+    assert([0, 2, 1, 5, 0, 3].dropBack(3) == [0, 2, 1]);
+    assert("hello world".dropBack(6) == "hello");
+    assert("hello world".dropBack(50).empty);
+    assert("hello world".drop(4).dropBack(4).equal("o w"));
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    import std.algorithm.iteration : filterBidirectional;
+
+    auto a = [1, 2, 3];
+    assert(a.dropExactly(2) == [3]);
+    assert(a.dropBackExactly(2) == [1]);
+
+    string s = "日本語";
+    assert(s.dropExactly(2) == "語");
+    assert(s.dropBackExactly(2) == "æ—¥");
+
+    auto bd = filterBidirectional!"true"([1, 2, 3]);
+    assert(bd.dropExactly(2).equal([3]));
+    assert(bd.dropBackExactly(2).equal([1]));
+}
+
+pure @safe nothrow unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    import std.algorithm.iteration : filterBidirectional;
+    import std.container.dlist : DList;
+
+    auto dl = DList!int(9, 1, 2, 3, 9);
+    assert(dl[].dropOne().dropBackOne().equal([1, 2, 3]));
+
+    auto a = [1, 2, 3];
+    assert(a.dropOne() == [2, 3]);
+    assert(a.dropBackOne() == [1, 2]);
+
+    string s = "日本語";
+    import std.exception : assumeWontThrow;
+    assert(assumeWontThrow(s.dropOne() == "本語"));
+    assert(assumeWontThrow(s.dropBackOne() == "日本"));
+
+    auto bd = filterBidirectional!"true"([1, 2, 3]);
+    assert(bd.dropOne().equal([2, 3]));
+    assert(bd.dropBackOne().equal([1, 2]));
+}
+
+pure @safe nothrow unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+
+    assert(5.repeat().take(4).equal([5, 5, 5, 5]));
+}
+
+pure @safe nothrow unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+
+    assert(5.repeat(4).equal([5, 5, 5, 5]));
+}
+
+@safe pure nothrow unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    import std.algorithm.iteration : map;
+
+    int i = 1;
+    auto powersOfTwo = generate!(() => i *= 2)().take(10);
+    assert(equal(powersOfTwo, iota(1, 11).map!"2^^a"()));
+}
+
+@safe pure nothrow unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+
+    //Returns a run-time delegate
+    auto infiniteIota(T)(T low, T high)
+    {
+        T i = high;
+        return (){if (i == high) i = low; return i++;};
+    }
+    //adapted as a range.
+    assert(equal(generate(infiniteIota(1, 4)).take(10), [1, 2, 3, 1, 2, 3, 1, 2, 3, 1]));
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.format : format;
+    import std.random : uniform;
+
+    auto r = generate!(() => uniform(0, 6)).take(10);
+    format("%(%s %)", r);
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    import std.range : cycle, take;
+
+    // Here we create an infinitive cyclic sequence from [1, 2]
+    // (i.e. get here [1, 2, 1, 2, 1, 2 and so on]) then
+    // take 5 elements of this sequence (so we have [1, 2, 1, 2, 1])
+    // and compare them with the expected values for equality.
+    assert(cycle([1, 2]).take(5).equal([ 1, 2, 1, 2, 1 ]));
+}
+
+@nogc nothrow pure @safe unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    import std.algorithm.iteration : map;
+
+    // pairwise sum
+    auto arr = only(0, 1, 2);
+    auto part1 = zip(arr, arr.dropOne).map!"a[0] + a[1]";
+    assert(part1.equal(only(1, 3)));
+}
+
+nothrow pure @safe unittest
+{
+    import std.range;
+
+    import std.conv : to;
+
+    int[] a = [ 1, 2, 3 ];
+    string[] b = [ "a", "b", "c" ];
+    string[] result;
+
+    foreach (tup; zip(a, b))
+    {
+        result ~= tup[0].to!string ~ tup[1];
+    }
+
+    assert(result == [ "1a", "2b", "3c" ]);
+
+    size_t idx = 0;
+    // unpacking tuple elements with foreach
+    foreach (e1, e2; zip(a, b))
+    {
+        assert(e1 == a[idx]);
+        assert(e2 == b[idx]);
+        ++idx;
+    }
+}
+
+nothrow pure @safe unittest
+{
+    import std.range;
+
+    import std.algorithm.sorting : sort;
+
+    int[] a = [ 1, 2, 3 ];
+    string[] b = [ "a", "c", "b" ];
+    zip(a, b).sort!((t1, t2) => t1[0] > t2[0]);
+
+    assert(a == [ 3, 2, 1 ]);
+    // b is sorted according to a's sorting
+    assert(b == [ "b", "c", "a" ]);
+}
+
+pure @safe unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    import std.exception : assertThrown;
+    import std.range.primitives;
+    import std.typecons : tuple;
+
+    auto a = [1, 2, 3];
+    auto b = [4, 5, 6, 7];
+
+    auto shortest = zip(StoppingPolicy.shortest, a, b);
+    assert(shortest.equal([
+        tuple(1, 4),
+        tuple(2, 5),
+        tuple(3, 6)
+    ]));
+
+    auto longest = zip(StoppingPolicy.longest, a, b);
+    assert(longest.equal([
+        tuple(1, 4),
+        tuple(2, 5),
+        tuple(3, 6),
+        tuple(0, 7)
+    ]));
+
+    auto same = zip(StoppingPolicy.requireSameLength, a, b);
+    same.popFrontN(3);
+    assertThrown!Exception(same.popFront);
+}
+
+pure @safe unittest
+{
+    import std.range;
+
+    int[6] arr1 = [1,2,3,4,5,100];
+    int[5] arr2 = [6,7,8,9,10];
+
+    foreach (ref a, b; lockstep(arr1[], arr2[]))
+    {
+        a += b;
+    }
+
+    assert(arr1 == [7,9,11,13,15,100]);
+}
+
+pure @safe unittest
+{
+    import std.range;
+
+    int[3] arr1 = [1,2,3];
+    int[3] arr2 = [4,5,6];
+
+    foreach (index, a, b; lockstep(arr1[], arr2[]))
+    {
+        assert(arr1[index] == a);
+        assert(arr2[index] == b);
+    }
+}
+
+pure @safe nothrow unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+
+    // The Fibonacci numbers, using function in string form:
+    // a[0] = 1, a[1] = 1, and compute a[n+1] = a[n-1] + a[n]
+    auto fib = recurrence!("a[n-1] + a[n-2]")(1, 1);
+    assert(fib.take(10).equal([1, 1, 2, 3, 5, 8, 13, 21, 34, 55]));
+
+    // The factorials, using function in lambda form:
+    auto fac = recurrence!((a,n) => a[n-1] * n)(1);
+    assert(take(fac, 10).equal([
+        1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880
+    ]));
+
+    // The triangular numbers, using function in explicit form:
+    static size_t genTriangular(R)(R state, size_t n)
+    {
+        return state[n-1] + n;
+    }
+    auto tri = recurrence!genTriangular(0);
+    assert(take(tri, 10).equal([0, 1, 3, 6, 10, 15, 21, 28, 36, 45]));
+}
+
+pure @safe nothrow @nogc unittest
+{
+    import std.range;
+
+    auto odds = sequence!("a[0] + n * a[1]")(1, 2);
+    assert(odds.front == 1);
+    odds.popFront();
+    assert(odds.front == 3);
+    odds.popFront();
+    assert(odds.front == 5);
+}
+
+pure @safe nothrow @nogc unittest
+{
+    import std.range;
+
+    auto tri = sequence!((a,n) => n*(n+1)/2)();
+
+    // Note random access
+    assert(tri[0] == 0);
+    assert(tri[3] == 6);
+    assert(tri[1] == 1);
+    assert(tri[4] == 10);
+    assert(tri[2] == 3);
+}
+
+@safe nothrow @nogc unittest
+{
+    import std.range;
+
+    import std.math.exponential : pow;
+    import std.math.rounding : round;
+    import std.math.algebraic : sqrt;
+    static ulong computeFib(S)(S state, size_t n)
+    {
+        // Binet's formula
+        return cast(ulong)(round((pow(state[0], n+1) - pow(state[1], n+1)) /
+                                 state[2]));
+    }
+    auto fib = sequence!computeFib(
+        (1.0 + sqrt(5.0)) / 2.0,    // Golden Ratio
+        (1.0 - sqrt(5.0)) / 2.0,    // Conjugate of Golden Ratio
+        sqrt(5.0));
+
+    // Note random access with [] operator
+    assert(fib[1] == 1);
+    assert(fib[4] == 5);
+    assert(fib[3] == 3);
+    assert(fib[2] == 2);
+    assert(fib[9] == 55);
+}
+
+pure @safe unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    import std.math.operations : isClose;
+
+    auto r = iota(0, 10, 1);
+    assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));
+    assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));
+    assert(3 in r);
+    assert(r.contains(3)); //Same as above
+    assert(!(10 in r));
+    assert(!(-8 in r));
+    r = iota(0, 11, 3);
+    assert(equal(r, [0, 3, 6, 9]));
+    assert(r[2] == 6);
+    assert(!(2 in r));
+    auto rf = iota(0.0, 0.5, 0.1);
+    assert(isClose(rf, [0.0, 0.1, 0.2, 0.3, 0.4]));
+}
+
+@safe pure unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    import std.exception : assertThrown;
+
+    auto arr = [[1, 2], [3, 4, 5]];
+
+    auto r1 = arr.frontTransversal!(TransverseOptions.assumeJagged);
+    assert(r1.equal([1, 3]));
+
+    // throws on construction
+    assertThrown!Exception(arr.frontTransversal!(TransverseOptions.enforceNotJagged));
+
+    auto r2 = arr.frontTransversal!(TransverseOptions.assumeNotJagged);
+    assert(r2.equal([1, 3]));
+
+    // either assuming or checking for equal lengths makes
+    // the result a random access range
+    assert(r2[0] == 1);
+    static assert(!__traits(compiles, r1[0]));
+}
+
+pure @safe nothrow unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    int[][] x = new int[][2];
+    x[0] = [1, 2];
+    x[1] = [3, 4];
+    auto ror = frontTransversal(x);
+    assert(equal(ror, [ 1, 3 ][]));
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    int[][] x = new int[][2];
+    x[0] = [1, 2];
+    x[1] = [3, 4];
+    auto ror = transversal(x, 1);
+    assert(equal(ror, [ 2, 4 ]));
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    import std.algorithm.iteration : map;
+    int[][] y = [[1, 2, 3], [4, 5, 6]];
+    auto z = y.front.walkLength.iota.map!(i => transversal(y, i));
+    assert(equal!equal(z, [[1, 4], [2, 5], [3, 6]]));
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    int[][] ror = [
+        [1, 2, 3],
+        [4, 5, 6]
+    ];
+    auto xp = transposed(ror);
+    assert(equal!"a.equal(b)"(xp, [
+        [1, 4],
+        [2, 5],
+        [3, 6]
+    ]));
+}
+
+@safe unittest
+{
+    import std.range;
+
+    int[][] x = new int[][2];
+    x[0] = [1, 2];
+    x[1] = [3, 4];
+    auto tr = transposed(x);
+    int[][] witness = [ [ 1, 3 ], [ 2, 4 ] ];
+    uint i;
+
+    foreach (e; tr)
+    {
+        assert(array(e) == witness[i++]);
+    }
+}
+
+@safe unittest
+{
+    import std.range;
+
+            auto ind = indexed([1, 2, 3, 4, 5], [1, 3, 4]);
+            assert(ind.physicalIndex(0) == 1);
+        
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    auto source = [1, 2, 3, 4, 5];
+    auto indices = [4, 3, 1, 2, 0, 4];
+    auto ind = indexed(source, indices);
+    assert(equal(ind, [5, 4, 2, 3, 1, 5]));
+    assert(equal(retro(ind), [5, 1, 3, 2, 4, 5]));
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+    auto chunks = chunks(source, 4);
+    assert(chunks[0] == [1, 2, 3, 4]);
+    assert(chunks[1] == [5, 6, 7, 8]);
+    assert(chunks[2] == [9, 10]);
+    assert(chunks.back == chunks[2]);
+    assert(chunks.front == chunks[0]);
+    assert(chunks.length == 3);
+    assert(equal(retro(array(chunks)), array(retro(chunks))));
+}
+
+@system unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+
+    int i;
+
+    // The generator doesn't save state, so it cannot be a forward range.
+    auto inputRange = generate!(() => ++i).take(10);
+
+    // We can still process it in chunks, but it will be single-pass only.
+    auto chunked = inputRange.chunks(2);
+
+    assert(chunked.front.equal([1, 2]));
+    assert(chunked.front.empty); // Iterating the chunk has consumed it
+    chunked.popFront;
+    assert(chunked.front.equal([3, 4]));
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    auto source = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
+    auto chunks = evenChunks(source, 3);
+    assert(chunks[0] == [1, 2, 3, 4]);
+    assert(chunks[1] == [5, 6, 7]);
+    assert(chunks[2] == [8, 9, 10]);
+}
+
+@safe pure nothrow unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+
+    assert([0, 1, 2, 3].slide(2).equal!equal(
+        [[0, 1], [1, 2], [2, 3]]
+    ));
+
+    assert(5.iota.slide(3).equal!equal(
+        [[0, 1, 2], [1, 2, 3], [2, 3, 4]]
+    ));
+}
+
+@safe pure nothrow unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+
+    assert(6.iota.slide(1, 2).equal!equal(
+        [[0], [2], [4]]
+    ));
+
+    assert(6.iota.slide(2, 4).equal!equal(
+        [[0, 1], [4, 5]]
+    ));
+
+    assert(iota(7).slide(2, 2).equal!equal(
+        [[0, 1], [2, 3], [4, 5], [6]]
+    ));
+
+    assert(iota(12).slide(2, 4).equal!equal(
+        [[0, 1], [4, 5], [8, 9]]
+    ));
+}
+
+@safe pure nothrow unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+
+    assert(3.iota.slide!(No.withPartial)(4).empty);
+    assert(3.iota.slide!(Yes.withPartial)(4).equal!equal(
+        [[0, 1, 2]]
+    ));
+}
+
+@safe pure nothrow unittest
+{
+    import std.range;
+
+    import std.algorithm.iteration : each;
+
+    int[dstring] d;
+    "AGAGA"d.slide!(Yes.withPartial)(2).each!(a => d[a]++);
+    assert(d == ["AG"d: 2, "GA"d: 2]);
+}
+
+@safe pure nothrow unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+
+    assert(5.iota.slide!(Yes.withPartial)(3, 4).equal!equal([[0, 1, 2], [4]]));
+    assert(6.iota.slide!(Yes.withPartial)(3, 4).equal!equal([[0, 1, 2], [4, 5]]));
+    assert(7.iota.slide!(Yes.withPartial)(3, 4).equal!equal([[0, 1, 2], [4, 5, 6]]));
+
+    assert(5.iota.slide!(No.withPartial)(3, 4).equal!equal([[0, 1, 2]]));
+    assert(6.iota.slide!(No.withPartial)(3, 4).equal!equal([[0, 1, 2]]));
+    assert(7.iota.slide!(No.withPartial)(3, 4).equal!equal([[0, 1, 2], [4, 5, 6]]));
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    import std.algorithm.iteration : filter, joiner, map;
+    import std.algorithm.searching : findSplitBefore;
+    import std.uni : isUpper;
+
+    assert(equal(only('♡'), "♡"));
+    assert([1, 2, 3, 4].findSplitBefore(only(3))[0] == [1, 2]);
+
+    assert(only("one", "two", "three").joiner(" ").equal("one two three"));
+
+    string title = "The D Programming Language";
+    assert(title
+        .filter!isUpper // take the upper case letters
+        .map!only       // make each letter its own range
+        .joiner(".")    // join the ranges together lazily
+        .equal("T.D.P.L"));
+}
+
+pure @safe nothrow unittest
+{
+    import std.range;
+
+    import std.array : assocArray;
+    import std.range : enumerate;
+
+    bool[int] aa = true.repeat(3).enumerate(-1).assocArray();
+    assert(aa[-1]);
+    assert(aa[0]);
+    assert(aa[1]);
+}
+
+@safe unittest
+{
+    import std.range;
+
+    void func1(int a, int b);
+    void func2(int a, float b);
+
+    static assert(isTwoWayCompatible!(func1, int, int));
+    static assert(isTwoWayCompatible!(func1, short, int));
+    static assert(!isTwoWayCompatible!(func2, int, float));
+
+    void func3(ref int a, ref int b);
+    static assert( isTwoWayCompatible!(func3, int, int));
+    static assert(!isTwoWayCompatible!(func3, short, int));
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+
+    auto a = assumeSorted([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
+    auto p1 = a.upperBound!(SearchPolicy.binarySearch)(3);
+    assert(p1.equal([4, 5, 6, 7, 8, 9]));
+
+    auto p2 = a.lowerBound!(SearchPolicy.gallop)(4);
+    assert(p2.equal([0, 1, 2, 3]));
+}
+
+@safe pure unittest
+{
+    import std.range;
+
+    // create a SortedRange, that's checked strictly
+    SortedRange!(int[],"a < b", SortedRangeOptions.checkStrictly)([ 1, 3, 5, 7, 9 ]);
+}
+
+@safe unittest
+{
+    import std.range;
+
+        import std.algorithm.sorting : sort;
+        int[3] data = [ 1, 2, 3 ];
+        auto a = assumeSorted(data[]);
+        assert(a == sort!"a < b"(data[]));
+        int[] p = a.release();
+        assert(p == [ 1, 2, 3 ]);
+    
+}
+
+@safe unittest
+{
+    import std.range;
+
+        import std.algorithm.comparison : equal;
+        auto a = assumeSorted([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]);
+        auto p = a.lowerBound(4);
+        assert(equal(p, [ 0, 1, 2, 3 ]));
+    
+}
+
+@safe unittest
+{
+    import std.range;
+
+        import std.algorithm.comparison : equal;
+        auto a = assumeSorted([ 1, 2, 3, 3, 3, 4, 4, 5, 6 ]);
+        auto p = a.upperBound(3);
+        assert(equal(p, [4, 4, 5, 6]));
+    
+}
+
+@safe unittest
+{
+    import std.range;
+
+        import std.algorithm.comparison : equal;
+        auto a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ];
+        auto r = a.assumeSorted.equalRange(3);
+        assert(equal(r, [ 3, 3, 3 ]));
+    
+}
+
+@safe unittest
+{
+    import std.range;
+
+        import std.algorithm.comparison : equal;
+        auto a = [ 1, 2, 3, 3, 3, 4, 4, 5, 6 ];
+        auto r = assumeSorted(a).trisect(3);
+        assert(equal(r[0], [ 1, 2 ]));
+        assert(equal(r[1], [ 3, 3, 3 ]));
+        assert(equal(r[2], [ 4, 4, 5, 6 ]));
+    
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.algorithm.sorting : sort;
+    auto a = [ 1, 2, 3, 42, 52, 64 ];
+    auto r = assumeSorted(a);
+    assert(r.contains(3));
+    assert(!(32 in r));
+    auto r1 = sort!"a > b"(a);
+    assert(3 in r1);
+    assert(!r1.contains(32));
+    assert(r1.release() == [ 64, 52, 42, 3, 2, 1 ]);
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.algorithm.mutation : swap;
+    auto a = [ 1, 2, 3, 42, 52, 64 ];
+    auto r = assumeSorted(a);
+    assert(r.contains(42));
+    swap(a[3], a[5]);         // illegal to break sortedness of original range
+    assert(!r.contains(42));  // passes although it shouldn't
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    static struct S { int i; }
+    static bool byI(A, B)(A a, B b)
+    {
+        static if (is(A == S))
+            return a.i < b;
+        else
+            return a < b.i;
+    }
+    auto r = assumeSorted!byI([S(1), S(2), S(3)]);
+    auto lessThanTwo = r.lowerBound(2);
+    assert(equal(lessThanTwo, [S(1)]));
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+
+    int[] a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
+    auto p = assumeSorted(a);
+
+    assert(equal(p.lowerBound(4), [0, 1, 2, 3]));
+    assert(equal(p.lowerBound(5), [0, 1, 2, 3, 4]));
+    assert(equal(p.lowerBound(6), [0, 1, 2, 3, 4, 5]));
+    assert(equal(p.lowerBound(6.9), [0, 1, 2, 3, 4, 5, 6]));
+}
+
+@system unittest
+{
+    import std.range;
+
+    import std.algorithm.searching : find;
+    ubyte[] buffer = [1, 9, 45, 12, 22];
+    auto found1 = find(buffer, 45);
+    assert(found1 == [45, 12, 22]);
+    assert(buffer == [1, 9, 45, 12, 22]);
+
+    auto wrapped1 = refRange(&buffer);
+    auto found2 = find(wrapped1, 45);
+    assert(*found2.ptr == [45, 12, 22]);
+    assert(buffer == [45, 12, 22]);
+
+    auto found3 = find(wrapped1.save, 22);
+    assert(*found3.ptr == [22]);
+    assert(buffer == [45, 12, 22]);
+
+    string str = "hello world";
+    auto wrappedStr = refRange(&str);
+    assert(str.front == 'h');
+    str.popFrontN(5);
+    assert(str == " world");
+    assert(wrappedStr.front == ' ');
+    assert(*wrappedStr.ptr == " world");
+}
+
+@system unittest
+{
+    import std.range;
+
+    ubyte[] buffer1 = [1, 2, 3, 4, 5];
+    ubyte[] buffer2 = [6, 7, 8, 9, 10];
+    auto wrapped1 = refRange(&buffer1);
+    auto wrapped2 = refRange(&buffer2);
+    assert(wrapped1.ptr is &buffer1);
+    assert(wrapped2.ptr is &buffer2);
+    assert(wrapped1.ptr !is wrapped2.ptr);
+    assert(buffer1 != buffer2);
+
+    wrapped1 = wrapped2;
+
+    //Everything points to the same stuff as before.
+    assert(wrapped1.ptr is &buffer1);
+    assert(wrapped2.ptr is &buffer2);
+    assert(wrapped1.ptr !is wrapped2.ptr);
+
+    //But buffer1 has changed due to the assignment.
+    assert(buffer1 == [6, 7, 8, 9, 10]);
+    assert(buffer2 == [6, 7, 8, 9, 10]);
+
+    buffer2 = [11, 12, 13, 14, 15];
+
+    //Everything points to the same stuff as before.
+    assert(wrapped1.ptr is &buffer1);
+    assert(wrapped2.ptr is &buffer2);
+    assert(wrapped1.ptr !is wrapped2.ptr);
+
+    //But buffer2 has changed due to the assignment.
+    assert(buffer1 == [6, 7, 8, 9, 10]);
+    assert(buffer2 == [11, 12, 13, 14, 15]);
+
+    wrapped2 = null;
+
+    //The pointer changed for wrapped2 but not wrapped1.
+    assert(wrapped1.ptr is &buffer1);
+    assert(wrapped2.ptr is null);
+    assert(wrapped1.ptr !is wrapped2.ptr);
+
+    //buffer2 is not affected by the assignment.
+    assert(buffer1 == [6, 7, 8, 9, 10]);
+    assert(buffer2 == [11, 12, 13, 14, 15]);
+}
+
+@safe pure unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    import std.format : format;
+
+    // 00000011 00001001
+    ubyte[] arr = [3, 9];
+    auto r = arr.bitwise;
+
+    // iterate through it as with any other range
+    assert(format("%(%d%)", r) == "1100000010010000");
+    assert(format("%(%d%)", r.retro).equal("1100000010010000".retro));
+
+    auto r2 = r[5 .. $];
+    // set a bit
+    r[2] = 1;
+    assert(arr[0] == 7);
+    assert(r[5] == r2[0]);
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    import std.random : rndGen;
+
+    auto rb = rndGen.bitwise;
+    static assert(isInfinite!(typeof(rb)));
+
+    auto rb2 = rndGen.bitwise;
+    // Don't forget that structs are passed by value
+    assert(rb.take(10).equal(rb2.take(10)));
+}
+
+@safe nothrow unittest
+{
+    import std.range;
+
+    import std.algorithm.iteration : map;
+    import std.algorithm.mutation : copy;
+    [4, 5, 6].map!(x => x * 2).copy(nullSink); // data is discarded
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.csv : csvNextToken;
+
+    string line = "a,b,c";
+
+    // ignore the first column
+    line.csvNextToken(nullSink, ',', '"');
+    line.popFront;
+
+    // look at the second column
+    Appender!string app;
+    line.csvNextToken(app, ',', '"');
+    assert(app.data == "b");
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+    import std.algorithm.iteration : filter, map;
+
+    // Sum values while copying
+    int[] values = [1, 4, 9, 16, 25];
+    int sum = 0;
+    auto newValues = values.tee!(a => sum += a).array;
+    assert(equal(newValues, values));
+    assert(sum == 1 + 4 + 9 + 16 + 25);
+
+    // Count values that pass the first filter
+    int count = 0;
+    auto newValues4 = values.filter!(a => a < 10)
+                            .tee!(a => count++)
+                            .map!(a => a + 1)
+                            .filter!(a => a < 10);
+
+    //Fine, equal also evaluates any lazy ranges passed to it.
+    //count is not 3 until equal evaluates newValues4
+    assert(equal(newValues4, [2, 5]));
+    assert(count == 3);
+}
+
+@safe pure unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+
+    assert([1, 2, 3, 4].padLeft(0, 6).equal([0, 0, 1, 2, 3, 4]));
+    assert([1, 2, 3, 4].padLeft(0, 3).equal([1, 2, 3, 4]));
+
+    assert("abc".padLeft('_', 6).equal("___abc"));
+}
+
+@safe pure unittest
+{
+    import std.range;
+
+    import std.algorithm.comparison : equal;
+
+    assert([1, 2, 3, 4].padRight(0, 6).equal([1, 2, 3, 4, 0, 0]));
+    assert([1, 2, 3, 4].padRight(0, 4).equal([1, 2, 3, 4]));
+
+    assert("abc".padRight('_', 6).equal("abc___"));
+}
+
+@safe unittest
+{
+    import std.range;
+
+    import std.path : chainPath;
+    import std.range : chain;
+
+    void someLibraryMethod(R)(R argument)
+    if (isSomeFiniteCharInputRange!R)
+    {
+        // implementation detail, would iterate over each character of argument
+    }
+
+    someLibraryMethod("simple strings work");
+    someLibraryMethod(chain("chained", " ", "strings", " ", "work"));
+    someLibraryMethod(chainPath("chained", "paths", "work"));
+    // you can also use custom structs implementing a char range
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_range_primitives.d b/libphobos/testsuite/libphobos.phobos/std_range_primitives.d
new file mode 100644
index 0000000000000000000000000000000000000000..eb4bc267c8b40b16dc1efe97945b9691af7afac4
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_range_primitives.d
@@ -0,0 +1,591 @@
+@safe unittest
+{
+    import std.range.primitives;
+
+    struct A {}
+    struct B
+    {
+        void popFront();
+        @property bool empty();
+        @property int front();
+    }
+    static assert(!isInputRange!A);
+    static assert( isInputRange!B);
+    static assert( isInputRange!(int[]));
+    static assert( isInputRange!(char[]));
+    static assert(!isInputRange!(char[4]));
+    static assert( isInputRange!(inout(int)[]));
+    static assert(!isInputRange!(int[], string));
+    static assert( isInputRange!(int[], int));
+    static assert( isInputRange!(int[], const int));
+    static assert(!isInputRange!(int[], immutable int));
+
+    static assert(!isInputRange!(const(int)[], int));
+    static assert( isInputRange!(const(int)[], const int));
+    static assert(!isInputRange!(const(int)[], immutable int));
+
+    static assert(!isInputRange!(immutable(int)[], int));
+    static assert( isInputRange!(immutable(int)[], const int));
+    static assert( isInputRange!(immutable(int)[], immutable int));
+
+    static struct NotDefaultConstructible
+    {
+        @disable this();
+        void popFront();
+        @property bool empty();
+        @property int front();
+    }
+    static assert( isInputRange!NotDefaultConstructible);
+
+    static struct NotDefaultConstructibleOrCopyable
+    {
+        @disable this();
+        @disable this(this);
+        void popFront();
+        @property bool empty();
+        @property int front();
+    }
+    static assert(isInputRange!NotDefaultConstructibleOrCopyable);
+
+    static struct Frontless
+    {
+        void popFront();
+        @property bool empty();
+    }
+    static assert(!isInputRange!Frontless);
+
+    static struct VoidFront
+    {
+        void popFront();
+        @property bool empty();
+        void front();
+    }
+    static assert(!isInputRange!VoidFront);
+}
+
+@safe pure unittest
+{
+    import std.range.primitives;
+
+    import std.traits : isSomeChar;
+
+    static struct A
+    {
+        string data;
+
+        void put(C)(C c)
+        if (isSomeChar!C)
+        {
+            data ~= c;
+        }
+    }
+    static assert(isOutputRange!(A, char));
+
+    auto a = A();
+    put(a, "Hello");
+    assert(a.data == "Hello");
+}
+
+@safe pure nothrow unittest
+{
+    import std.range.primitives;
+
+    int[] a = [1, 2, 3], b = [10, 20];
+    auto c = a;
+    put(a, b);
+    assert(c == [10, 20, 3]);
+    // at this point, a was advanced twice, so it only contains
+    // its last element while c represents the whole array
+    assert(a == [3]);
+}
+
+@safe pure unittest
+{
+    import std.range.primitives;
+
+    // the elements must be mutable, so using string or const(char)[]
+    // won't compile
+    char[] s1 = new char[13];
+    auto r1 = s1;
+    put(r1, "Hello, World!"w);
+    assert(s1 == "Hello, World!");
+}
+
+@safe unittest
+{
+    import std.range.primitives;
+
+    void myprint(scope const(char)[] s) { }
+    static assert(isOutputRange!(typeof(&myprint), char));
+
+    static assert( isOutputRange!(char[], char));
+    static assert( isOutputRange!(dchar[], wchar));
+    static assert( isOutputRange!(dchar[], dchar));
+}
+
+@safe unittest
+{
+    import std.range.primitives;
+
+    static assert(!isForwardRange!(int));
+    static assert( isForwardRange!(int[]));
+    static assert( isForwardRange!(inout(int)[]));
+
+    static assert( isForwardRange!(int[], const int));
+    static assert(!isForwardRange!(int[], immutable int));
+
+    static assert(!isForwardRange!(const(int)[], int));
+    static assert( isForwardRange!(const(int)[], const int));
+    static assert(!isForwardRange!(const(int)[], immutable int));
+
+    static assert(!isForwardRange!(immutable(int)[], int));
+    static assert( isForwardRange!(immutable(int)[], const int));
+    static assert( isForwardRange!(immutable(int)[], immutable int));
+}
+
+@safe unittest
+{
+    import std.range.primitives;
+
+    alias R = int[];
+    R r = [0,1];
+    static assert(isForwardRange!R);           // is forward range
+    r.popBack();                               // can invoke popBack
+    auto t = r.back;                           // can get the back of the range
+    auto w = r.front;
+    static assert(is(typeof(t) == typeof(w))); // same type for front and back
+
+    // Checking the element type
+    static assert( isBidirectionalRange!(int[], const int));
+    static assert(!isBidirectionalRange!(int[], immutable int));
+
+    static assert(!isBidirectionalRange!(const(int)[], int));
+    static assert( isBidirectionalRange!(const(int)[], const int));
+    static assert(!isBidirectionalRange!(const(int)[], immutable int));
+
+    static assert(!isBidirectionalRange!(immutable(int)[], int));
+    static assert( isBidirectionalRange!(immutable(int)[], const int));
+    static assert( isBidirectionalRange!(immutable(int)[], immutable int));
+}
+
+@safe unittest
+{
+    import std.range.primitives;
+
+    import std.traits : isAggregateType, isAutodecodableString;
+
+    alias R = int[];
+
+    // range is finite and bidirectional or infinite and forward.
+    static assert(isBidirectionalRange!R ||
+                  isForwardRange!R && isInfinite!R);
+
+    R r = [0,1];
+    auto e = r[1]; // can index
+    auto f = r.front;
+    static assert(is(typeof(e) == typeof(f))); // same type for indexed and front
+    static assert(!(isAutodecodableString!R && !isAggregateType!R)); // narrow strings cannot be indexed as ranges
+    static assert(hasLength!R || isInfinite!R); // must have length or be infinite
+
+    // $ must work as it does with arrays if opIndex works with $
+    static if (is(typeof(r[$])))
+    {
+        static assert(is(typeof(f) == typeof(r[$])));
+
+        // $ - 1 doesn't make sense with infinite ranges but needs to work
+        // with finite ones.
+        static if (!isInfinite!R)
+            static assert(is(typeof(f) == typeof(r[$ - 1])));
+    }
+
+    // Checking the element type
+    static assert( isRandomAccessRange!(int[], const int));
+    static assert(!isRandomAccessRange!(int[], immutable int));
+
+    static assert(!isRandomAccessRange!(const(int)[], int));
+    static assert( isRandomAccessRange!(const(int)[], const int));
+    static assert(!isRandomAccessRange!(const(int)[], immutable int));
+
+    static assert(!isRandomAccessRange!(immutable(int)[], int));
+    static assert( isRandomAccessRange!(immutable(int)[], const int));
+    static assert( isRandomAccessRange!(immutable(int)[], immutable int));
+}
+
+@safe unittest
+{
+    import std.range.primitives;
+
+    import std.algorithm.iteration : map;
+    import std.range : iota, repeat;
+
+    static struct HasPostblit
+    {
+        this(this) {}
+    }
+
+    auto nonMobile = map!"a"(repeat(HasPostblit.init));
+    static assert(!hasMobileElements!(typeof(nonMobile)));
+    static assert( hasMobileElements!(int[]));
+    static assert( hasMobileElements!(inout(int)[]));
+    static assert( hasMobileElements!(typeof(iota(1000))));
+
+    static assert( hasMobileElements!( string));
+    static assert( hasMobileElements!(dstring));
+    static assert( hasMobileElements!( char[]));
+    static assert( hasMobileElements!(dchar[]));
+}
+
+@safe unittest
+{
+    import std.range.primitives;
+
+    import std.range : iota;
+
+    // Standard arrays: returns the type of the elements of the array
+    static assert(is(ElementType!(int[]) == int));
+
+    // Accessing .front retrieves the decoded dchar
+    static assert(is(ElementType!(char[])  == dchar)); // rvalue
+    static assert(is(ElementType!(dchar[]) == dchar)); // lvalue
+
+    // Ditto
+    static assert(is(ElementType!(string) == dchar));
+    static assert(is(ElementType!(dstring) == immutable(dchar)));
+
+    // For ranges it gets the type of .front.
+    auto range = iota(0, 10);
+    static assert(is(ElementType!(typeof(range)) == int));
+}
+
+@safe unittest
+{
+    import std.range.primitives;
+
+    import std.range : iota;
+    // internally the range stores the encoded type
+    static assert(is(ElementEncodingType!(char[])  == char));
+
+    static assert(is(ElementEncodingType!(wstring) == immutable(wchar)));
+
+    static assert(is(ElementEncodingType!(byte[]) == byte));
+
+    auto range = iota(0, 10);
+    static assert(is(ElementEncodingType!(typeof(range)) == int));
+}
+
+@safe unittest
+{
+    import std.range.primitives;
+
+    static assert(!hasSwappableElements!(const int[]));
+    static assert(!hasSwappableElements!(const(int)[]));
+    static assert(!hasSwappableElements!(inout(int)[]));
+    static assert( hasSwappableElements!(int[]));
+
+    static assert(!hasSwappableElements!( string));
+    static assert(!hasSwappableElements!(dstring));
+    static assert(!hasSwappableElements!( char[]));
+    static assert( hasSwappableElements!(dchar[]));
+}
+
+@safe unittest
+{
+    import std.range.primitives;
+
+    static assert(!hasAssignableElements!(const int[]));
+    static assert(!hasAssignableElements!(const(int)[]));
+    static assert( hasAssignableElements!(int[]));
+    static assert(!hasAssignableElements!(inout(int)[]));
+
+    static assert(!hasAssignableElements!( string));
+    static assert(!hasAssignableElements!(dstring));
+    static assert(!hasAssignableElements!( char[]));
+    static assert( hasAssignableElements!(dchar[]));
+}
+
+@safe unittest
+{
+    import std.range.primitives;
+
+    import std.range : iota, chain;
+
+    static assert( hasLvalueElements!(int[]));
+    static assert( hasLvalueElements!(const(int)[]));
+    static assert( hasLvalueElements!(inout(int)[]));
+    static assert( hasLvalueElements!(immutable(int)[]));
+    static assert(!hasLvalueElements!(typeof(iota(3))));
+
+    static assert(!hasLvalueElements!( string));
+    static assert( hasLvalueElements!(dstring));
+    static assert(!hasLvalueElements!( char[]));
+    static assert( hasLvalueElements!(dchar[]));
+
+    auto c = chain([1, 2, 3], [4, 5, 6]);
+    static assert( hasLvalueElements!(typeof(c)));
+}
+
+@safe unittest
+{
+    import std.range.primitives;
+
+    static assert(!hasLength!(char[]));
+    static assert( hasLength!(int[]));
+    static assert( hasLength!(inout(int)[]));
+
+    struct A { size_t length() { return 0; } }
+    struct B { @property size_t length() { return 0; } }
+    static assert( hasLength!(A));
+    static assert( hasLength!(B));
+}
+
+@safe unittest
+{
+    import std.range.primitives;
+
+    import std.range : Repeat;
+    static assert(!isInfinite!(int[]));
+    static assert( isInfinite!(Repeat!(int)));
+}
+
+@safe unittest
+{
+    import std.range.primitives;
+
+    import std.range : takeExactly;
+    static assert( hasSlicing!(int[]));
+    static assert( hasSlicing!(const(int)[]));
+    static assert(!hasSlicing!(const int[]));
+    static assert( hasSlicing!(inout(int)[]));
+    static assert(!hasSlicing!(inout int []));
+    static assert( hasSlicing!(immutable(int)[]));
+    static assert(!hasSlicing!(immutable int[]));
+    static assert(!hasSlicing!string);
+    static assert( hasSlicing!dstring);
+
+    enum rangeFuncs = "@property int front();" ~
+                      "void popFront();" ~
+                      "@property bool empty();" ~
+                      "@property auto save() { return this; }" ~
+                      "@property size_t length();";
+
+    struct A { mixin(rangeFuncs); int opSlice(size_t, size_t); }
+    struct B { mixin(rangeFuncs); B opSlice(size_t, size_t); }
+    struct C { mixin(rangeFuncs); @disable this(); C opSlice(size_t, size_t); }
+    struct D { mixin(rangeFuncs); int[] opSlice(size_t, size_t); }
+    static assert(!hasSlicing!(A));
+    static assert( hasSlicing!(B));
+    static assert( hasSlicing!(C));
+    static assert(!hasSlicing!(D));
+
+    struct InfOnes
+    {
+        enum empty = false;
+        void popFront() {}
+        @property int front() { return 1; }
+        @property InfOnes save() { return this; }
+        auto opSlice(size_t i, size_t j) { return takeExactly(this, j - i); }
+        auto opSlice(size_t i, Dollar d) { return this; }
+
+        struct Dollar {}
+        Dollar opDollar() const { return Dollar.init; }
+    }
+
+    static assert(hasSlicing!InfOnes);
+}
+
+@safe unittest
+{
+    import std.range.primitives;
+
+    import std.range : iota;
+
+    assert(10.iota.walkLength == 10);
+    // iota has a length function, and therefore the
+    // doesn't have to be walked, and the upTo
+    // parameter is ignored
+    assert(10.iota.walkLength(5) == 10);
+}
+
+@safe unittest
+{
+    import std.range.primitives;
+
+    int[] a = [ 1, 2, 3, 4, 5 ];
+    a.popFrontN(2);
+    assert(a == [ 3, 4, 5 ]);
+    a.popFrontN(7);
+    assert(a == [ ]);
+}
+
+@safe unittest
+{
+    import std.range.primitives;
+
+    import std.algorithm.comparison : equal;
+    import std.range : iota;
+    auto LL = iota(1L, 7L);
+    auto r = popFrontN(LL, 2);
+    assert(equal(LL, [3L, 4L, 5L, 6L]));
+    assert(r == 2);
+}
+
+@safe unittest
+{
+    import std.range.primitives;
+
+    int[] a = [ 1, 2, 3, 4, 5 ];
+    a.popBackN(2);
+    assert(a == [ 1, 2, 3 ]);
+    a.popBackN(7);
+    assert(a == [ ]);
+}
+
+@safe unittest
+{
+    import std.range.primitives;
+
+    import std.algorithm.comparison : equal;
+    import std.range : iota;
+    auto LL = iota(1L, 7L);
+    auto r = popBackN(LL, 2);
+    assert(equal(LL, [1L, 2L, 3L, 4L]));
+    assert(r == 2);
+}
+
+@safe unittest
+{
+    import std.range.primitives;
+
+    import std.algorithm.comparison : equal;
+    import std.algorithm.iteration : filterBidirectional;
+
+    auto a = [1, 2, 3];
+    a.popFrontExactly(1);
+    assert(a == [2, 3]);
+    a.popBackExactly(1);
+    assert(a == [2]);
+
+    string s = "日本語";
+    s.popFrontExactly(1);
+    assert(s == "本語");
+    s.popBackExactly(1);
+    assert(s == "本");
+
+    auto bd = filterBidirectional!"true"([1, 2, 3]);
+    bd.popFrontExactly(1);
+    assert(bd.equal([2, 3]));
+    bd.popBackExactly(1);
+    assert(bd.equal([2]));
+}
+
+@safe unittest
+{
+    import std.range.primitives;
+
+    auto a = [ 1, 2, 3 ];
+    assert(moveFront(a) == 1);
+    assert(a.length == 3);
+
+    // define a perfunctory input range
+    struct InputRange
+    {
+        enum bool empty = false;
+        enum int front = 7;
+        void popFront() {}
+        int moveFront() { return 43; }
+    }
+    InputRange r;
+    // calls r.moveFront
+    assert(moveFront(r) == 43);
+}
+
+@safe unittest
+{
+    import std.range.primitives;
+
+    struct TestRange
+    {
+        int payload = 5;
+        @property bool empty() { return false; }
+        @property TestRange save() { return this; }
+        @property ref int front() return { return payload; }
+        @property ref int back() return { return payload; }
+        void popFront() { }
+        void popBack() { }
+    }
+    static assert(isBidirectionalRange!TestRange);
+    TestRange r;
+    auto x = moveBack(r);
+    assert(x == 5);
+}
+
+@safe unittest
+{
+    import std.range.primitives;
+
+    auto a = [1,2,3,4];
+    foreach (idx, it; a)
+    {
+        assert(it == moveAt(a, idx));
+    }
+}
+
+@safe pure nothrow unittest
+{
+    import std.range.primitives;
+
+    auto a = [ 1, 2, 3 ];
+    assert(!a.empty);
+    assert(a[3 .. $].empty);
+
+    int[string] b;
+    assert(b.empty);
+    b["zero"] = 0;
+    assert(!b.empty);
+}
+
+@safe pure nothrow unittest
+{
+    import std.range.primitives;
+
+    auto a = [ 1, 2, 3 ];
+    auto b = a.save;
+    assert(b is a);
+}
+
+@safe pure nothrow unittest
+{
+    import std.range.primitives;
+
+    auto a = [ 1, 2, 3 ];
+    a.popFront();
+    assert(a == [ 2, 3 ]);
+}
+
+@safe pure nothrow unittest
+{
+    import std.range.primitives;
+
+    auto a = [ 1, 2, 3 ];
+    a.popBack();
+    assert(a == [ 1, 2 ]);
+}
+
+@safe pure nothrow unittest
+{
+    import std.range.primitives;
+
+    int[] a = [ 1, 2, 3 ];
+    assert(a.front == 1);
+}
+
+@safe pure nothrow unittest
+{
+    import std.range.primitives;
+
+    int[] a = [ 1, 2, 3 ];
+    assert(a.back == 3);
+    a.back += 4;
+    assert(a.back == 7);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_regex_package.d b/libphobos/testsuite/libphobos.phobos/std_regex_package.d
new file mode 100644
index 0000000000000000000000000000000000000000..448c9db1aea91bd0d0483cec1e0bc29f401b3d43
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_regex_package.d
@@ -0,0 +1,175 @@
+@system unittest
+{
+    import std.regex;
+
+    void test(S)()
+    {
+        // multi-pattern regex example
+        S[] arr = [`([a-z]+):(\d+)`, `(\d+),\d+`];
+        auto multi = regex(arr); // multi regex
+        S str = "abc:43 12,34";
+        auto m = str.matchAll(multi);
+        assert(m.front.whichPattern == 1);
+        assert(m.front[1] == "abc");
+        assert(m.front[2] == "43");
+        m.popFront();
+        assert(m.front.whichPattern == 2);
+        assert(m.front[1] == "12");
+    }
+
+    import std.meta : AliasSeq;
+    static foreach (C; AliasSeq!(string, wstring, dstring))
+        // Test with const array of patterns - see https://issues.dlang.org/show_bug.cgi?id=20301
+        static foreach (S; AliasSeq!(C, const C, immutable C))
+            test!S();
+}
+
+@system unittest
+{
+    import std.regex;
+
+        import std.regex;
+        assert(matchFirst("abc", "[0-9]+", "[a-z]+").whichPattern == 2);
+    
+}
+
+@system unittest
+{
+    import std.regex;
+
+    import std.range.primitives : popFrontN;
+
+    auto c = matchFirst("@abc#", regex(`(\w)(\w)(\w)`));
+    assert(c.pre == "@"); // Part of input preceding match
+    assert(c.post == "#"); // Immediately after match
+    assert(c.hit == c[0] && c.hit == "abc"); // The whole match
+    assert(c[2] == "b");
+    assert(c.front == "abc");
+    c.popFront();
+    assert(c.front == "a");
+    assert(c.back == "c");
+    c.popBack();
+    assert(c.back == "b");
+    popFrontN(c, 2);
+    assert(c.empty);
+
+    assert(!matchFirst("nothing", "something"));
+
+    // Captures that are not matched will be null.
+    c = matchFirst("ac", regex(`a(b)?c`));
+    assert(c);
+    assert(!c[1]);
+}
+
+@system unittest
+{
+    import std.regex;
+
+    assert(replaceFirst("noon", regex("n"), "[$&]") == "[n]oon");
+}
+
+@system unittest
+{
+    import std.regex;
+
+    import std.conv : to;
+    string list = "#21 out of 46";
+    string newList = replaceFirst!(cap => to!string(to!int(cap.hit)+1))
+        (list, regex(`[0-9]+`));
+    assert(newList == "#22 out of 46");
+}
+
+@system unittest
+{
+    import std.regex;
+
+    import std.array;
+    string m1 = "first message\n";
+    string m2 = "second message\n";
+    auto result = appender!string();
+    replaceFirstInto(result, m1, regex(`([a-z]+) message`), "$1");
+    //equivalent of the above with user-defined callback
+    replaceFirstInto!(cap=>cap[1])(result, m2, regex(`([a-z]+) message`));
+    assert(result.data == "first\nsecond\n");
+}
+
+@system unittest
+{
+    import std.regex;
+
+    // insert comma as thousands delimiter
+    auto re = regex(r"(?<=\d)(?=(\d\d\d)+\b)","g");
+    assert(replaceAll("12000 + 42100 = 54100", re, ",") == "12,000 + 42,100 = 54,100");
+}
+
+@system unittest
+{
+    import std.regex;
+
+    string baz(Captures!(string) m)
+    {
+        import std.string : toUpper;
+        return toUpper(m.hit);
+    }
+    // Capitalize the letters 'a' and 'r':
+    auto s = replaceAll!(baz)("Strap a rocket engine on a chicken.",
+            regex("[ar]"));
+    assert(s == "StRAp A Rocket engine on A chicken.");
+}
+
+@system unittest
+{
+    import std.regex;
+
+    // insert comma as thousands delimiter in fifty randomly produced big numbers
+    import std.array, std.conv, std.random, std.range;
+    static re = regex(`(?<=\d)(?=(\d\d\d)+\b)`, "g");
+    auto sink = appender!(char [])();
+    enum ulong min = 10UL ^^ 10, max = 10UL ^^ 19;
+    foreach (i; 0 .. 50)
+    {
+        sink.clear();
+        replaceAllInto(sink, text(uniform(min, max)), re, ",");
+        foreach (pos; iota(sink.data.length - 4, 0, -4))
+            assert(sink.data[pos] == ',');
+    }
+}
+
+@system unittest
+{
+    import std.regex;
+
+    import std.algorithm.comparison : equal;
+    auto s1 = ", abc, de,  fg, hi, ";
+    assert(equal(splitter(s1, regex(", *")),
+        ["", "abc", "de", "fg", "hi", ""]));
+}
+
+@system unittest
+{
+    import std.regex;
+
+    import std.algorithm.comparison : equal;
+    import std.typecons : Yes;
+
+    auto pattern = regex(`([\.,])`);
+
+    assert("2003.04.05"
+        .splitter!(Yes.keepSeparators)(pattern)
+        .equal(["2003", ".", "04", ".", "05"]));
+
+    assert(",1,2,3"
+        .splitter!(Yes.keepSeparators)(pattern)
+        .equal([",", "1", ",", "2", ",", "3"]));
+}
+
+@system unittest
+{
+    import std.regex;
+
+    import std.algorithm.comparison;
+    import std.regex;
+    string s = `This is {unfriendly} to *regex*`;
+    assert(s.escaper.equal(`This is \{unfriendly\} to \*regex\*`));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_signals.d b/libphobos/testsuite/libphobos.phobos/std_signals.d
new file mode 100644
index 0000000000000000000000000000000000000000..255dbde7d754b3cab5c211871b9f7f0874e3920a
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_signals.d
@@ -0,0 +1,85 @@
+@system unittest
+{
+    import std.signals;
+
+    import std.signals;
+
+    int observedMessageCounter = 0;
+
+    class Observer
+    {   // our slot
+        void watch(string msg, int value)
+        {
+            switch (observedMessageCounter++)
+            {
+                case 0:
+                    assert(msg == "setting new value");
+                    assert(value == 4);
+                    break;
+                case 1:
+                    assert(msg == "setting new value");
+                    assert(value == 6);
+                    break;
+                default:
+                    assert(0, "Unknown observation");
+            }
+        }
+    }
+
+    class Observer2
+    {   // our slot
+        void watch(string msg, int value)
+        {
+        }
+    }
+
+    class Foo
+    {
+        int value() { return _value; }
+
+        int value(int v)
+        {
+            if (v != _value)
+            {   _value = v;
+                // call all the connected slots with the two parameters
+                emit("setting new value", v);
+            }
+            return v;
+        }
+
+        // Mix in all the code we need to make Foo into a signal
+        mixin Signal!(string, int);
+
+      private :
+        int _value;
+    }
+
+    Foo a = new Foo;
+    Observer o = new Observer;
+    auto o2 = new Observer2;
+    auto o3 = new Observer2;
+    auto o4 = new Observer2;
+    auto o5 = new Observer2;
+
+    a.value = 3;                // should not call o.watch()
+    a.connect(&o.watch);        // o.watch is the slot
+    a.connect(&o2.watch);
+    a.connect(&o3.watch);
+    a.connect(&o4.watch);
+    a.connect(&o5.watch);
+    a.value = 4;                // should call o.watch()
+    a.disconnect(&o.watch);     // o.watch is no longer a slot
+    a.disconnect(&o3.watch);
+    a.disconnect(&o5.watch);
+    a.disconnect(&o4.watch);
+    a.disconnect(&o2.watch);
+    a.value = 5;                // so should not call o.watch()
+    a.connect(&o2.watch);
+    a.connect(&o.watch);        // connect again
+    a.value = 6;                // should call o.watch()
+    destroy(o);                 // destroying o should automatically disconnect it
+    a.value = 7;                // should not call o.watch()
+
+    assert(observedMessageCounter == 2);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_socket.d b/libphobos/testsuite/libphobos.phobos/std_socket.d
new file mode 100644
index 0000000000000000000000000000000000000000..5a63193dc7cca87192d4c813638ed5f23e8f245b
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_socket.d
@@ -0,0 +1,66 @@
+@safe unittest
+{
+    import std.socket;
+
+    InternetHost ih = new InternetHost;
+
+    ih.getHostByAddr(0x7F_00_00_01);
+    assert(ih.addrList[0] == 0x7F_00_00_01);
+    ih.getHostByAddr("127.0.0.1");
+    assert(ih.addrList[0] == 0x7F_00_00_01);
+
+    if (!ih.getHostByName("www.digitalmars.com"))
+        return;             // don't fail if not connected to internet
+
+    assert(ih.addrList.length);
+    InternetAddress ia = new InternetAddress(ih.addrList[0], InternetAddress.PORT_ANY);
+    assert(ih.name == "www.digitalmars.com" || ih.name == "digitalmars.com",
+            ih.name);
+
+    /* The following assert randomly fails in the test suite.
+     * https://issues.dlang.org/show_bug.cgi?id=22791
+     * So just ignore it when it fails.
+     */
+    //assert(ih.getHostByAddr(ih.addrList[0]));
+    if (ih.getHostByAddr(ih.addrList[0]))
+    {
+        string getHostNameFromInt = ih.name.dup;
+
+        // This randomly fails in the compiler test suite
+        //assert(ih.getHostByAddr(ia.toAddrString()));
+
+        if (ih.getHostByAddr(ia.toAddrString()))
+        {
+            string getHostNameFromStr = ih.name.dup;
+            assert(getHostNameFromInt == getHostNameFromStr);
+        }
+    }
+}
+
+@system unittest
+{
+    import std.socket;
+
+        auto addr1 = new InternetAddress("127.0.0.1", 80);
+        auto addr2 = new InternetAddress("127.0.0.2", 80);
+
+        assert(addr1 == addr1);
+        assert(addr1 != addr2);
+    
+}
+
+@safe unittest
+{
+    import std.socket;
+
+    immutable ubyte[4] data = [1, 2, 3, 4];
+    auto pair = socketPair();
+    scope(exit) foreach (s; pair) s.close();
+
+    pair[0].send(data[]);
+
+    auto buf = new ubyte[data.length];
+    pair[1].receive(buf);
+    assert(buf == data);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_stdio.d b/libphobos/testsuite/libphobos.phobos/std_stdio.d
new file mode 100644
index 0000000000000000000000000000000000000000..43b3cf6aa50d807c64d2053659cf350e6a298803
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_stdio.d
@@ -0,0 +1,185 @@
+@system unittest
+{
+    import std.stdio;
+
+        static import std.file;
+
+        auto testFile = std.file.deleteme();
+        std.file.write(testFile, "\r\n\n\r\n");
+        scope(exit) std.file.remove(testFile);
+
+        auto f = File(testFile, "r");
+        auto buf = f.rawRead(new char[5]);
+        f.close();
+        assert(buf == "\r\n\n\r\n");
+    
+}
+
+@system unittest
+{
+    import std.stdio;
+
+        static import std.file;
+
+        auto testFile = std.file.deleteme();
+        auto f = File(testFile, "w");
+        scope(exit) std.file.remove(testFile);
+
+        f.rawWrite("\r\n\n\r\n");
+        f.close();
+        assert(std.file.read(testFile) == "\r\n\n\r\n");
+    
+}
+
+@system unittest
+{
+    import std.stdio;
+
+        import std.conv : text;
+        static import std.file;
+
+        auto testFile = std.file.deleteme();
+        std.file.write(testFile, "abcdefghijklmnopqrstuvwqxyz");
+        scope(exit) { std.file.remove(testFile); }
+
+        auto f = File(testFile);
+        auto a = new ubyte[4];
+        f.rawRead(a);
+        assert(f.tell == 4, text(f.tell));
+    
+}
+
+@system unittest
+{
+    import std.stdio;
+
+        static import std.file;
+
+        auto deleteme = std.file.deleteme();
+        std.file.write(deleteme, "hello\nworld\ntrue\nfalse\n");
+        scope(exit) std.file.remove(deleteme);
+        string s;
+        auto f = File(deleteme);
+        f.readf!"%s\n"(s);
+        assert(s == "hello", "["~s~"]");
+        f.readf("%s\n", s);
+        assert(s == "world", "["~s~"]");
+
+        bool b1, b2;
+        f.readf("%s\n%s\n", b1, b2);
+        assert(b1 == true && b2 == false);
+    
+}
+
+@system unittest
+{
+    import std.stdio;
+
+         static import std.file;
+         import std.typecons : tuple;
+
+         // prepare test file
+         auto testFile = std.file.deleteme();
+         scope(failure) printf("Failed test at line %d\n", __LINE__);
+         std.file.write(testFile, "1 2\n4 1\n5 100");
+         scope(exit) std.file.remove(testFile);
+
+         File f = File(testFile);
+         scope(exit) f.close();
+
+         auto expected = [tuple(1, 2), tuple(4, 1), tuple(5, 100)];
+         uint i;
+         foreach (e; f.byRecord!(int, int)("%s %s"))
+         {
+             assert(e == expected[i++]);
+         }
+    
+}
+
+@safe unittest
+{
+    import std.stdio;
+
+    static assert(isFileHandle!(FILE*));
+    static assert(isFileHandle!(File));
+}
+
+@safe unittest
+{
+    import std.stdio;
+
+    // Read stdin, sort lines, write to stdout
+    import std.algorithm.mutation : copy;
+    import std.algorithm.sorting : sort;
+    import std.array : array;
+    import std.typecons : Yes;
+
+    void main()
+    {
+        stdin                       // read from stdin
+        .byLineCopy(Yes.keepTerminator) // copying each line
+        .array()                    // convert to array of lines
+        .sort()                     // sort the lines
+        .copy(                      // copy output of .sort to an OutputRange
+            stdout.lockingTextWriter()); // the OutputRange
+    }
+}
+
+@safe unittest
+{
+    import std.stdio;
+
+    void main()
+    {
+        stdout.writeln("Write a message to stdout.");
+    }
+}
+
+@safe unittest
+{
+    import std.stdio;
+
+    void main()
+    {
+        import std.algorithm.iteration : filter, map, sum;
+        import std.format : format;
+        import std.range : iota, tee;
+
+        int len;
+        const r = 6.iota
+                  .filter!(a => a % 2) // 1 3 5
+                  .map!(a => a * 2) // 2 6 10
+                  .tee!(_ => stdout.writefln("len: %d", len++))
+                  .sum;
+
+        assert(r == 18);
+    }
+}
+
+@safe unittest
+{
+    import std.stdio;
+
+    void main()
+    {
+        import std.algorithm.mutation : copy;
+        import std.algorithm.iteration : map;
+        import std.format : format;
+        import std.range : iota;
+
+        10.iota
+        .map!(e => "N: %d".format(e))
+        .copy(stdout.lockingTextWriter()); // the OutputRange
+    }
+}
+
+@safe unittest
+{
+    import std.stdio;
+
+    void main()
+    {
+        stderr.writeln("Write a message to stderr.");
+    }
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_string.d b/libphobos/testsuite/libphobos.phobos/std_string.d
new file mode 100644
index 0000000000000000000000000000000000000000..256b5a77287b291a867b20c1a235e64215dd0d29
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_string.d
@@ -0,0 +1,892 @@
+@safe pure unittest
+{
+    import std.string;
+
+    import std.exception : assertThrown;
+    auto bad = "      a\n\tb\n   c";
+    assertThrown!StringException(bad.outdent);
+}
+
+@system pure unittest
+{
+    import std.string;
+
+    assert(fromStringz("foo\0"c.ptr) == "foo"c);
+    assert(fromStringz("foo\0"w.ptr) == "foo"w);
+    assert(fromStringz("foo\0"d.ptr) == "foo"d);
+
+    assert(fromStringz("福\0"c.ptr) == "福"c);
+    assert(fromStringz("福\0"w.ptr) == "福"w);
+    assert(fromStringz("福\0"d.ptr) == "福"d);
+}
+
+@nogc @safe pure nothrow unittest
+{
+    import std.string;
+
+    struct C
+    {
+        char[32] name;
+    }
+    assert(C("foo\0"c).name.fromStringz() == "foo"c);
+
+    struct W
+    {
+        wchar[32] name;
+    }
+    assert(W("foo\0"w).name.fromStringz() == "foo"w);
+
+    struct D
+    {
+        dchar[32] name;
+    }
+    assert(D("foo\0"d).name.fromStringz() == "foo"d);
+}
+
+pure nothrow @system unittest
+{
+    import std.string;
+
+    import core.stdc.string : strlen;
+    import std.conv : to;
+
+    auto p = toStringz("foo");
+    assert(strlen(p) == 3);
+    const(char)[] foo = "abbzxyzzy";
+    p = toStringz(foo[3 .. 5]);
+    assert(strlen(p) == 2);
+
+    string test = "";
+    p = toStringz(test);
+    assert(*p == 0);
+
+    test = "\0";
+    p = toStringz(test);
+    assert(*p == 0);
+
+    test = "foo\0";
+    p = toStringz(test);
+    assert(p[0] == 'f' && p[1] == 'o' && p[2] == 'o' && p[3] == 0);
+
+    const string test2 = "";
+    p = toStringz(test2);
+    assert(*p == 0);
+
+    assert(toStringz([]) is toStringz(""));
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    import std.typecons : No;
+
+    string s = "Hello World";
+    assert(indexOf(s, 'W') == 6);
+    assert(indexOf(s, 'Z') == -1);
+    assert(indexOf(s, 'w', No.caseSensitive) == 6);
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    import std.typecons : No;
+
+    string s = "Hello World";
+    assert(indexOf(s, 'W', 4) == 6);
+    assert(indexOf(s, 'Z', 100) == -1);
+    assert(indexOf(s, 'w', 3, No.caseSensitive) == 6);
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    import std.typecons : No;
+
+    string s = "Hello World";
+    assert(indexOf(s, "Wo", 4) == 6);
+    assert(indexOf(s, "Zo", 100) == -1);
+    assert(indexOf(s, "wo", 3, No.caseSensitive) == 6);
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    import std.typecons : No;
+
+    string s = "Hello World";
+    assert(indexOf(s, "Wo") == 6);
+    assert(indexOf(s, "Zo") == -1);
+    assert(indexOf(s, "wO", No.caseSensitive) == 6);
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    import std.typecons : No;
+
+    string s = "Hello World";
+    assert(lastIndexOf(s, 'l') == 9);
+    assert(lastIndexOf(s, 'Z') == -1);
+    assert(lastIndexOf(s, 'L', No.caseSensitive) == 9);
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    import std.typecons : No;
+
+    string s = "Hello World";
+    assert(lastIndexOf(s, 'l', 4) == 3);
+    assert(lastIndexOf(s, 'Z', 1337) == -1);
+    assert(lastIndexOf(s, 'L', 7, No.caseSensitive) == 3);
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    import std.typecons : No;
+
+    string s = "Hello World";
+    assert(lastIndexOf(s, "ll") == 2);
+    assert(lastIndexOf(s, "Zo") == -1);
+    assert(lastIndexOf(s, "lL", No.caseSensitive) == 2);
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    import std.typecons : No;
+
+    string s = "Hello World";
+    assert(lastIndexOf(s, "ll", 4) == 2);
+    assert(lastIndexOf(s, "Zo", 128) == -1);
+    assert(lastIndexOf(s, "lL", 3, No.caseSensitive) == -1);
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    import std.conv : to;
+
+    ptrdiff_t i = "helloWorld".indexOfAny("Wr");
+    assert(i == 5);
+    i = "öällo world".indexOfAny("lo ");
+    assert(i == 4, to!string(i));
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    import std.conv : to;
+
+    ptrdiff_t i = "helloWorld".indexOfAny("Wr", 4);
+    assert(i == 5);
+
+    i = "Foo öällo world".indexOfAny("lh", 3);
+    assert(i == 8, to!string(i));
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    ptrdiff_t i = "helloWorld".lastIndexOfAny("Wlo");
+    assert(i == 8);
+
+    i = "Foo öäöllo world".lastIndexOfAny("öF");
+    assert(i == 8);
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    import std.conv : to;
+
+    ptrdiff_t i = "helloWorld".lastIndexOfAny("Wlo", 4);
+    assert(i == 3);
+
+    i = "Foo öäöllo world".lastIndexOfAny("öF", 3);
+    assert(i == 0);
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    assert(indexOfNeither("abba", "a", 2) == 2);
+    assert(indexOfNeither("def", "de", 1) == 2);
+    assert(indexOfNeither("dfefffg", "dfe", 4) == 6);
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    assert(indexOfNeither("def", "a") == 0);
+    assert(indexOfNeither("def", "de") == 2);
+    assert(indexOfNeither("dfefffg", "dfe") == 6);
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    assert(lastIndexOfNeither("abba", "a") == 2);
+    assert(lastIndexOfNeither("def", "f") == 1);
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    assert(lastIndexOfNeither("def", "rsa", 3) == -1);
+    assert(lastIndexOfNeither("abba", "a", 2) == 1);
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    string s = "hello";
+    static assert(is(typeof(representation(s)) == immutable(ubyte)[]));
+    assert(representation(s) is cast(immutable(ubyte)[]) s);
+    assert(representation(s) == [0x68, 0x65, 0x6c, 0x6c, 0x6f]);
+}
+
+pure @safe unittest
+{
+    import std.string;
+
+    assert(capitalize("hello") == "Hello");
+    assert(capitalize("World") == "World");
+}
+
+@safe pure nothrow unittest
+{
+    import std.string;
+
+    string s = "Hello\nmy\rname\nis";
+    assert(splitLines(s) == ["Hello", "my", "name", "is"]);
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    import std.array : array;
+
+    string s = "Hello\nmy\rname\nis";
+
+    /* notice the call to 'array' to turn the lazy range created by
+    lineSplitter comparable to the string[] created by splitLines.
+    */
+    assert(lineSplitter(s).array == splitLines(s));
+}
+
+@nogc @safe pure unittest
+{
+    import std.string;
+
+    auto s = "\rpeter\n\rpaul\r\njerry\u2028ice\u2029cream\n\nsunday\nmon\u2030day\n";
+    auto lines = s.lineSplitter();
+    static immutable witness = ["", "peter", "", "paul", "jerry", "ice", "cream", "", "sunday", "mon\u2030day"];
+    uint i;
+    foreach (line; lines)
+    {
+        assert(line == witness[i++]);
+    }
+    assert(i == witness.length);
+}
+
+nothrow @safe pure unittest
+{
+    import std.string;
+
+    import std.uni : lineSep, paraSep;
+    assert(stripLeft("     hello world     ") ==
+           "hello world     ");
+    assert(stripLeft("\n\t\v\rhello world\n\t\v\r") ==
+           "hello world\n\t\v\r");
+    assert(stripLeft(" \u2028hello world") ==
+           "hello world");
+    assert(stripLeft("hello world") ==
+           "hello world");
+    assert(stripLeft([lineSep] ~ "hello world" ~ lineSep) ==
+           "hello world" ~ [lineSep]);
+    assert(stripLeft([paraSep] ~ "hello world" ~ paraSep) ==
+           "hello world" ~ [paraSep]);
+
+    import std.array : array;
+    import std.utf : byChar;
+    assert(stripLeft("     hello world     "w.byChar).array ==
+           "hello world     ");
+    assert(stripLeft("     \u2022hello world     ".byChar).array ==
+           "\u2022hello world     ");
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    assert(stripLeft("     hello world     ", " ") ==
+           "hello world     ");
+    assert(stripLeft("xxxxxhello world     ", "x") ==
+           "hello world     ");
+    assert(stripLeft("xxxyy    hello world     ", "xy ") ==
+           "hello world     ");
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    import std.array : array;
+    import std.utf : byChar, byWchar, byDchar;
+
+    assert(stripLeft("  xxxyy hello world     "w.byChar, "xy ").array ==
+           "hello world     ");
+
+    assert(stripLeft("\u2028\u2020hello world\u2028"w.byWchar,
+                     "\u2028").array == "\u2020hello world\u2028");
+    assert(stripLeft("\U00010001hello world"w.byWchar, " ").array ==
+           "\U00010001hello world"w);
+    assert(stripLeft("\U00010001 xyhello world"d.byDchar,
+                     "\U00010001 xy").array == "hello world"d);
+
+    assert(stripLeft("\u2020hello"w, "\u2020"w) == "hello"w);
+    assert(stripLeft("\U00010001hello"d, "\U00010001"d) == "hello"d);
+    assert(stripLeft(" hello ", "") == " hello ");
+}
+
+nothrow @safe pure unittest
+{
+    import std.string;
+
+    import std.uni : lineSep, paraSep;
+    assert(stripRight("     hello world     ") ==
+           "     hello world");
+    assert(stripRight("\n\t\v\rhello world\n\t\v\r") ==
+           "\n\t\v\rhello world");
+    assert(stripRight("hello world") ==
+           "hello world");
+    assert(stripRight([lineSep] ~ "hello world" ~ lineSep) ==
+           [lineSep] ~ "hello world");
+    assert(stripRight([paraSep] ~ "hello world" ~ paraSep) ==
+           [paraSep] ~ "hello world");
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    assert(stripRight("     hello world     ", "x") ==
+           "     hello world     ");
+    assert(stripRight("     hello world     ", " ") ==
+           "     hello world");
+    assert(stripRight("     hello worldxy     ", "xy ") ==
+           "     hello world");
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    import std.uni : lineSep, paraSep;
+    assert(strip("     hello world     ") ==
+           "hello world");
+    assert(strip("\n\t\v\rhello world\n\t\v\r") ==
+           "hello world");
+    assert(strip("hello world") ==
+           "hello world");
+    assert(strip([lineSep] ~ "hello world" ~ [lineSep]) ==
+           "hello world");
+    assert(strip([paraSep] ~ "hello world" ~ [paraSep]) ==
+           "hello world");
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    assert(strip("     hello world     ", "x") ==
+           "     hello world     ");
+    assert(strip("     hello world     ", " ") ==
+           "hello world");
+    assert(strip("   xyxyhello worldxyxy     ", "xy ") ==
+           "hello world");
+    assert(strip("\u2020hello\u2020"w, "\u2020"w) == "hello"w);
+    assert(strip("\U00010001hello\U00010001"d, "\U00010001"d) == "hello"d);
+    assert(strip(" hello ", "") == " hello ");
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    assert(strip("xxhelloyy", "x", "y") == "hello");
+    assert(strip("   xyxyhello worldxyxyzz    ", "xy ", "xyz ") ==
+           "hello world");
+    assert(strip("\u2020hello\u2028"w, "\u2020"w, "\u2028"w) == "hello"w);
+    assert(strip("\U00010001hello\U00010002"d, "\U00010001"d, "\U00010002"d) ==
+           "hello"d);
+    assert(strip(" hello ", "", "") == " hello ");
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    import std.uni : lineSep, paraSep, nelSep;
+    import std.utf : decode;
+    assert(chomp(" hello world  \n\r") == " hello world  \n");
+    assert(chomp(" hello world  \r\n") == " hello world  ");
+    assert(chomp(" hello world  \f") == " hello world  ");
+    assert(chomp(" hello world  \v") == " hello world  ");
+    assert(chomp(" hello world  \n\n") == " hello world  \n");
+    assert(chomp(" hello world  \n\n ") == " hello world  \n\n ");
+    assert(chomp(" hello world  \n\n" ~ [lineSep]) == " hello world  \n\n");
+    assert(chomp(" hello world  \n\n" ~ [paraSep]) == " hello world  \n\n");
+    assert(chomp(" hello world  \n\n" ~ [ nelSep]) == " hello world  \n\n");
+    assert(chomp(" hello world ") == " hello world ");
+    assert(chomp(" hello world") == " hello world");
+    assert(chomp("") == "");
+
+    assert(chomp(" hello world", "orld") == " hello w");
+    assert(chomp(" hello world", " he") == " hello world");
+    assert(chomp("", "hello") == "");
+
+    // Don't decode pointlessly
+    assert(chomp("hello\xFE", "\r") == "hello\xFE");
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    assert(chompPrefix("hello world", "he") == "llo world");
+    assert(chompPrefix("hello world", "hello w") == "orld");
+    assert(chompPrefix("hello world", " world") == "hello world");
+    assert(chompPrefix("", "hello") == "");
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    assert(chop("hello world") == "hello worl");
+    assert(chop("hello world\n") == "hello world");
+    assert(chop("hello world\r") == "hello world");
+    assert(chop("hello world\n\r") == "hello world\n");
+    assert(chop("hello world\r\n") == "hello world");
+    assert(chop("Walter Bright") == "Walter Brigh");
+    assert(chop("") == "");
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    assert(leftJustify("hello", 7, 'X') == "helloXX");
+    assert(leftJustify("hello", 2, 'X') == "hello");
+    assert(leftJustify("hello", 9, 'X') == "helloXXXX");
+}
+
+@safe pure @nogc nothrow unittest
+{
+    import std.string;
+
+    import std.algorithm.comparison : equal;
+    import std.utf : byChar;
+    assert(leftJustifier("hello", 2).equal("hello".byChar));
+    assert(leftJustifier("hello", 7).equal("hello  ".byChar));
+    assert(leftJustifier("hello", 7, 'x').equal("helloxx".byChar));
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    assert(rightJustify("hello", 7, 'X') == "XXhello");
+    assert(rightJustify("hello", 2, 'X') == "hello");
+    assert(rightJustify("hello", 9, 'X') == "XXXXhello");
+}
+
+@safe pure @nogc nothrow unittest
+{
+    import std.string;
+
+    import std.algorithm.comparison : equal;
+    import std.utf : byChar;
+    assert(rightJustifier("hello", 2).equal("hello".byChar));
+    assert(rightJustifier("hello", 7).equal("  hello".byChar));
+    assert(rightJustifier("hello", 7, 'x').equal("xxhello".byChar));
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    assert(center("hello", 7, 'X') == "XhelloX");
+    assert(center("hello", 2, 'X') == "hello");
+    assert(center("hello", 9, 'X') == "XXhelloXX");
+}
+
+@safe pure @nogc nothrow unittest
+{
+    import std.string;
+
+    import std.algorithm.comparison : equal;
+    import std.utf : byChar;
+    assert(centerJustifier("hello", 2).equal("hello".byChar));
+    assert(centerJustifier("hello", 8).equal(" hello  ".byChar));
+    assert(centerJustifier("hello", 7, 'x').equal("xhellox".byChar));
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    assert(detab(" \n\tx", 9) == " \n         x");
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    import std.array : array;
+
+    assert(detabber(" \n\tx", 9).array == " \n         x");
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    import std.array : array;
+    import std.utf : byChar, byWchar;
+
+    assert(detabber(" \u2029\t".byChar, 9).array == " \u2029         ");
+    auto r = "hel\tx".byWchar.detabber();
+    assert(r.front == 'h');
+    auto s = r.save;
+    r.popFront();
+    r.popFront();
+    assert(r.front == 'l');
+    assert(s.front == 'h');
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    assert(entab("        x \n") == "\tx\n");
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    import std.array : array;
+    assert(entabber("        x \n").array == "\tx\n");
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    dchar[dchar] transTable1 = ['e' : '5', 'o' : '7', '5': 'q'];
+    assert(translate("hello world", transTable1) == "h5ll7 w7rld");
+
+    assert(translate("hello world", transTable1, "low") == "h5 rd");
+
+    string[dchar] transTable2 = ['e' : "5", 'o' : "orange"];
+    assert(translate("hello world", transTable2) == "h5llorange worangerld");
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    import std.array : appender;
+    dchar[dchar] transTable1 = ['e' : '5', 'o' : '7', '5': 'q'];
+    auto buffer = appender!(dchar[])();
+    translate("hello world", transTable1, null, buffer);
+    assert(buffer.data == "h5ll7 w7rld");
+
+    buffer.clear();
+    translate("hello world", transTable1, "low", buffer);
+    assert(buffer.data == "h5 rd");
+
+    buffer.clear();
+    string[dchar] transTable2 = ['e' : "5", 'o' : "orange"];
+    translate("hello world", transTable2, null, buffer);
+    assert(buffer.data == "h5llorange worangerld");
+}
+
+@safe pure nothrow unittest
+{
+    import std.string;
+
+    auto transTable1 = makeTrans("eo5", "57q");
+    assert(translate("hello world", transTable1) == "h5ll7 w7rld");
+
+    assert(translate("hello world", transTable1, "low") == "h5 rd");
+}
+
+@safe pure nothrow unittest
+{
+    import std.string;
+
+    auto transTable1 = makeTrans("eo5", "57q");
+    assert(translate("hello world", transTable1) == "h5ll7 w7rld");
+
+    assert(translate("hello world", transTable1, "low") == "h5 rd");
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    assert(translate("hello world", makeTransTable("hl", "q5")) == "qe55o wor5d");
+    assert(translate("hello world", makeTransTable("12345", "67890")) == "hello world");
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    import std.array : appender;
+    auto buffer = appender!(char[])();
+    auto transTable1 = makeTransTable("eo5", "57q");
+    translate("hello world", transTable1, null, buffer);
+    assert(buffer.data == "h5ll7 w7rld");
+
+    buffer.clear();
+    translate("hello world", transTable1, "low", buffer);
+    assert(buffer.data == "h5 rd");
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    assert(succ("1") == "2");
+    assert(succ("9") == "10");
+    assert(succ("999") == "1000");
+    assert(succ("zz99") == "aaa00");
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    assert(tr("abcdef", "cd", "CD") == "abCDef");
+    assert(tr("1st March, 2018", "March", "MAR", "s") == "1st MAR, 2018");
+    assert(tr("abcdef", "ef", "", "d") == "abcd");
+    assert(tr("14-Jul-87", "a-zA-Z", " ", "cs") == " Jul ");
+}
+
+@safe @nogc pure nothrow unittest
+{
+    import std.string;
+
+    assert(isNumeric("123"));
+    assert(isNumeric("123UL"));
+    assert(isNumeric("123L"));
+    assert(isNumeric("+123U"));
+    assert(isNumeric("-123L"));
+}
+
+@safe @nogc pure nothrow unittest
+{
+    import std.string;
+
+    assert(isNumeric("+123"));
+    assert(isNumeric("-123.01"));
+    assert(isNumeric("123.3e-10f"));
+    assert(isNumeric("123.3e-10fi"));
+    assert(isNumeric("123.3e-10L"));
+
+    assert(isNumeric("nan"));
+    assert(isNumeric("nani"));
+    assert(isNumeric("-inf"));
+}
+
+@safe @nogc pure nothrow unittest
+{
+    import std.string;
+
+    assert(isNumeric("-123e-1+456.9e-10Li"));
+    assert(isNumeric("+123e+10+456i"));
+    assert(isNumeric("123+456"));
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    enum a = isNumeric("123.00E-5+1234.45E-12Li");
+    enum b = isNumeric("12345xxxx890");
+
+    static assert( a);
+    static assert(!b);
+}
+
+@safe unittest
+{
+    import std.string;
+
+    assert(soundexer("Gauss") == "G200");
+    assert(soundexer("Ghosh") == "G200");
+
+    assert(soundexer("Robert") == "R163");
+    assert(soundexer("Rupert") == "R163");
+
+    assert(soundexer("0123^&^^**&^") == ['\0', '\0', '\0', '\0']);
+}
+
+@safe unittest
+{
+    import std.string;
+
+    assert(soundex("Gauss") == "G200");
+    assert(soundex("Ghosh") == "G200");
+
+    assert(soundex("Robert") == "R163");
+    assert(soundex("Rupert") == "R163");
+
+    assert(soundex("0123^&^^**&^") == null);
+}
+
+@safe unittest
+{
+    import std.string;
+
+    import std.string;
+
+    static string[] list = [ "food", "foxy" ];
+    auto abbrevs = abbrev(list);
+    assert(abbrevs == ["fox": "foxy", "food": "food",
+                       "foxy": "foxy", "foo": "food"]);
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    import std.utf : byChar, byWchar, byDchar;
+
+    assert(column("1234 ") == 5);
+    assert(column("1234 "w) == 5);
+    assert(column("1234 "d) == 5);
+
+    assert(column("1234 ".byChar()) == 5);
+    assert(column("1234 "w.byWchar()) == 5);
+    assert(column("1234 "d.byDchar()) == 5);
+
+    // Tab stops are set at 8 spaces by default; tab characters insert enough
+    // spaces to bring the column position to the next multiple of 8.
+    assert(column("\t") == 8);
+    assert(column("1\t") == 8);
+    assert(column("\t1") == 9);
+    assert(column("123\t") == 8);
+
+    // Other tab widths are possible by specifying it explicitly:
+    assert(column("\t", 4) == 4);
+    assert(column("1\t", 4) == 4);
+    assert(column("\t1", 4) == 5);
+    assert(column("123\t", 4) == 4);
+
+    // New lines reset the column number.
+    assert(column("abc\n") == 0);
+    assert(column("abc\n1") == 1);
+    assert(column("abcdefg\r1234") == 4);
+    assert(column("abc\u20281") == 1);
+    assert(column("abc\u20291") == 1);
+    assert(column("abc\u00851") == 1);
+    assert(column("abc\u00861") == 5);
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    assert(wrap("a short string", 7) == "a short\nstring\n");
+
+    // wrap will not break inside of a word, but at the next space
+    assert(wrap("a short string", 4) == "a\nshort\nstring\n");
+
+    assert(wrap("a short string", 7, "\t") == "\ta\nshort\nstring\n");
+    assert(wrap("a short string", 7, "\t", "    ") == "\ta\n    short\n    string\n");
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    enum pretty = q{
+       import std.stdio;
+       void main() {
+           writeln("Hello");
+       }
+    }.outdent();
+
+    enum ugly = q{
+import std.stdio;
+void main() {
+    writeln("Hello");
+}
+};
+
+    assert(pretty == ugly);
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    auto str1 = [
+        "    void main()\n",
+        "    {\n",
+        "        test();\n",
+        "    }\n"
+    ];
+    auto str1Expected = [
+        "void main()\n",
+        "{\n",
+        "    test();\n",
+        "}\n"
+    ];
+    assert(str1.outdent == str1Expected);
+
+    auto str2 = [
+        "void main()\n",
+        "    {\n",
+        "            test();\n",
+        "    }\n"
+    ];
+    assert(str2.outdent == str2);
+}
+
+@safe pure unittest
+{
+    import std.string;
+
+    string a = "Hölo World";
+    immutable(ubyte)[] b = a.representation;
+    string c = b.assumeUTF;
+
+    assert(c == "Hölo World");
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_sumtype.d b/libphobos/testsuite/libphobos.phobos/std_sumtype.d
new file mode 100644
index 0000000000000000000000000000000000000000..2a7eaf35f125d8df124c8b25e74a336842f1b428
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_sumtype.d
@@ -0,0 +1,301 @@
+@safe unittest
+{
+    import std.sumtype;
+
+    import std.math.operations : isClose;
+
+    struct Fahrenheit { double degrees; }
+    struct Celsius { double degrees; }
+    struct Kelvin { double degrees; }
+
+    alias Temperature = SumType!(Fahrenheit, Celsius, Kelvin);
+
+    // Construct from any of the member types.
+    Temperature t1 = Fahrenheit(98.6);
+    Temperature t2 = Celsius(100);
+    Temperature t3 = Kelvin(273);
+
+    // Use pattern matching to access the value.
+    Fahrenheit toFahrenheit(Temperature t)
+    {
+        return Fahrenheit(
+            t.match!(
+                (Fahrenheit f) => f.degrees,
+                (Celsius c) => c.degrees * 9.0/5 + 32,
+                (Kelvin k) => k.degrees * 9.0/5 - 459.4
+            )
+        );
+    }
+
+    assert(toFahrenheit(t1).degrees.isClose(98.6));
+    assert(toFahrenheit(t2).degrees.isClose(212));
+    assert(toFahrenheit(t3).degrees.isClose(32));
+
+    // Use ref to modify the value in place.
+    void freeze(ref Temperature t)
+    {
+        t.match!(
+            (ref Fahrenheit f) => f.degrees = 32,
+            (ref Celsius c) => c.degrees = 0,
+            (ref Kelvin k) => k.degrees = 273
+        );
+    }
+
+    freeze(t1);
+    assert(toFahrenheit(t1).degrees.isClose(32));
+
+    // Use a catch-all handler to give a default result.
+    bool isFahrenheit(Temperature t)
+    {
+        return t.match!(
+            (Fahrenheit f) => true,
+            _ => false
+        );
+    }
+
+    assert(isFahrenheit(t1));
+    assert(!isFahrenheit(t2));
+    assert(!isFahrenheit(t3));
+}
+
+@safe unittest
+{
+    import std.sumtype;
+
+    alias ExampleSumType = SumType!(int, string, double);
+
+    ExampleSumType a = 123;
+    ExampleSumType b = "hello";
+    ExampleSumType c = 3.14;
+
+    assert(a.handle == "got an int");
+    assert(b.handle == "got a string");
+    assert(c.handle == "got a double");
+}
+
+@system unittest
+{
+    import std.sumtype;
+
+    import std.functional : partial;
+    import std.traits : EnumMembers;
+    import std.typecons : Tuple;
+
+    enum Op : string
+    {
+        Plus  = "+",
+        Minus = "-",
+        Times = "*",
+        Div   = "/"
+    }
+
+    // An expression is either
+    //  - a number,
+    //  - a variable, or
+    //  - a binary operation combining two sub-expressions.
+    alias Expr = SumType!(
+        double,
+        string,
+        Tuple!(Op, "op", This*, "lhs", This*, "rhs")
+    );
+
+    // Shorthand for Tuple!(Op, "op", Expr*, "lhs", Expr*, "rhs"),
+    // the Tuple type above with Expr substituted for This.
+    alias BinOp = Expr.Types[2];
+
+    // Factory function for number expressions
+    Expr* num(double value)
+    {
+        return new Expr(value);
+    }
+
+    // Factory function for variable expressions
+    Expr* var(string name)
+    {
+        return new Expr(name);
+    }
+
+    // Factory function for binary operation expressions
+    Expr* binOp(Op op, Expr* lhs, Expr* rhs)
+    {
+        return new Expr(BinOp(op, lhs, rhs));
+    }
+
+    // Convenience wrappers for creating BinOp expressions
+    alias sum  = partial!(binOp, Op.Plus);
+    alias diff = partial!(binOp, Op.Minus);
+    alias prod = partial!(binOp, Op.Times);
+    alias quot = partial!(binOp, Op.Div);
+
+    // Evaluate expr, looking up variables in env
+    double eval(Expr expr, double[string] env)
+    {
+        return expr.match!(
+            (double num) => num,
+            (string var) => env[var],
+            (BinOp bop)
+            {
+                double lhs = eval(*bop.lhs, env);
+                double rhs = eval(*bop.rhs, env);
+                final switch (bop.op)
+                {
+                    static foreach (op; EnumMembers!Op)
+                    {
+                        case op:
+                            return mixin("lhs" ~ op ~ "rhs");
+                    }
+                }
+            }
+        );
+    }
+
+    // Return a "pretty-printed" representation of expr
+    string pprint(Expr expr)
+    {
+        import std.format : format;
+
+        return expr.match!(
+            (double num) => "%g".format(num),
+            (string var) => var,
+            (BinOp bop) => "(%s %s %s)".format(
+                pprint(*bop.lhs),
+                cast(string) bop.op,
+                pprint(*bop.rhs)
+            )
+        );
+    }
+
+    Expr* myExpr = sum(var("a"), prod(num(2), var("b")));
+    double[string] myEnv = ["a":3, "b":4, "c":7];
+
+    assert(eval(*myExpr, myEnv) == 11);
+    assert(pprint(*myExpr) == "(a + (2 * b))");
+}
+
+@safe unittest
+{
+    import std.sumtype;
+
+    static struct ConvertsToSumType
+    {
+        SumType!int payload;
+        alias payload this;
+    }
+
+    static struct ContainsSumType
+    {
+        SumType!int payload;
+    }
+
+    assert(isSumType!(SumType!int));
+    assert(isSumType!ConvertsToSumType);
+    assert(!isSumType!ContainsSumType);
+}
+
+@safe unittest
+{
+    import std.sumtype;
+
+    alias Number = SumType!(double, int);
+
+    Number x;
+
+    // Problem: because int implicitly converts to double, the double
+    // handler is used for both types, and the int handler never matches.
+    assert(!__traits(compiles,
+        x.match!(
+            (double d) => "got double",
+            (int n) => "got int"
+        )
+    ));
+
+    // Solution 1: put the handler for the "more specialized" type (in this
+    // case, int) before the handler for the type it converts to.
+    assert(__traits(compiles,
+        x.match!(
+            (int n) => "got int",
+            (double d) => "got double"
+        )
+    ));
+
+    // Solution 2: use a template that only accepts the exact type it's
+    // supposed to match, instead of any type that implicitly converts to it.
+    alias exactly(T, alias fun) = function (arg)
+    {
+        static assert(is(typeof(arg) == T));
+        return fun(arg);
+    };
+
+    // Now, even if we put the double handler first, it will only be used for
+    // doubles, not ints.
+    assert(__traits(compiles,
+        x.match!(
+            exactly!(double, d => "got double"),
+            exactly!(int, n => "got int")
+        )
+    ));
+}
+
+@safe unittest
+{
+    import std.sumtype;
+
+    struct Point2D { double x, y; }
+    struct Point3D { double x, y, z; }
+
+    alias Point = SumType!(Point2D, Point3D);
+
+    version (none)
+    {
+        // This function works, but the code is ugly and repetitive.
+        // It uses three separate calls to match!
+        @safe pure nothrow @nogc
+        bool sameDimensions(Point p1, Point p2)
+        {
+            return p1.match!(
+                (Point2D _) => p2.match!(
+                    (Point2D _) => true,
+                    _ => false
+                ),
+                (Point3D _) => p2.match!(
+                    (Point3D _) => true,
+                    _ => false
+                )
+            );
+        }
+    }
+
+    // This version is much nicer.
+    @safe pure nothrow @nogc
+    bool sameDimensions(Point p1, Point p2)
+    {
+        alias doMatch = match!(
+            (Point2D _1, Point2D _2) => true,
+            (Point3D _1, Point3D _2) => true,
+            (_1, _2) => false
+        );
+
+        return doMatch(p1, p2);
+    }
+
+    Point a = Point2D(1, 2);
+    Point b = Point2D(3, 4);
+    Point c = Point3D(5, 6, 7);
+    Point d = Point3D(8, 9, 0);
+
+    assert( sameDimensions(a, b));
+    assert( sameDimensions(c, d));
+    assert(!sameDimensions(a, c));
+    assert(!sameDimensions(d, b));
+}
+
+@safe unittest
+{
+    import std.sumtype;
+
+    alias handleInt = (int i) => "got an int";
+
+    assert( canMatch!(handleInt, int));
+    assert(!canMatch!(handleInt, string));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_traits.d b/libphobos/testsuite/libphobos.phobos/std_traits.d
new file mode 100644
index 0000000000000000000000000000000000000000..a86892c26ab8cf4c0b8b6f6ec9b9b44ed7759d1f
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_traits.d
@@ -0,0 +1,2419 @@
+@safe unittest
+{
+    import std.traits;
+
+    static assert(is(InoutOf!(int) == inout int));
+    static assert(is(InoutOf!(inout int) == inout int));
+    static assert(is(InoutOf!(const int) == inout const int));
+    static assert(is(InoutOf!(shared int) == inout shared int));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(is(ConstOf!(int) == const int));
+    static assert(is(ConstOf!(const int) == const int));
+    static assert(is(ConstOf!(inout int) == const inout int));
+    static assert(is(ConstOf!(shared int) == const shared int));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(is(SharedOf!(int) == shared int));
+    static assert(is(SharedOf!(shared int) == shared int));
+    static assert(is(SharedOf!(inout int) == shared inout int));
+    static assert(is(SharedOf!(immutable int) == shared immutable int));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(is(SharedInoutOf!(int) == shared inout int));
+    static assert(is(SharedInoutOf!(int) == inout shared int));
+
+    static assert(is(SharedInoutOf!(const int) == shared inout const int));
+    static assert(is(SharedInoutOf!(immutable int) == shared inout immutable int));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(is(SharedConstOf!(int) == shared const int));
+    static assert(is(SharedConstOf!(int) == const shared int));
+
+    static assert(is(SharedConstOf!(inout int) == shared inout const int));
+    // immutable variables are implicitly shared and const
+    static assert(is(SharedConstOf!(immutable int) == immutable int));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(is(SharedConstInoutOf!(int) == shared const inout int));
+    static assert(is(SharedConstInoutOf!(int) == const shared inout int));
+    static assert(is(SharedConstInoutOf!(inout int) == shared inout const int));
+    // immutable variables are implicitly shared and const
+    static assert(is(SharedConstInoutOf!(immutable int) == immutable int));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(is(ImmutableOf!(int) == immutable int));
+    static assert(is(ImmutableOf!(const int) == immutable int));
+    static assert(is(ImmutableOf!(inout int) == immutable int));
+    static assert(is(ImmutableOf!(shared int) == immutable int));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(__traits(isSame, QualifierOf!(shared const inout int), SharedConstInoutOf));
+    static assert(__traits(isSame, QualifierOf!(immutable int), ImmutableOf));
+    static assert(__traits(isSame, QualifierOf!(shared int), SharedOf));
+    static assert(__traits(isSame, QualifierOf!(shared inout int), SharedInoutOf));
+    import std.meta : Alias;
+    static assert(__traits(isSame, QualifierOf!(int), Alias));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(packageName!packageName == "std");
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(packageName!moduleName == "std");
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(moduleName!moduleName == "std.traits");
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(fullyQualifiedName!fullyQualifiedName == "std.traits.fullyQualifiedName");
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    int foo();
+    ReturnType!foo x;   // x is declared as int
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    int foo(int, long);
+    void bar(Parameters!foo);      // declares void bar(int, long);
+    void abc(Parameters!foo[1]);   // declares void abc(long);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    void foo(){}
+    static assert(arity!foo == 0);
+    void bar(uint){}
+    static assert(arity!bar == 1);
+    void variadicFoo(uint...){}
+    static assert(!__traits(compiles, arity!variadicFoo));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    alias STC = ParameterStorageClass; // shorten the enum name
+
+    void func(ref int ctx, out real result, in real param, void* ptr)
+    {
+    }
+    alias pstc = ParameterStorageClassTuple!func;
+    static assert(pstc.length == 4); // number of parameters
+    static assert(pstc[0] == STC.ref_);
+    static assert(pstc[1] == STC.out_);
+    version (none)
+    {
+        // TODO: When the DMD PR (dlang/dmd#11474) gets merged,
+        // remove the versioning and the second test
+        static assert(pstc[2] == STC.in_);
+        // This is the current behavior, before `in` is fixed to not be an alias
+        static assert(pstc[2] == STC.scope_);
+    }
+    static assert(pstc[3] == STC.none);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static void func(ref int ctx, out real result);
+
+    enum param1 = extractParameterStorageClassFlags!(
+        __traits(getParameterStorageClasses, func, 0)
+    );
+    static assert(param1 == ParameterStorageClass.ref_);
+
+    enum param2 = extractParameterStorageClassFlags!(
+        __traits(getParameterStorageClasses, func, 1)
+    );
+    static assert(param2 == ParameterStorageClass.out_);
+
+    enum param3 = extractParameterStorageClassFlags!(
+        __traits(getParameterStorageClasses, func, 0),
+        __traits(getParameterStorageClasses, func, 1)
+    );
+    static assert(param3 == (ParameterStorageClass.ref_ | ParameterStorageClass.out_));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    int foo(int num, string name, int);
+    static assert([ParameterIdentifierTuple!foo] == ["num", "name", ""]);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    int foo(int num, string name = "hello", int[] = [1,2,3], lazy int x = 0);
+    static assert(is(ParameterDefaults!foo[0] == void));
+    static assert(   ParameterDefaults!foo[1] == "hello");
+    static assert(   ParameterDefaults!foo[2] == [1,2,3]);
+    static assert(   ParameterDefaults!foo[3] == 0);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    alias FA = FunctionAttribute; // shorten the enum name
+
+    real func(real x) pure nothrow @safe
+    {
+        return x;
+    }
+    static assert(functionAttributes!func & FA.pure_);
+    static assert(functionAttributes!func & FA.safe);
+    static assert(!(functionAttributes!func & FA.trusted)); // not @trusted
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    real func(real x) pure nothrow @safe;
+    static assert(hasFunctionAttributes!(func, "@safe", "pure"));
+    static assert(!hasFunctionAttributes!(func, "@trusted"));
+
+    // for templates attributes are automatically inferred
+    bool myFunc(T)(T b)
+    {
+        return !b;
+    }
+    static assert(hasFunctionAttributes!(myFunc!bool, "@safe", "pure", "@nogc", "nothrow"));
+    static assert(!hasFunctionAttributes!(myFunc!bool, "shared"));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    @safe    int add(int a, int b) {return a+b;}
+    @trusted int sub(int a, int b) {return a-b;}
+    @system  int mul(int a, int b) {return a*b;}
+
+    static assert( isSafe!add);
+    static assert( isSafe!sub);
+    static assert(!isSafe!mul);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    @safe    int add(int a, int b) {return a+b;}
+    @trusted int sub(int a, int b) {return a-b;}
+    @system  int mul(int a, int b) {return a*b;}
+
+    static assert(!isUnsafe!add);
+    static assert(!isUnsafe!sub);
+    static assert( isUnsafe!mul);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    extern(D) void Dfunc() {}
+    extern(C) void Cfunc() {}
+    static assert(functionLinkage!Dfunc == "D");
+    static assert(functionLinkage!Cfunc == "C");
+
+    string a = functionLinkage!Dfunc;
+    assert(a == "D");
+
+    auto fp = &Cfunc;
+    string b = functionLinkage!fp;
+    assert(b == "C");
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    void func() {}
+    static assert(variadicFunctionStyle!func == Variadic.no);
+
+    extern(C) int printf(const char*, ...);
+    static assert(variadicFunctionStyle!printf == Variadic.c);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    class C
+    {
+        int value() @property => 0;
+        static string opCall() => "hi";
+    }
+    static assert(is( typeof(C.value) == int ));
+    static assert(is( FunctionTypeOf!(C.value) == function ));
+    static assert(is( FunctionTypeOf!C == typeof(C.opCall) ));
+
+    int function() fp;
+    alias IntFn = int();
+    static assert(is( typeof(fp) == IntFn* ));
+    static assert(is( FunctionTypeOf!fp == IntFn ));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    alias ExternC(T) = SetFunctionAttributes!(T, "C", functionAttributes!T);
+
+    auto assumePure(T)(T t)
+    if (isFunctionPointer!T || isDelegate!T)
+    {
+        enum attrs = functionAttributes!T | FunctionAttribute.pure_;
+        return cast(SetFunctionAttributes!(T, functionLinkage!T, attrs)) t;
+    }
+
+    int f()
+    {
+        import core.thread : getpid;
+        return getpid();
+    }
+
+    int g() pure @trusted
+    {
+        auto pureF = assumePure(&f);
+        return pureF();
+    }
+    assert(g() > 0);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    class C
+    {
+        int outer;
+    }
+    static assert(!isInnerClass!C);
+
+    class Outer1
+    {
+        class Inner1 { }
+        class Inner2
+        {
+            int outer;
+        }
+    }
+    static assert(isInnerClass!(Outer1.Inner1));
+    static assert(!isInnerClass!(Outer1.Inner2));
+
+    static class Outer2
+    {
+        static class Inner
+        {
+            int outer;
+        }
+    }
+    static assert(!isInnerClass!(Outer2.Inner));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static struct S { }
+    static assert(!isNested!S);
+
+    int i;
+    struct NestedStruct { void f() { ++i; } }
+    static assert(isNested!NestedStruct);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static struct S { }
+
+    int i;
+    struct NS { void f() { ++i; } }
+
+    static assert(!hasNested!(S[2]));
+    static assert(hasNested!(NS[2]));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    import std.meta : AliasSeq;
+    struct S { int x; float y; }
+    static assert(is(Fields!S == AliasSeq!(int, float)));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    import std.meta : AliasSeq;
+    struct S { int x; float y; }
+    static assert(FieldNameTuple!S == AliasSeq!("x", "y"));
+    static assert(FieldNameTuple!int == AliasSeq!"");
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    struct S1 { int a; float b; }
+    struct S2 { char[] a; union { S1 b; S1 * c; } }
+    alias R = RepresentationTypeTuple!S2;
+    assert(R.length == 4
+        && is(R[0] == char[]) && is(R[1] == int)
+        && is(R[2] == float) && is(R[3] == S1*));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    struct S1 { int a; Object b; }
+    struct S2 { string a; }
+    struct S3 { int a; immutable Object b; }
+    struct S4 { float[3] vals; }
+    static assert( hasAliasing!S1);
+    static assert(!hasAliasing!S2);
+    static assert(!hasAliasing!S3);
+    static assert(!hasAliasing!S4);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert( hasIndirections!(int[string]));
+    static assert( hasIndirections!(void delegate()));
+    static assert( hasIndirections!(void delegate() immutable));
+    static assert( hasIndirections!(immutable(void delegate())));
+    static assert( hasIndirections!(immutable(void delegate() immutable)));
+
+    static assert(!hasIndirections!(void function()));
+    static assert( hasIndirections!(void*[1]));
+    static assert(!hasIndirections!(byte[1]));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    struct S1 { int a; Object b; }
+    struct S2 { string a; }
+    struct S3 { int a; immutable Object b; }
+    static assert( hasUnsharedAliasing!S1);
+    static assert(!hasUnsharedAliasing!S2);
+    static assert(!hasUnsharedAliasing!S3);
+
+    struct S4 { int a; shared Object b; }
+    struct S5 { char[] a; }
+    struct S6 { shared char[] b; }
+    struct S7 { float[3] vals; }
+    static assert(!hasUnsharedAliasing!S4);
+    static assert( hasUnsharedAliasing!S5);
+    static assert(!hasUnsharedAliasing!S6);
+    static assert(!hasUnsharedAliasing!S7);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(!hasElaborateCopyConstructor!int);
+
+    static struct S1 { }
+    static struct S2 { this(this) {} }
+    static struct S3 { S2 field; }
+    static struct S4 { S3[1] field; }
+    static struct S5 { S3[] field; }
+    static struct S6 { S3[0] field; }
+    static struct S7 { @disable this(); S3 field; }
+    static assert(!hasElaborateCopyConstructor!S1);
+    static assert( hasElaborateCopyConstructor!S2);
+    static assert( hasElaborateCopyConstructor!(immutable S2));
+    static assert( hasElaborateCopyConstructor!S3);
+    static assert( hasElaborateCopyConstructor!(S3[1]));
+    static assert(!hasElaborateCopyConstructor!(S3[0]));
+    static assert( hasElaborateCopyConstructor!S4);
+    static assert(!hasElaborateCopyConstructor!S5);
+    static assert(!hasElaborateCopyConstructor!S6);
+    static assert( hasElaborateCopyConstructor!S7);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(!hasElaborateAssign!int);
+
+    static struct S  { void opAssign(S) {} }
+    static assert( hasElaborateAssign!S);
+    static assert(!hasElaborateAssign!(const(S)));
+
+    static struct S1 { void opAssign(ref S1) {} }
+    static struct S2 { void opAssign(int) {} }
+    static struct S3 { S s; }
+    static assert( hasElaborateAssign!S1);
+    static assert(!hasElaborateAssign!S2);
+    static assert( hasElaborateAssign!S3);
+    static assert( hasElaborateAssign!(S3[1]));
+    static assert(!hasElaborateAssign!(S3[0]));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(!hasElaborateDestructor!int);
+
+    static struct S1 { }
+    static struct S2 { ~this() {} }
+    static struct S3 { S2 field; }
+    static struct S4 { S3[1] field; }
+    static struct S5 { S3[] field; }
+    static struct S6 { S3[0] field; }
+    static struct S7 { @disable this(); S3 field; }
+    static assert(!hasElaborateDestructor!S1);
+    static assert( hasElaborateDestructor!S2);
+    static assert( hasElaborateDestructor!(immutable S2));
+    static assert( hasElaborateDestructor!S3);
+    static assert( hasElaborateDestructor!(S3[1]));
+    static assert(!hasElaborateDestructor!(S3[0]));
+    static assert( hasElaborateDestructor!S4);
+    static assert(!hasElaborateDestructor!S5);
+    static assert(!hasElaborateDestructor!S6);
+    static assert( hasElaborateDestructor!S7);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(!hasElaborateMove!int);
+
+    static struct S1 { }
+    static struct S2 { void opPostMove(ref S2) {} }
+    static struct S3 { void opPostMove(inout ref S3) inout {} }
+    static struct S4 { void opPostMove(const ref S4) {} }
+    static struct S5 { void opPostMove(S5) {} }
+    static struct S6 { void opPostMove(int) {} }
+    static struct S7 { S3[1] field; }
+    static struct S8 { S3[] field; }
+    static struct S9 { S3[0] field; }
+    static struct S10 { @disable this(); S3 field; }
+    static assert(!hasElaborateMove!S1);
+    static assert( hasElaborateMove!S2);
+    static assert( hasElaborateMove!S3);
+    static assert( hasElaborateMove!(immutable S3));
+    static assert( hasElaborateMove!S4);
+    static assert(!hasElaborateMove!S5);
+    static assert(!hasElaborateMove!S6);
+    static assert( hasElaborateMove!S7);
+    static assert(!hasElaborateMove!S8);
+    static assert(!hasElaborateMove!S9);
+    static assert( hasElaborateMove!S10);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(!hasMember!(int, "blah"));
+    struct S1 { int blah; }
+    struct S2 { int blah(){ return 0; } }
+    class C1 { int blah; }
+    class C2 { int blah(){ return 0; } }
+    static assert(hasMember!(S1, "blah"));
+    static assert(hasMember!(S2, "blah"));
+    static assert(hasMember!(C1, "blah"));
+    static assert(hasMember!(C2, "blah"));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static struct S
+    {
+        static void sf() {}
+        void f() {}
+
+        static int si;
+        int i;
+    }
+
+    static assert( hasStaticMember!(S, "sf"));
+    static assert(!hasStaticMember!(S, "f"));
+
+    static assert( hasStaticMember!(S, "si"));
+    static assert(!hasStaticMember!(S, "i"));
+
+    static assert(!hasStaticMember!(S, "hello"));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    enum Sqrts : real
+    {
+        one = 1,
+        two = 1.41421,
+        three = 1.73205
+    }
+    auto sqrts = [EnumMembers!Sqrts];
+    assert(sqrts == [Sqrts.one, Sqrts.two, Sqrts.three]);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    // Returns i if e is the i-th enumerator of E.
+    static size_t rank(E)(E e)
+    if (is(E == enum))
+    {
+        static foreach (i, member; EnumMembers!E)
+        {
+            if (e == member)
+                return i;
+        }
+        assert(0, "Not an enum member");
+    }
+
+    enum Mode
+    {
+        read = 1,
+        write = 2,
+        map = 4
+    }
+    assert(rank(Mode.read) == 0);
+    assert(rank(Mode.write) == 1);
+    assert(rank(Mode.map) == 2);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    import std.conv : to;
+    class FooClass
+    {
+        string calledMethod;
+        void foo() @safe { calledMethod = "foo"; }
+        void bar() @safe { calledMethod = "bar"; }
+        void baz() @safe { calledMethod = "baz"; }
+    }
+
+    enum FooEnum { foo, bar, baz }
+
+    auto var = FooEnum.bar;
+    auto fooObj = new FooClass();
+    s: final switch (var)
+    {
+        static foreach (member; EnumMembers!FooEnum)
+        {
+            case member: // Generate a case for each enum value.
+                // Call fooObj.{name of enum value}().
+                __traits(getMember, fooObj, to!string(member))();
+                break s;
+        }
+    }
+    // As we pass in FooEnum.bar, the bar() method gets called.
+    assert(fooObj.calledMethod == "bar");
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    import std.meta : AliasSeq;
+
+    interface I1 { }
+    interface I2 { }
+    interface I12 : I1, I2 { }
+    static assert(is(BaseTypeTuple!I12 == AliasSeq!(I1, I2)));
+
+    interface I3 : I1 { }
+    interface I123 : I1, I2, I3 { }
+    static assert(is(BaseTypeTuple!I123 == AliasSeq!(I1, I2, I3)));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    import std.meta : AliasSeq;
+
+    class C1 { }
+    class C2 : C1 { }
+    class C3 : C2 { }
+    static assert(!BaseClassesTuple!Object.length);
+    static assert(is(BaseClassesTuple!C1 == AliasSeq!(Object)));
+    static assert(is(BaseClassesTuple!C2 == AliasSeq!(C1, Object)));
+    static assert(is(BaseClassesTuple!C3 == AliasSeq!(C2, C1, Object)));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    interface I1 {}
+    interface I2 {}
+    class A : I1, I2 {}
+    class B : A, I1 {}
+    class C : B {}
+
+    alias TL = InterfacesTuple!C;
+    static assert(is(TL[0] == I1) && is(TL[1] == I2));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    interface J1 {}
+    interface J2 {}
+    class B1 {}
+    class B2 : B1, J1, J2 {}
+    class B3 : B2, J1 {}
+    alias TL = TransitiveBaseTypeTuple!B3;
+    assert(TL.length == 5);
+    assert(is (TL[0] == B2));
+    assert(is (TL[1] == B1));
+    assert(is (TL[2] == Object));
+    assert(is (TL[3] == J1));
+    assert(is (TL[4] == J2));
+
+    assert(TransitiveBaseTypeTuple!Object.length == 0);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    interface I { I foo(); }
+    class B
+    {
+        real foo(real v) { return v; }
+    }
+    class C : B, I
+    {
+        override C foo() { return this; } // covariant overriding of I.foo()
+    }
+    alias foos = MemberFunctionsTuple!(C, "foo");
+    static assert(foos.length == 2);
+    static assert(__traits(isSame, foos[0], C.foo));
+    static assert(__traits(isSame, foos[1], B.foo));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    struct Foo(T, U) {}
+    static assert(__traits(isSame, TemplateOf!(Foo!(int, real)), Foo));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    import std.meta : AliasSeq;
+
+    struct Foo(T, U) {}
+    static assert(is(TemplateArgsOf!(Foo!(int, real)) == AliasSeq!(int, real)));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    class A { byte b; }
+    class B { long l; }
+
+    // As class instance always has a hidden pointer
+    static assert(classInstanceAlignment!A == (void*).alignof);
+    static assert(classInstanceAlignment!B == long.alignof);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    alias X = CommonType!(int, long, short);
+    assert(is(X == long));
+    alias Y = CommonType!(int, char[], short);
+    assert(is(Y == void));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(is(CommonType!(3) == int));
+    static assert(is(CommonType!(double, 4, float) == double));
+    static assert(is(CommonType!(string, char[]) == const(char)[]));
+    static assert(is(CommonType!(3, 3U) == uint));
+    static assert(is(CommonType!(double, int) == double));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    import std.meta : AliasSeq;
+
+    static assert(is(AllImplicitConversionTargets!(ulong) == AliasSeq!(long, float, double, real)));
+    static assert(is(AllImplicitConversionTargets!(int) == AliasSeq!(dchar, uint, long, ulong, float, double, real)));
+    static assert(is(AllImplicitConversionTargets!(float) == AliasSeq!(double, real)));
+    static assert(is(AllImplicitConversionTargets!(double) == AliasSeq!(float, real)));
+
+    static assert(is(AllImplicitConversionTargets!(char) ==
+        AliasSeq!(byte, ubyte, short, ushort, wchar, int, dchar, uint, long,
+            ulong, float, double, real)
+    ));
+    static assert(is(AllImplicitConversionTargets!(wchar) == AliasSeq!(
+        short, ushort, dchar, int, uint, long, ulong, float, double, real
+    )));
+    static assert(is(AllImplicitConversionTargets!(dchar) == AliasSeq!(
+        int, uint, long, ulong, float, double, real
+    )));
+
+    static assert(is(AllImplicitConversionTargets!(string) == AliasSeq!(const(char)[])));
+    static assert(is(AllImplicitConversionTargets!(int*) == AliasSeq!(void*)));
+
+    interface A {}
+    interface B {}
+    class C : A, B {}
+
+    static assert(is(AllImplicitConversionTargets!(C) == AliasSeq!(Object, A, B)));
+    static assert(is(AllImplicitConversionTargets!(const C) == AliasSeq!(const Object, const A, const B)));
+    static assert(is(AllImplicitConversionTargets!(immutable C) == AliasSeq!(
+        immutable Object, immutable A, immutable B
+    )));
+
+    interface I : A, B {}
+
+    static assert(is(AllImplicitConversionTargets!(I) == AliasSeq!(A, B)));
+    static assert(is(AllImplicitConversionTargets!(const I) == AliasSeq!(const A, const B)));
+    static assert(is(AllImplicitConversionTargets!(immutable I) == AliasSeq!(
+        immutable A, immutable B
+    )));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert( isImplicitlyConvertible!(immutable(char), char));
+    static assert( isImplicitlyConvertible!(const(char), char));
+    static assert( isImplicitlyConvertible!(char, wchar));
+    static assert(!isImplicitlyConvertible!(wchar, char));
+
+    static assert(!isImplicitlyConvertible!(const(ushort), ubyte));
+    static assert(!isImplicitlyConvertible!(const(uint), ubyte));
+    static assert(!isImplicitlyConvertible!(const(ulong), ubyte));
+
+    static assert(!isImplicitlyConvertible!(const(char)[], string));
+    static assert( isImplicitlyConvertible!(string, const(char)[]));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    // Mutable and immmutable both convert to const...
+    static assert( isQualifierConvertible!(char, const(char)));
+    static assert( isQualifierConvertible!(immutable(char), const(char)));
+    // ...but const does not convert back to mutable or immutable
+    static assert(!isQualifierConvertible!(const(char), char));
+    static assert(!isQualifierConvertible!(const(char), immutable(char)));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert( isAssignable!(long, int));
+    static assert(!isAssignable!(int, long));
+    static assert( isAssignable!(const(char)[], string));
+    static assert(!isAssignable!(string, char[]));
+
+    // int is assignable to int
+    static assert( isAssignable!int);
+
+    // immutable int is not assignable to immutable int
+    static assert(!isAssignable!(immutable int));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    struct S1
+    {
+        void opAssign(S1);
+    }
+
+    struct S2
+    {
+        void opAssign(ref S2);
+    }
+
+    static assert( isRvalueAssignable!(long, int));
+    static assert(!isRvalueAssignable!(int, long));
+    static assert( isRvalueAssignable!S1);
+    static assert(!isRvalueAssignable!S2);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    struct S1
+    {
+        void opAssign(S1);
+    }
+
+    struct S2
+    {
+        void opAssign(ref S2);
+    }
+
+    static assert( isLvalueAssignable!(long, int));
+    static assert(!isLvalueAssignable!(int, long));
+    static assert( isLvalueAssignable!S1);
+    static assert( isLvalueAssignable!S2);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    interface I { I clone(); }
+    interface J { J clone(); }
+    class C : I
+    {
+        override C clone()   // covariant overriding of I.clone()
+        {
+            return new C;
+        }
+    }
+
+    // C.clone() can override I.clone(), indeed.
+    static assert(isCovariantWith!(typeof(C.clone), typeof(I.clone)));
+
+    // C.clone() can't override J.clone(); the return type C is not implicitly
+    // convertible to J.
+    static assert(!isCovariantWith!(typeof(C.clone), typeof(J.clone)));
+}
+
+@system unittest
+{
+    import std.traits;
+
+    static int f(int);
+    static assert(is(typeof(f(rvalueOf!int)) == int));
+}
+
+@system unittest
+{
+    import std.traits;
+
+    static bool f(ref int);
+    static assert(is(typeof(f(lvalueOf!int)) == bool));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert( isBoolean!bool);
+    enum EB : bool { a = true }
+    static assert( isBoolean!EB);
+
+    struct SubTypeOfBool
+    {
+        bool val;
+        alias val this;
+    }
+    static assert(!isBoolean!(SubTypeOfBool));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(
+        isIntegral!byte &&
+        isIntegral!short &&
+        isIntegral!int &&
+        isIntegral!long &&
+        isIntegral!(const(long)) &&
+        isIntegral!(immutable(long))
+    );
+
+    static assert(
+        !isIntegral!bool &&
+        !isIntegral!char &&
+        !isIntegral!double
+    );
+
+    // types which act as integral values do not pass
+    struct S
+    {
+        int val;
+        alias val this;
+    }
+
+    static assert(!isIntegral!S);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(
+        isFloatingPoint!float &&
+        isFloatingPoint!double &&
+        isFloatingPoint!real &&
+        isFloatingPoint!(const(real)) &&
+        isFloatingPoint!(immutable(real))
+    );
+
+    static assert(!isFloatingPoint!int);
+
+    // types which act as floating point values do not pass
+    struct S
+    {
+        float val;
+        alias val this;
+    }
+
+    static assert(!isFloatingPoint!S);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(
+        isNumeric!byte &&
+        isNumeric!short &&
+        isNumeric!int &&
+        isNumeric!long &&
+        isNumeric!float &&
+        isNumeric!double &&
+        isNumeric!real &&
+        isNumeric!(const(real)) &&
+        isNumeric!(immutable(real))
+    );
+
+    static assert(
+        !isNumeric!void &&
+        !isNumeric!bool &&
+        !isNumeric!char &&
+        !isNumeric!wchar &&
+        !isNumeric!dchar
+    );
+
+    // types which act as numeric values do not pass
+    struct S
+    {
+        int val;
+        alias val this;
+    }
+
+    static assert(!isNumeric!S);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(!isScalarType!void);
+    static assert( isScalarType!(immutable(byte)));
+    static assert( isScalarType!(immutable(ushort)));
+    static assert( isScalarType!(immutable(int)));
+    static assert( isScalarType!(ulong));
+    static assert( isScalarType!(shared(float)));
+    static assert( isScalarType!(shared(const bool)));
+    static assert( isScalarType!(const(char)));
+    static assert( isScalarType!(wchar));
+    static assert( isScalarType!(const(dchar)));
+    static assert( isScalarType!(const(double)));
+    static assert( isScalarType!(const(real)));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(isBasicType!void);
+    static assert(isBasicType!(const(void)));
+    static assert(isBasicType!(shared(void)));
+    static assert(isBasicType!(immutable(void)));
+    static assert(isBasicType!(shared const(void)));
+    static assert(isBasicType!(shared inout(void)));
+    static assert(isBasicType!(shared inout const(void)));
+    static assert(isBasicType!(inout(void)));
+    static assert(isBasicType!(inout const(void)));
+    static assert(isBasicType!(immutable(int)));
+    static assert(isBasicType!(shared(float)));
+    static assert(isBasicType!(shared(const bool)));
+    static assert(isBasicType!(const(dchar)));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(
+        isUnsigned!uint &&
+        isUnsigned!ulong
+    );
+
+    static assert(
+        !isUnsigned!char &&
+        !isUnsigned!int &&
+        !isUnsigned!long &&
+        !isUnsigned!char &&
+        !isUnsigned!wchar &&
+        !isUnsigned!dchar
+    );
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(
+        isSigned!int &&
+        isSigned!long
+    );
+
+    static assert(
+        !isSigned!uint &&
+        !isSigned!ulong
+    );
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    //Char types
+    static assert( isSomeChar!char);
+    static assert( isSomeChar!wchar);
+    static assert( isSomeChar!dchar);
+    static assert( isSomeChar!(typeof('c')));
+    static assert( isSomeChar!(immutable char));
+    static assert( isSomeChar!(const dchar));
+
+    //Non char types
+    static assert(!isSomeChar!int);
+    static assert(!isSomeChar!byte);
+    static assert(!isSomeChar!string);
+    static assert(!isSomeChar!wstring);
+    static assert(!isSomeChar!dstring);
+    static assert(!isSomeChar!(char[4]));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    //String types
+    static assert( isSomeString!string);
+    static assert( isSomeString!(wchar[]));
+    static assert( isSomeString!(dchar[]));
+    static assert( isSomeString!(typeof("aaa")));
+    static assert( isSomeString!(const(char)[]));
+
+    //Non string types
+    static assert(!isSomeString!int);
+    static assert(!isSomeString!(int[]));
+    static assert(!isSomeString!(byte[]));
+    static assert(!isSomeString!(typeof(null)));
+    static assert(!isSomeString!(char[4]));
+
+    enum ES : string { a = "aaa", b = "bbb" }
+    static assert(!isSomeString!ES);
+
+    static struct Stringish
+    {
+        string str;
+        alias str this;
+    }
+    static assert(!isSomeString!Stringish);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(isNarrowString!string);
+    static assert(isNarrowString!wstring);
+    static assert(isNarrowString!(char[]));
+    static assert(isNarrowString!(wchar[]));
+
+    static assert(!isNarrowString!dstring);
+    static assert(!isNarrowString!(dchar[]));
+
+    static assert(!isNarrowString!(typeof(null)));
+    static assert(!isNarrowString!(char[4]));
+
+    enum ES : string { a = "aaa", b = "bbb" }
+    static assert(!isNarrowString!ES);
+
+    static struct Stringish
+    {
+        string str;
+        alias str this;
+    }
+    static assert(!isNarrowString!Stringish);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(isOrderingComparable!int);
+    static assert(isOrderingComparable!string);
+
+    static struct Foo {}
+    static assert(!isOrderingComparable!Foo);
+
+    static struct Bar
+    {
+        int a;
+        auto opCmp(Bar b1) const { return a - b1.a; }
+    }
+
+    Bar b1 = Bar(5);
+    Bar b2 = Bar(7);
+    assert(isOrderingComparable!Bar && b2 > b1);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static struct AliasedString
+    {
+        string s;
+        alias s this;
+    }
+
+    enum StringEnum { a = "foo" }
+
+    assert(!isConvertibleToString!string);
+    assert(isConvertibleToString!AliasedString);
+    assert(isConvertibleToString!StringEnum);
+    assert(isConvertibleToString!(char[25]));
+    assert(!isConvertibleToString!(char[]));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static struct Stringish
+    {
+        string s;
+        alias s this;
+    }
+    static assert(isAutodecodableString!wstring);
+    static assert(isAutodecodableString!Stringish);
+    static assert(!isAutodecodableString!dstring);
+
+    enum E : const(char)[3] { X = "abc" }
+    enum F : const(char)[] { X = "abc" }
+    enum G : F { X = F.init }
+
+    static assert(isAutodecodableString!(char[]));
+    static assert(!isAutodecodableString!(E));
+    static assert(isAutodecodableString!(F));
+    static assert(isAutodecodableString!(G));
+
+    struct Stringish2
+    {
+        Stringish s;
+        alias s this;
+    }
+
+    enum H : Stringish { X = Stringish() }
+    enum I : Stringish2 { X = Stringish2() }
+
+    static assert(isAutodecodableString!(H));
+    static assert(isAutodecodableString!(I));
+
+    static assert(!isAutodecodableString!(noreturn[]));
+    static assert(!isAutodecodableString!(immutable(noreturn)[]));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert( isStaticArray!(int[3]));
+    static assert( isStaticArray!(const(int)[5]));
+    static assert( isStaticArray!(const(int)[][5]));
+
+    static assert(!isStaticArray!(const(int)[]));
+    static assert(!isStaticArray!(immutable(int)[]));
+    static assert(!isStaticArray!(const(int)[4][]));
+    static assert(!isStaticArray!(int[]));
+    static assert(!isStaticArray!(int[char]));
+    static assert(!isStaticArray!(int[1][]));
+    static assert(!isStaticArray!(int[int]));
+    static assert(!isStaticArray!int);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert( isDynamicArray!(int[]));
+    static assert( isDynamicArray!(string));
+    static assert( isDynamicArray!(long[3][]));
+
+    static assert(!isDynamicArray!(int[5]));
+    static assert(!isDynamicArray!(typeof(null)));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert( isArray!(int[]));
+    static assert( isArray!(int[5]));
+    static assert( isArray!(string));
+
+    static assert(!isArray!uint);
+    static assert(!isArray!(uint[uint]));
+    static assert(!isArray!(typeof(null)));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    struct S;
+
+    static assert( isAssociativeArray!(int[string]));
+    static assert( isAssociativeArray!(S[S]));
+    static assert(!isAssociativeArray!(string[]));
+    static assert(!isAssociativeArray!S);
+    static assert(!isAssociativeArray!(int[4]));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    class C;
+    union U;
+    struct S;
+    interface I;
+
+    static assert( isBuiltinType!void);
+    static assert( isBuiltinType!string);
+    static assert( isBuiltinType!(int[]));
+    static assert( isBuiltinType!(C[string]));
+    static assert( isBuiltinType!(typeof(null)));
+    static assert(!isBuiltinType!C);
+    static assert(!isBuiltinType!U);
+    static assert(!isBuiltinType!S);
+    static assert(!isBuiltinType!I);
+    static assert(!isBuiltinType!(void delegate(int)));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static if (is(__vector(float[4])))
+    {
+        alias SimdVec = __vector(float[4]);
+        static assert(isSIMDVector!(__vector(float[4])));
+        static assert(isSIMDVector!SimdVec);
+    }
+    static assert(!isSIMDVector!uint);
+    static assert(!isSIMDVector!(float[4]));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    void fun();
+
+    static assert( isPointer!(int*));
+    static assert( isPointer!(int function()));
+    static assert(!isPointer!int);
+    static assert(!isPointer!string);
+    static assert(!isPointer!(typeof(null)));
+    static assert(!isPointer!(typeof(fun)));
+    static assert(!isPointer!(int delegate()));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(is(PointerTarget!(int*) == int));
+    static assert(is(PointerTarget!(void*) == void));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    class C {}
+    union U {}
+    struct S {}
+    interface I {}
+
+    static assert( isAggregateType!C);
+    static assert( isAggregateType!U);
+    static assert( isAggregateType!S);
+    static assert( isAggregateType!I);
+    static assert(!isAggregateType!void);
+    static assert(!isAggregateType!string);
+    static assert(!isAggregateType!(int[]));
+    static assert(!isAggregateType!(C[string]));
+    static assert(!isAggregateType!(void delegate(int)));
+
+    enum ES : S { a = S.init }
+    enum EC : C { a = C.init }
+    enum EI : I { a = I.init }
+    enum EU : U { a = U.init }
+
+    static assert( isAggregateType!ES);
+    static assert( isAggregateType!EC);
+    static assert( isAggregateType!EI);
+    static assert( isAggregateType!EU);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    struct OpApply
+    {
+        int opApply(scope int delegate(ref uint) dg) { assert(0); }
+    }
+
+    struct Range
+    {
+        @property uint front() { assert(0); }
+        void popFront() { assert(0); }
+        enum bool empty = false;
+    }
+
+    static assert( isIterable!(uint[]));
+    static assert( isIterable!OpApply);
+    static assert( isIterable!(uint[string]));
+    static assert( isIterable!Range);
+
+    static assert(!isIterable!uint);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert( isMutable!int);
+    static assert( isMutable!string);
+    static assert( isMutable!(shared int));
+    static assert( isMutable!(shared const(int)[]));
+
+    static assert(!isMutable!(const int));
+    static assert(!isMutable!(inout int));
+    static assert(!isMutable!(shared(const int)));
+    static assert(!isMutable!(shared(inout int)));
+    static assert(!isMutable!(immutable string));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static struct Foo(T...) { }
+    static struct Bar(T...) { }
+    static struct Doo(T) { }
+    static struct ABC(int x) { }
+    static void fun(T)() { }
+    template templ(T) { }
+
+    static assert(isInstanceOf!(Foo, Foo!int));
+    static assert(!isInstanceOf!(Foo, Bar!int));
+    static assert(!isInstanceOf!(Foo, int));
+    static assert(isInstanceOf!(Doo, Doo!int));
+    static assert(isInstanceOf!(ABC, ABC!1));
+    static assert(!isInstanceOf!(Foo, Foo));
+    static assert(isInstanceOf!(fun, fun!int));
+    static assert(isInstanceOf!(templ, templ!int));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static struct A(T = void)
+    {
+        // doesn't work as expected, only accepts A when T = void
+        void func(B)(B b)
+        if (isInstanceOf!(A, B)) {}
+
+        // correct behavior
+        void method(B)(B b)
+        if (isInstanceOf!(TemplateOf!(A), B)) {}
+    }
+
+    A!(void) a1;
+    A!(void) a2;
+    A!(int) a3;
+
+    static assert(!__traits(compiles, a1.func(a3)));
+    static assert( __traits(compiles, a1.method(a2)));
+    static assert( __traits(compiles, a1.method(a3)));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(isExpressions!(1, 2.0, "a"));
+    static assert(!isExpressions!(int, double, string));
+    static assert(!isExpressions!(int, 2.0, "a"));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(isTypeTuple!(int, float, string));
+    static assert(!isTypeTuple!(1, 2.0, "a"));
+    static assert(!isTypeTuple!(1, double, string));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static void foo() {}
+    void bar() {}
+
+    auto fpfoo = &foo;
+    static assert( isFunctionPointer!fpfoo);
+    static assert( isFunctionPointer!(void function()));
+
+    auto dgbar = &bar;
+    static assert(!isFunctionPointer!dgbar);
+    static assert(!isFunctionPointer!(void delegate()));
+    static assert(!isFunctionPointer!foo);
+    static assert(!isFunctionPointer!bar);
+
+    static assert( isFunctionPointer!((int a) {}));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static void sfunc() { }
+    int x;
+    void func() { x++; }
+
+    int delegate() dg;
+    assert(isDelegate!dg);
+    assert(isDelegate!(int delegate()));
+    assert(isDelegate!(typeof(&func)));
+
+    int function() fp;
+    assert(!isDelegate!fp);
+    assert(!isDelegate!(int function()));
+    assert(!isDelegate!(typeof(&sfunc)));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static real func(ref int) { return 0; }
+    static void prop() @property { }
+    class C
+    {
+        real method(ref int) { return 0; }
+        real prop() @property { return 0; }
+    }
+    auto c = new C;
+    auto fp = &func;
+    auto dg = &c.method;
+
+    static assert( isSomeFunction!func);
+    static assert( isSomeFunction!prop);
+    static assert( isSomeFunction!(C.method));
+    static assert( isSomeFunction!(C.prop));
+    static assert( isSomeFunction!(c.prop));
+    static assert( isSomeFunction!fp);
+    static assert( isSomeFunction!dg);
+
+    real val;
+    static assert(!isSomeFunction!int);
+    static assert(!isSomeFunction!val);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    void f() { }
+    int g(int x) { return x; }
+
+    static assert( isCallable!f);
+    static assert( isCallable!g);
+
+    class C { int opCall(int) { return 0; } }
+    auto c = new C;
+    struct S { static int opCall(int) { return 0; } }
+    interface I { real value() @property; }
+
+    static assert( isCallable!c);
+    static assert( isCallable!(c.opCall));
+    static assert( isCallable!S);
+    static assert( isCallable!(I.value));
+    static assert( isCallable!((int a) { return a; }));
+
+    static assert(!isCallable!I);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    void f()() { }
+    T g(T = int)(T x) { return x; }
+    struct S1 { static void opCall()() { } }
+    struct S2 { static T opCall(T = int)(T x) {return x; } }
+
+    static assert( isCallable!f);
+    static assert( isCallable!g);
+    static assert( isCallable!S1);
+    static assert( isCallable!S2);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static struct Wrapper
+    {
+        void f() { }
+        int f(int x) { return x; }
+
+        void g()() { }
+        T g(T = int)(T x) { return x; }
+    }
+
+    static assert(isCallable!(Wrapper.f));
+    static assert(isCallable!(Wrapper.g));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    struct S { void foo() { } }
+    class C { void foo() { } }
+    class AC { abstract void foo(); }
+    static assert(!isAbstractFunction!(int));
+    static assert(!isAbstractFunction!(S.foo));
+    static assert(!isAbstractFunction!(C.foo));
+    static assert( isAbstractFunction!(AC.foo));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    struct S { void bar() { } }
+    final class FC { void foo(); }
+    class C
+    {
+        void bar() { }
+        final void foo();
+    }
+    static assert(!isFinalFunction!(int));
+    static assert(!isFinalFunction!(S.bar));
+    static assert( isFinalFunction!(FC.foo));
+    static assert(!isFinalFunction!(C.bar));
+    static assert( isFinalFunction!(C.foo));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static void f() {}
+    static void fun()
+    {
+        int i;
+        int f() { return i; }
+
+        static assert(isNestedFunction!(f));
+    }
+
+    static assert(!isNestedFunction!f);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    struct S { }
+    class C { }
+    abstract class AC { }
+    static assert(!isAbstractClass!S);
+    static assert(!isAbstractClass!C);
+    static assert( isAbstractClass!AC);
+    C c;
+    static assert(!isAbstractClass!c);
+    AC ac;
+    static assert( isAbstractClass!ac);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    class C { }
+    abstract class AC { }
+    final class FC1 : C { }
+    final class FC2 { }
+    static assert(!isFinalClass!C);
+    static assert(!isFinalClass!AC);
+    static assert( isFinalClass!FC1);
+    static assert( isFinalClass!FC2);
+    C c;
+    static assert(!isFinalClass!c);
+    FC1 fc1;
+    static assert( isFinalClass!fc1);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(is(Unconst!int == int));
+    static assert(is(Unconst!(const int) == int));
+    static assert(is(Unconst!(immutable int) == int));
+    static assert(is(Unconst!(shared int) == shared int));
+    static assert(is(Unconst!(shared(const int)) == shared int));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(is(Unshared!int == int));
+    static assert(is(Unshared!(const int) == const int));
+    static assert(is(Unshared!(immutable int) == immutable int));
+
+    static assert(is(Unshared!(shared int) == int));
+    static assert(is(Unshared!(shared(const int)) == const int));
+
+    static assert(is(Unshared!(shared(int[])) == shared(int)[]));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(is(Unqual!int == int));
+    static assert(is(Unqual!(const int) == int));
+    static assert(is(Unqual!(immutable int) == int));
+    static assert(is(Unqual!(shared int) == int));
+    static assert(is(Unqual!(shared(const int)) == int));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(is(CopyTypeQualifiers!(inout const real, int) == inout const int));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    const(int) i;
+    CopyConstness!(typeof(i), float) f;
+    assert( is(typeof(f) == const float));
+
+    CopyConstness!(char, uint) u;
+    assert( is(typeof(u) == uint));
+
+    //The 'shared' qualifier will not be copied
+    assert(!is(CopyConstness!(shared bool, int) == shared int));
+
+    //But the constness will be
+    assert( is(CopyConstness!(shared const real, double) == const double));
+
+    //Careful, const(int)[] is a mutable array of const(int)
+    alias MutT = CopyConstness!(const(int)[], int);
+    assert(!is(MutT == const(int)));
+
+    //Okay, const(int[]) applies to array and contained ints
+    alias CstT = CopyConstness!(const(int[]), int);
+    assert( is(CstT == const(int)));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(is(ForeachType!(uint[]) == uint));
+    static assert(is(ForeachType!string == immutable(char)));
+    static assert(is(ForeachType!(string[string]) == string));
+    static assert(is(ForeachType!(inout(int)[]) == inout(int)));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    enum E : real { a = 0 } // NOTE: explicit initialization to 0 required during Enum init deprecation cycle
+    enum F : E    { a = E.a }
+    alias G = const(F);
+    static assert(is(OriginalType!E == real));
+    static assert(is(OriginalType!F == real));
+    static assert(is(OriginalType!G == const real));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    alias Hash = int[string];
+    static assert(is(KeyType!Hash == string));
+    static assert(is(ValueType!Hash == int));
+    KeyType!Hash str = "a"; // str is declared as string
+    ValueType!Hash num = 1; // num is declared as int
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    alias Hash = int[string];
+    static assert(is(KeyType!Hash == string));
+    static assert(is(ValueType!Hash == int));
+    KeyType!Hash str = "a"; // str is declared as string
+    ValueType!Hash num = 1; // num is declared as int
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(is(Unsigned!(int) == uint));
+    static assert(is(Unsigned!(long) == ulong));
+    static assert(is(Unsigned!(const short) == const ushort));
+    static assert(is(Unsigned!(immutable byte) == immutable ubyte));
+    static assert(is(Unsigned!(inout int) == inout uint));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(is(Unsigned!(uint) == uint));
+    static assert(is(Unsigned!(const uint) == const uint));
+
+    static assert(is(Unsigned!(ubyte) == ubyte));
+    static assert(is(Unsigned!(immutable uint) == immutable uint));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(is(Largest!(uint, ubyte, ushort, real) == real));
+    static assert(is(Largest!(ulong, double) == ulong));
+    static assert(is(Largest!(double, ulong) == double));
+    static assert(is(Largest!(uint, byte, double, short) == double));
+    static if (is(ucent))
+        static assert(is(Largest!(uint, ubyte, ucent, ushort) == ucent));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    alias S1 = Signed!uint;
+    static assert(is(S1 == int));
+    alias S2 = Signed!(const(uint));
+    static assert(is(S2 == const(int)));
+    alias S3 = Signed!(immutable(uint));
+    static assert(is(S3 == immutable(int)));
+    static if (is(ucent))
+    {
+        alias S4 = Signed!ucent;
+        static assert(is(S4 == cent));
+    }
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(mostNegative!float == -float.max);
+    static assert(mostNegative!double == -double.max);
+    static assert(mostNegative!real == -real.max);
+    static assert(mostNegative!bool == false);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    import std.meta : AliasSeq;
+
+    static foreach (T; AliasSeq!(bool, byte, short, int, long))
+        static assert(mostNegative!T == T.min);
+
+    static foreach (T; AliasSeq!(ubyte, ushort, uint, ulong, char, wchar, dchar))
+        static assert(mostNegative!T == 0);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    ubyte a = 3, b = 5;
+    static assert(is(typeof(a * b) == Promoted!ubyte));
+    static assert(is(Promoted!ubyte == int));
+
+    static assert(is(Promoted!(shared(bool)) == shared(int)));
+    static assert(is(Promoted!(const(int)) == const(int)));
+    static assert(is(Promoted!double == double));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    import std.meta : AliasSeq;
+    alias TL = staticMap!(mangledName, int, const int, immutable int);
+    static assert(TL == AliasSeq!("i", "xi", "yi"));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    // can select types
+    static assert(is(Select!(true, int, long) == int));
+    static assert(is(Select!(false, int, long) == long));
+    static struct Foo {}
+    static assert(is(Select!(false, const(int), const(Foo)) == const(Foo)));
+
+    // can select symbols
+    int a = 1;
+    int b = 2;
+    alias selA = Select!(true, a, b);
+    alias selB = Select!(false, a, b);
+    assert(selA == 1);
+    assert(selB == 2);
+
+    // can select (compile-time) expressions
+    enum val = Select!(false, -4, 9 - 6);
+    static assert(val == 3);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    real run() { return 0; }
+    int fail() { assert(0); }
+    auto a = select!true(run(), fail());
+    auto b = select!false(fail(), run());
+    static assert(is(typeof(a) == real));
+    static assert(is(typeof(b) == real));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    enum E;
+    struct S {}
+
+    @("alpha") int a;
+    static assert(hasUDA!(a, "alpha"));
+    static assert(!hasUDA!(a, S));
+    static assert(!hasUDA!(a, E));
+
+    @(E) int b;
+    static assert(!hasUDA!(b, "alpha"));
+    static assert(!hasUDA!(b, S));
+    static assert(hasUDA!(b, E));
+
+    @E int c;
+    static assert(!hasUDA!(c, "alpha"));
+    static assert(!hasUDA!(c, S));
+    static assert(hasUDA!(c, E));
+
+    @(S, E) int d;
+    static assert(!hasUDA!(d, "alpha"));
+    static assert(hasUDA!(d, S));
+    static assert(hasUDA!(d, E));
+
+    @S int e;
+    static assert(!hasUDA!(e, "alpha"));
+    static assert(hasUDA!(e, S));
+    static assert(!hasUDA!(e, S()));
+    static assert(!hasUDA!(e, E));
+
+    @S() int f;
+    static assert(!hasUDA!(f, "alpha"));
+    static assert(hasUDA!(f, S));
+    static assert(hasUDA!(f, S()));
+    static assert(!hasUDA!(f, E));
+
+    @(S, E, "alpha") int g;
+    static assert(hasUDA!(g, "alpha"));
+    static assert(hasUDA!(g, S));
+    static assert(hasUDA!(g, E));
+
+    @(100) int h;
+    static assert(hasUDA!(h, 100));
+
+    struct Named { string name; }
+
+    @Named("abc") int i;
+    static assert(hasUDA!(i, Named));
+    static assert(hasUDA!(i, Named("abc")));
+    static assert(!hasUDA!(i, Named("def")));
+
+    struct AttrT(T)
+    {
+        string name;
+        T value;
+    }
+
+    @AttrT!int("answer", 42) int j;
+    static assert(hasUDA!(j, AttrT));
+    static assert(hasUDA!(j, AttrT!int));
+    static assert(!hasUDA!(j, AttrT!string));
+
+    @AttrT!string("hello", "world") int k;
+    static assert(hasUDA!(k, AttrT));
+    static assert(!hasUDA!(k, AttrT!int));
+    static assert(hasUDA!(k, AttrT!string));
+
+    struct FuncAttr(alias f) { alias func = f; }
+    static int fourtyTwo() { return 42; }
+    static size_t getLen(string s) { return s.length; }
+
+    @FuncAttr!getLen int l;
+    static assert(hasUDA!(l, FuncAttr));
+    static assert(!hasUDA!(l, FuncAttr!fourtyTwo));
+    static assert(hasUDA!(l, FuncAttr!getLen));
+    static assert(!hasUDA!(l, FuncAttr!fourtyTwo()));
+    static assert(!hasUDA!(l, FuncAttr!getLen()));
+
+    @FuncAttr!getLen() int m;
+    static assert(hasUDA!(m, FuncAttr));
+    static assert(!hasUDA!(m, FuncAttr!fourtyTwo));
+    static assert(hasUDA!(m, FuncAttr!getLen));
+    static assert(!hasUDA!(m, FuncAttr!fourtyTwo()));
+    static assert(hasUDA!(m, FuncAttr!getLen()));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    struct Attr
+    {
+        string name;
+        int value;
+    }
+
+    @Attr("Answer", 42) int a;
+    static assert(getUDAs!(a, Attr).length == 1);
+    static assert(getUDAs!(a, Attr)[0].name == "Answer");
+    static assert(getUDAs!(a, Attr)[0].value == 42);
+
+    @(Attr("Answer", 42), "string", 9999) int b;
+    static assert(getUDAs!(b, Attr).length == 1);
+    static assert(getUDAs!(b, Attr)[0].name == "Answer");
+    static assert(getUDAs!(b, Attr)[0].value == 42);
+
+    @Attr("Answer", 42) @Attr("Pi", 3) int c;
+    static assert(getUDAs!(c, Attr).length == 2);
+    static assert(getUDAs!(c, Attr)[0].name == "Answer");
+    static assert(getUDAs!(c, Attr)[0].value == 42);
+    static assert(getUDAs!(c, Attr)[1].name == "Pi");
+    static assert(getUDAs!(c, Attr)[1].value == 3);
+
+    static assert(getUDAs!(c, Attr("Answer", 42)).length == 1);
+    static assert(getUDAs!(c, Attr("Answer", 42))[0].name == "Answer");
+    static assert(getUDAs!(c, Attr("Answer", 42))[0].value == 42);
+
+    static assert(getUDAs!(c, Attr("Answer", 99)).length == 0);
+
+    struct AttrT(T)
+    {
+        string name;
+        T value;
+    }
+
+    @AttrT!uint("Answer", 42) @AttrT!int("Pi", 3) @AttrT int d;
+    static assert(getUDAs!(d, AttrT).length == 2);
+    static assert(getUDAs!(d, AttrT)[0].name == "Answer");
+    static assert(getUDAs!(d, AttrT)[0].value == 42);
+    static assert(getUDAs!(d, AttrT)[1].name == "Pi");
+    static assert(getUDAs!(d, AttrT)[1].value == 3);
+
+    static assert(getUDAs!(d, AttrT!uint).length == 1);
+    static assert(getUDAs!(d, AttrT!uint)[0].name == "Answer");
+    static assert(getUDAs!(d, AttrT!uint)[0].value == 42);
+
+    static assert(getUDAs!(d, AttrT!int).length == 1);
+    static assert(getUDAs!(d, AttrT!int)[0].name == "Pi");
+    static assert(getUDAs!(d, AttrT!int)[0].value == 3);
+
+    struct SimpleAttr {}
+
+    @SimpleAttr int e;
+    static assert(getUDAs!(e, SimpleAttr).length == 1);
+    static assert(is(getUDAs!(e, SimpleAttr)[0] == SimpleAttr));
+
+    @SimpleAttr() int f;
+    static assert(getUDAs!(f, SimpleAttr).length == 1);
+    static assert(is(typeof(getUDAs!(f, SimpleAttr)[0]) == SimpleAttr));
+
+    struct FuncAttr(alias f) { alias func = f; }
+    static int add42(int v) { return v + 42; }
+    static string concat(string l, string r) { return l ~ r; }
+
+    @FuncAttr!add42 int g;
+    static assert(getUDAs!(g, FuncAttr).length == 1);
+    static assert(getUDAs!(g, FuncAttr)[0].func(5) == 47);
+
+    static assert(getUDAs!(g, FuncAttr!add42).length == 1);
+    static assert(getUDAs!(g, FuncAttr!add42)[0].func(5) == 47);
+
+    static assert(getUDAs!(g, FuncAttr!add42()).length == 0);
+
+    static assert(getUDAs!(g, FuncAttr!concat).length == 0);
+    static assert(getUDAs!(g, FuncAttr!concat()).length == 0);
+
+    @FuncAttr!add42() int h;
+    static assert(getUDAs!(h, FuncAttr).length == 1);
+    static assert(getUDAs!(h, FuncAttr)[0].func(5) == 47);
+
+    static assert(getUDAs!(h, FuncAttr!add42).length == 1);
+    static assert(getUDAs!(h, FuncAttr!add42)[0].func(5) == 47);
+
+    static assert(getUDAs!(h, FuncAttr!add42()).length == 1);
+    static assert(getUDAs!(h, FuncAttr!add42())[0].func(5) == 47);
+
+    static assert(getUDAs!(h, FuncAttr!concat).length == 0);
+    static assert(getUDAs!(h, FuncAttr!concat()).length == 0);
+
+    @("alpha") @(42) int i;
+    static assert(getUDAs!(i, "alpha").length == 1);
+    static assert(getUDAs!(i, "alpha")[0] == "alpha");
+
+    static assert(getUDAs!(i, 42).length == 1);
+    static assert(getUDAs!(i, 42)[0] == 42);
+
+    static assert(getUDAs!(i, 'c').length == 0);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    enum Attr;
+    struct A
+    {
+        @Attr int a;
+        int b;
+    }
+
+    static assert(getSymbolsByUDA!(A, Attr).length == 1);
+    static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[0], Attr));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    enum Attr;
+
+    static struct A
+    {
+        @Attr int a;
+        int b;
+        @Attr void doStuff() {}
+        void doOtherStuff() {}
+        static struct Inner
+        {
+            // Not found by getSymbolsByUDA
+            @Attr int c;
+        }
+    }
+
+    // Finds both variables and functions with the attribute, but
+    // doesn't include the variables and functions without it.
+    static assert(getSymbolsByUDA!(A, Attr).length == 2);
+    // Can access attributes on the symbols returned by getSymbolsByUDA.
+    static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[0], Attr));
+    static assert(hasUDA!(getSymbolsByUDA!(A, Attr)[1], Attr));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static struct UDA { string name; }
+
+    static struct B
+    {
+        @UDA("X")
+        int x;
+        @UDA("Y")
+        int y;
+        @(100)
+        int z;
+    }
+
+    // Finds both UDA attributes.
+    static assert(getSymbolsByUDA!(B, UDA).length == 2);
+    // Finds one `100` attribute.
+    static assert(getSymbolsByUDA!(B, 100).length == 1);
+    // Can get the value of the UDA from the return value
+    static assert(getUDAs!(getSymbolsByUDA!(B, UDA)[0], UDA)[0].name == "X");
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static struct UDA { string name; }
+
+    @UDA("A")
+    static struct C
+    {
+        @UDA("B")
+        int d;
+    }
+
+    static assert(getSymbolsByUDA!(C, UDA).length == 2);
+    static assert(getSymbolsByUDA!(C, UDA)[0].stringof == "C");
+    static assert(getSymbolsByUDA!(C, UDA)[1].stringof == "d");
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static struct UDA { string name; }
+
+    static struct D
+    {
+        int x;
+    }
+
+    static assert(getSymbolsByUDA!(D, UDA).length == 0);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static assert(allSameType!());
+    static assert(allSameType!(int));
+    static assert(allSameType!(int, int));
+    static assert(allSameType!(int, int, int));
+    static assert(allSameType!(float, float, float));
+    static assert(!allSameType!(int, double));
+    static assert(!allSameType!(int, float, double));
+    static assert(!allSameType!(int, float, double, real));
+    static assert(!allSameType!(short, int, float, double, real));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    class C;
+    struct S1;
+    struct S2
+    {
+        T opCast(T)() const;
+    }
+
+    static assert( ifTestable!bool);
+    static assert( ifTestable!int);
+    static assert( ifTestable!(S1*));
+    static assert( ifTestable!(typeof(null)));
+    static assert( ifTestable!(int[]));
+    static assert( ifTestable!(int[string]));
+    static assert( ifTestable!S2);
+    static assert( ifTestable!C);
+    static assert(!ifTestable!S1);
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    struct S {
+        template Test() {}
+    }
+    class C {}
+    interface I {}
+    union U {}
+    static assert(isType!int);
+    static assert(isType!string);
+    static assert(isType!(int[int]));
+    static assert(isType!S);
+    static assert(isType!C);
+    static assert(isType!I);
+    static assert(isType!U);
+
+    int n;
+    void func(){}
+    static assert(!isType!n);
+    static assert(!isType!func);
+    static assert(!isType!(S.Test));
+    static assert(!isType!(S.Test!()));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    static void func(){}
+    static assert(isFunction!func);
+
+    struct S
+    {
+        void func(){}
+    }
+    static assert(isFunction!(S.func));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    class C
+    {
+        void nf() {}
+        static void sf() {}
+        final void ff() {}
+    }
+    final class FC { }
+
+    static assert(!isFinal!(C));
+    static assert( isFinal!(FC));
+
+    static assert(!isFinal!(C.nf));
+    static assert(!isFinal!(C.sf));
+    static assert( isFinal!(C.ff));
+}
+
+@safe unittest
+{
+    import std.traits;
+
+    struct S1 {}                        // Fine. Can be copied
+    struct S2 {         this(this) {}}  // Fine. Can be copied
+    struct S3 {@disable this(this);  }  // Not fine. Copying is disabled.
+    struct S4 {S3 s;}                   // Not fine. A field has copying disabled.
+
+    class C1 {}
+
+    static assert( isCopyable!S1);
+    static assert( isCopyable!S2);
+    static assert(!isCopyable!S3);
+    static assert(!isCopyable!S4);
+
+    static assert(isCopyable!C1);
+    static assert(isCopyable!int);
+    static assert(isCopyable!(int[]));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_typecons.d b/libphobos/testsuite/libphobos.phobos/std_typecons.d
new file mode 100644
index 0000000000000000000000000000000000000000..c8e43ac499aa506d12e678cd969bcd33be93dbc2
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_typecons.d
@@ -0,0 +1,1931 @@
+@safe unittest
+{
+    import std.typecons;
+
+    alias Coord = Tuple!(int, "x", int, "y", int, "z");
+    Coord c;
+    c[1] = 1;       // access by index
+    c.z = 1;        // access by given name
+    assert(c == Coord(0, 1, 1));
+
+    // names can be omitted, types can be mixed
+    alias DictEntry = Tuple!(string, int);
+    auto dict = DictEntry("seven", 7);
+
+    // element types can be inferred
+    assert(tuple(2, 3, 4)[1] == 3);
+    // type inference works with names too
+    auto tup = tuple!("x", "y", "z")(2, 3, 4);
+    assert(tup.y == 3);
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    class Widget
+    {
+        void foo() const @safe {}
+    }
+    const w1 = new Widget, w2 = new Widget;
+    w1.foo();
+    // w1 = w2 would not work; can't rebind const object
+
+    auto r = Rebindable!(const Widget)(w1);
+    // invoke method as if r were a Widget object
+    r.foo();
+    // rebind r to refer to another object
+    r = w2;
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    struct S
+    {
+        int i;
+        this(int i){this.i = i;}
+    }
+    Unique!S produce()
+    {
+        // Construct a unique instance of S on the heap
+        Unique!S ut = new S(5);
+        // Implicit transfer of ownership
+        return ut;
+    }
+    // Borrow a unique resource by ref
+    void increment(ref Unique!S ur)
+    {
+        ur.i++;
+    }
+    void consume(Unique!S u2)
+    {
+        assert(u2.i == 6);
+        // Resource automatically deleted here
+    }
+    Unique!S u1;
+    assert(u1.isEmpty);
+    u1 = produce();
+    assert(u1.i == 5);
+    increment(u1);
+    assert(u1.i == 6);
+    //consume(u1); // Error: u1 is not copyable
+    // Transfer ownership of the resource
+    consume(u1.release);
+    assert(u1.isEmpty);
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+            import std.meta : AliasSeq;
+            alias Fields = Tuple!(int, "id", string, float);
+            static assert(is(Fields.Types == AliasSeq!(int, string, float)));
+        
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+            import std.meta : AliasSeq;
+            alias Fields = Tuple!(int, "id", string, float);
+            static assert(Fields.fieldNames == AliasSeq!("id", "", ""));
+        
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+            auto t1 = tuple(1, " hello ", 'a');
+            assert(t1.toString() == `Tuple!(int, string, char)(1, " hello ", 'a')`);
+
+            void takeSeveralTypes(int n, string s, bool b)
+            {
+                assert(n == 4 && s == "test" && b == false);
+            }
+
+            auto t2 = tuple(4, "test", false);
+            //t.expand acting as a list of values
+            takeSeveralTypes(t2.expand);
+        
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+            alias ISD = Tuple!(int, string, double);
+            auto tup = ISD(1, "test", 3.2);
+            assert(tup.toString() == `Tuple!(int, string, double)(1, "test", 3.2)`);
+        
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+            int[2] ints;
+            Tuple!(int, int) t = ints;
+        
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+            alias IntVec = Tuple!(int, int, int);
+            alias DubVec = Tuple!(double, double, double);
+
+            IntVec iv = tuple(1, 1, 1);
+
+            //Ok, int can implicitly convert to double
+            DubVec dv = iv;
+            //Error: double cannot implicitly convert to int
+            //IntVec iv2 = dv;
+        
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+            Tuple!(int, string) t1 = tuple(1, "test");
+            Tuple!(double, string) t2 =  tuple(1.0, "test");
+            //Ok, int can be compared with double and
+            //both have a value of 1
+            assert(t1 == t2);
+        
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+            auto tup1 = tuple(1, 1, 1);
+            auto tup2 = tuple(1, 100, 100);
+            assert(tup1 < tup2);
+
+            //Only the first result matters for comparison
+            tup1[0] = 2;
+            assert(tup1 > tup2);
+        
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+            auto t0 = tuple(4, "hello");
+
+            auto t0Named = t0.rename!("val", "tag");
+            assert(t0Named.val == 4);
+            assert(t0Named.tag == "hello");
+
+            Tuple!(float, "dat", size_t[2], "pos") t1;
+            t1.pos = [2, 1];
+            auto t1Named = t1.rename!"height";
+            t1Named.height = 3.4f;
+            assert(t1Named.height == 3.4f);
+            assert(t1Named.pos == [2, 1]);
+            t1Named.rename!"altitude".altitude = 5;
+            assert(t1Named.height == 5);
+
+            Tuple!(int, "a", int, int, "c") t2;
+            t2 = tuple(3,4,5);
+            auto t2Named = t2.rename!("", "b");
+            // "a" no longer has a name
+            static assert(!__traits(hasMember, typeof(t2Named), "a"));
+            assert(t2Named[0] == 3);
+            assert(t2Named.b == 4);
+            assert(t2Named.c == 5);
+
+            // not allowed to specify more names than the tuple has members
+            static assert(!__traits(compiles, t2.rename!("a","b","c","d")));
+
+            // use it in a range pipeline
+            import std.range : iota, zip;
+            import std.algorithm.iteration : map, sum;
+            auto res = zip(iota(1, 4), iota(10, 13))
+                .map!(t => t.rename!("a", "b"))
+                .map!(t => t.a * t.b)
+                .sum;
+            assert(res == 68);
+
+            const tup = Tuple!(int, "a", int, "b")(2, 3);
+            const renamed = tup.rename!("c", "d");
+            assert(renamed.c + renamed.d == 5);
+        
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+            //replacing names by their current name
+
+            Tuple!(float, "dat", size_t[2], "pos") t1;
+            t1.pos = [2, 1];
+            auto t1Named = t1.rename!(["dat": "height"]);
+            t1Named.height = 3.4;
+            assert(t1Named.pos == [2, 1]);
+            t1Named.rename!(["height": "altitude"]).altitude = 5;
+            assert(t1Named.height == 5);
+
+            Tuple!(int, "a", int, "b") t2;
+            t2 = tuple(3, 4);
+            auto t2Named = t2.rename!(["a": "b", "b": "c"]);
+            assert(t2Named.b == 3);
+            assert(t2Named.c == 4);
+
+            const t3 = Tuple!(int, "a", int, "b")(3, 4);
+            const t3Named = t3.rename!(["a": "b", "b": "c"]);
+            assert(t3Named.b == 3);
+            assert(t3Named.c == 4);
+        
+}
+
+@system unittest
+{
+    import std.typecons;
+
+            //replace names by their position
+
+            Tuple!(float, "dat", size_t[2], "pos") t1;
+            t1.pos = [2, 1];
+            auto t1Named = t1.rename!([0: "height"]);
+            t1Named.height = 3.4;
+            assert(t1Named.pos == [2, 1]);
+            t1Named.rename!([0: "altitude"]).altitude = 5;
+            assert(t1Named.height == 5);
+
+            Tuple!(int, "a", int, "b", int, "c") t2;
+            t2 = tuple(3, 4, 5);
+            auto t2Named = t2.rename!([0: "c", 2: "a"]);
+            assert(t2Named.a == 5);
+            assert(t2Named.b == 4);
+            assert(t2Named.c == 3);
+        
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+            Tuple!(int, string, float, double) a;
+            a[1] = "abc";
+            a[2] = 4.5;
+            auto s = a.slice!(1, 3);
+            static assert(is(typeof(s) == Tuple!(string, float)));
+            assert(s[0] == "abc" && s[1] == 4.5);
+
+            // https://issues.dlang.org/show_bug.cgi?id=15645
+            Tuple!(int, short, bool, double) b;
+            static assert(!__traits(compiles, b.slice!(2, 4)));
+        
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+            import std.format : format;
+
+            Tuple!(int, double)[3] tupList = [ tuple(1, 1.0), tuple(2, 4.0), tuple(3, 9.0) ];
+
+            // Default format
+            assert(format("%s", tuple("a", 1)) == `Tuple!(string, int)("a", 1)`);
+
+            // One Format for each individual component
+            assert(format("%(%#x v %.4f w %#x%)", tuple(1, 1.0, 10))         == `0x1 v 1.0000 w 0xa`);
+            assert(format(  "%#x v %.4f w %#x"  , tuple(1, 1.0, 10).expand)  == `0x1 v 1.0000 w 0xa`);
+
+            // One Format for all components
+            assert(format("%(>%s<%| & %)", tuple("abc", 1, 2.3, [4, 5])) == `>abc< & >1< & >2.3< & >[4, 5]<`);
+
+            // Array of Tuples
+            assert(format("%(%(f(%d) = %.1f%);  %)", tupList) == `f(1) = 1.0;  f(2) = 4.0;  f(3) = 9.0`);
+        
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+            import std.exception : assertThrown;
+            import std.format : format, FormatException;
+
+            // Error: %( %) missing.
+            assertThrown!FormatException(
+                format("%d, %f", tuple(1, 2.0)) == `1, 2.0`
+            );
+
+            // Error: %( %| %) missing.
+            assertThrown!FormatException(
+                format("%d", tuple(1, 2)) == `1, 2`
+            );
+
+            // Error: %d inadequate for double
+            assertThrown!FormatException(
+                format("%(%d%|, %)", tuple(1, 2.0)) == `1, 2.0`
+            );
+        
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    Tuple!(int, int) point;
+    // assign coordinates
+    point[0] = 5;
+    point[1] = 6;
+    // read coordinates
+    auto x = point[0];
+    auto y = point[1];
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    alias Entry = Tuple!(int, "index", string, "value");
+    Entry e;
+    e.index = 4;
+    e.value = "Hello";
+    assert(e[1] == "Hello");
+    assert(e[0] == 4);
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    Tuple!(int, "x", int, "y") point1;
+    Tuple!(int, int) point2;
+    assert(!is(typeof(point1) == typeof(point2)));
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    import std.algorithm.iteration : sum;
+    import std.range : only;
+    auto t = tuple(1, 2);
+    assert(t.expand.only.sum == 3);
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    import std.meta : AliasSeq;
+    auto t = tuple(1, "2") ~ tuple(ushort(42), true);
+    static assert(is(t.Types == AliasSeq!(int, string, ushort, bool)));
+    assert(t[1] == "2");
+    assert(t[2] == 42);
+    assert(t[3] == true);
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    auto tup = tuple(1, "2");
+    assert(tup.reverse == tuple("2", 1));
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    auto value = tuple(5, 6.7, "hello");
+    assert(value[0] == 5);
+    assert(value[1] == 6.7);
+    assert(value[2] == "hello");
+
+    // Field names can be provided.
+    auto entry = tuple!("index", "value")(4, "Hello");
+    assert(entry.index == 4);
+    assert(entry.value == "Hello");
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    static assert(isTuple!(Tuple!()));
+    static assert(isTuple!(Tuple!(int)));
+    static assert(isTuple!(Tuple!(int, real, string)));
+    static assert(isTuple!(Tuple!(int, "x", real, "y")));
+    static assert(isTuple!(Tuple!(int, Tuple!(real), string)));
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    class Widget { int x; int y() @safe const { return x; } }
+    const a = new Widget;
+    // Fine
+    a.y();
+    // error! can't modify const a
+    // a.x = 5;
+    // error! can't modify const a
+    // a = new Widget;
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    class Widget { int x; int y() const @safe { return x; } }
+    auto a = Rebindable!(const Widget)(new Widget);
+    // Fine
+    a.y();
+    // error! can't modify const a
+    // a.x = 5;
+    // Fine
+    a = new Widget;
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    import std.range.primitives : front, popFront;
+
+    // simple version of std.algorithm.searching.maxElement
+    typeof(R.init.front) maxElement(R)(R r)
+    {
+        auto max = rebindable(r.front);
+        r.popFront;
+        foreach (e; r)
+            if (e > max)
+                max = e; // Rebindable allows const-correct reassignment
+        return max;
+    }
+    struct S
+    {
+        char[] arr;
+        alias arr this; // for comparison
+    }
+    // can't convert to mutable
+    const S cs;
+    static assert(!__traits(compiles, { S s = cs; }));
+
+    alias CS = const S;
+    CS[] arr = [CS("harp"), CS("apple"), CS("pot")];
+    CS ms = maxElement(arr);
+    assert(ms.arr == "pot");
+}
+
+@system unittest
+{
+    import std.typecons;
+
+    static struct S
+    {
+        int* ptr;
+    }
+    S s = S(new int);
+
+    const cs = s;
+    // Can't assign s.ptr to cs.ptr
+    static assert(!__traits(compiles, {s = cs;}));
+
+    Rebindable!(const S) rs = s;
+    assert(rs.ptr is s.ptr);
+    // rs.ptr is const
+    static assert(!__traits(compiles, {rs.ptr = null;}));
+
+    // Can't assign s.ptr to rs.ptr
+    static assert(!__traits(compiles, {s = rs;}));
+
+    const S cs2 = rs;
+    // Rebind rs
+    rs = cs2;
+    rs = S();
+    assert(rs.ptr is null);
+}
+
+@system unittest
+{
+    import std.typecons;
+
+    class C
+    {
+        int payload;
+        this(int p) { payload = p; }
+    }
+    const c = new C(1);
+
+    auto c2 = c.rebindable;
+    assert(c2.payload == 1);
+    // passing Rebindable to rebindable
+    c2 = c2.rebindable;
+
+    c2 = new C(2);
+    assert(c2.payload == 2);
+
+    const c3 = c2.get;
+    assert(c3.payload == 2);
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    immutable struct S
+    {
+        int[] array;
+    }
+    auto s1 = [3].idup.rebindable;
+    s1 = [4].idup.rebindable;
+    assert(s1 == [4]);
+}
+
+@system unittest
+{
+    import std.typecons;
+
+    class C
+    {
+        int payload;
+        this(int p) { payload = p; }
+    }
+    const c = new C(1);
+
+    auto c2 = c.rebindable;
+    assert(c2.payload == 1);
+    // passing Rebindable to rebindable
+    c2 = c2.rebindable;
+    assert(c2.payload == 1);
+}
+
+@system unittest
+{
+    import std.typecons;
+
+    class Data {}
+
+    static shared(Data) a;
+    static UnqualRef!(shared Data) b;
+
+    import core.thread;
+
+    auto thread = new core.thread.Thread({
+        a = new shared Data();
+        b = new shared Data();
+    });
+
+    thread.start();
+    thread.join();
+
+    assert(a !is null);
+    assert(b is null);
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    struct Banner {
+        mixin(alignForSize!(byte[6], double)(["name", "height"]));
+    }
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+        Nullable!int empty;
+        Nullable!int a = 42;
+        Nullable!int b = 42;
+        Nullable!int c = 27;
+
+        assert(empty == empty);
+        assert(empty == Nullable!int.init);
+        assert(empty != a);
+        assert(empty != b);
+        assert(empty != c);
+
+        assert(a == b);
+        assert(a != c);
+
+        assert(empty != 42);
+        assert(a == 42);
+        assert(c != 42);
+    
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+        Nullable!int ni;
+        assert(ni.isNull);
+
+        ni = 0;
+        assert(!ni.isNull);
+    
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+        Nullable!int ni = 0;
+        assert(!ni.isNull);
+
+        ni.nullify();
+        assert(ni.isNull);
+    
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+        //Passes
+        Nullable!(int*) npi;
+        assert(npi.isNull);
+
+        //Passes?!
+        npi = null;
+        assert(!npi.isNull);
+    
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    struct CustomerRecord
+    {
+        string name;
+        string address;
+        int customerNum;
+    }
+
+    Nullable!CustomerRecord getByName(string name)
+    {
+        //A bunch of hairy stuff
+
+        return Nullable!CustomerRecord.init;
+    }
+
+    auto queryResult = getByName("Doe, John");
+    if (!queryResult.isNull)
+    {
+        //Process Mr. Doe's customer record
+        auto address = queryResult.get.address;
+        auto customerNum = queryResult.get.customerNum;
+
+        //Do some things with this customer's info
+    }
+    else
+    {
+        //Add the customer to the database
+    }
+}
+
+@system unittest
+{
+    import std.typecons;
+
+    import std.exception : assertThrown;
+
+    auto a = 42.nullable;
+    assert(!a.isNull);
+    assert(a.get == 42);
+
+    a.nullify();
+    assert(a.isNull);
+    assertThrown!Throwable(a.get);
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    import std.algorithm.iteration : each, joiner;
+    Nullable!int a = 42;
+    Nullable!int b;
+    // Add each value to an array
+    int[] arr;
+    a.each!((n) => arr ~= n);
+    assert(arr == [42]);
+    b.each!((n) => arr ~= n);
+    assert(arr == [42]);
+    // Take first value from an array of Nullables
+    Nullable!int[] c = new Nullable!int[](10);
+    c[7] = Nullable!int(42);
+    assert(c.joiner.front == 42);
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    Nullable!(int, -1) ni;
+    //Initialized to "null" state
+    assert(ni.isNull);
+
+    ni = 0;
+    assert(!ni.isNull);
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    Nullable!(int, -1) ni = 0;
+    assert(!ni.isNull);
+
+    ni = -1;
+    assert(ni.isNull);
+}
+
+@system unittest
+{
+    import std.typecons;
+
+    //Passes
+    enum nullVal = cast(int*) 0xCAFEBABE;
+    Nullable!(int*, nullVal) npi;
+    assert(npi.isNull);
+
+    //Passes?!
+    npi = null;
+    assert(!npi.isNull);
+}
+
+@system unittest
+{
+    import std.typecons;
+
+    import std.exception : assertThrown, assertNotThrown;
+
+    Nullable!(int, -1) ni;
+    //`get` is implicitly called. Will throw
+    //an error in non-release mode
+    assertThrown!Throwable(ni == 0);
+
+    ni = 0;
+    assertNotThrown!Throwable(ni == 0);
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    Nullable!(size_t, size_t.max) indexOf(string[] haystack, string needle)
+    {
+        //Find the needle, returning -1 if not found
+
+        return Nullable!(size_t, size_t.max).init;
+    }
+
+    void sendLunchInvite(string name)
+    {
+    }
+
+    //It's safer than C...
+    auto coworkers = ["Jane", "Jim", "Marry", "Fred"];
+    auto pos = indexOf(coworkers, "Bob");
+    if (!pos.isNull)
+    {
+        //Send Bob an invitation to lunch
+        sendLunchInvite(coworkers[pos]);
+    }
+    else
+    {
+        //Bob not found; report the error
+    }
+
+    //And there's no overhead
+    static assert(Nullable!(size_t, size_t.max).sizeof == size_t.sizeof);
+}
+
+@system unittest
+{
+    import std.typecons;
+
+    import std.exception : assertThrown;
+
+    Nullable!(int, int.min) a;
+    assert(a.isNull);
+    assertThrown!Throwable(a.get);
+    a = 5;
+    assert(!a.isNull);
+    assert(a == 5);
+    static assert(a.sizeof == int.sizeof);
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    auto a = nullable!(int.min)(8);
+    assert(a == 8);
+    a.nullify();
+    assert(a.isNull);
+}
+
+nothrow pure @nogc @safe unittest
+{
+    import std.typecons;
+
+    alias toFloat = i => cast(float) i;
+
+    Nullable!int sample;
+
+    // apply(null) results in a null `Nullable` of the function's return type.
+    Nullable!float f = sample.apply!toFloat;
+    assert(sample.isNull && f.isNull);
+
+    sample = 3;
+
+    // apply(non-null) calls the function and wraps the result in a `Nullable`.
+    f = sample.apply!toFloat;
+    assert(!sample.isNull && !f.isNull);
+    assert(f.get == 3.0f);
+}
+
+nothrow pure @nogc @safe unittest
+{
+    import std.typecons;
+
+    alias greaterThree = i => (i > 3) ? i.nullable : Nullable!(typeof(i)).init;
+
+    Nullable!int sample;
+
+    // when the function already returns a `Nullable`, that `Nullable` is not wrapped.
+    auto result = sample.apply!greaterThree;
+    assert(sample.isNull && result.isNull);
+
+    // The function may decide to return a null `Nullable`.
+    sample = 3;
+    result = sample.apply!greaterThree;
+    assert(!sample.isNull && result.isNull);
+
+    // Or it may return a value already wrapped in a `Nullable`.
+    sample = 4;
+    result = sample.apply!greaterThree;
+    assert(!sample.isNull && !result.isNull);
+    assert(result.get == 4);
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+        NullableRef!int nr = new int(42);
+        assert(nr == 42);
+
+        int* n = new int(1);
+        nr.bind(n);
+        assert(nr == 1);
+    
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+        NullableRef!int nr;
+        assert(nr.isNull);
+
+        int* n = new int(42);
+        nr.bind(n);
+        assert(!nr.isNull && nr == 42);
+    
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+        NullableRef!int nr = new int(42);
+        assert(!nr.isNull);
+
+        nr.nullify();
+        assert(nr.isNull);
+    
+}
+
+@system unittest
+{
+    import std.typecons;
+
+        import std.exception : assertThrown, assertNotThrown;
+
+        NullableRef!int nr;
+        assert(nr.isNull);
+        assertThrown!Throwable(nr = 42);
+
+        nr.bind(new int(0));
+        assert(!nr.isNull);
+        assertNotThrown!Throwable(nr = 42);
+        assert(nr == 42);
+    
+}
+
+@system unittest
+{
+    import std.typecons;
+
+        import std.exception : assertThrown, assertNotThrown;
+
+        NullableRef!int nr;
+        //`get` is implicitly called. Will throw
+        //an error in non-release mode
+        assertThrown!Throwable(nr == 0);
+
+        nr.bind(new int(0));
+        assertNotThrown!Throwable(nr == 0);
+    
+}
+
+@system unittest
+{
+    import std.typecons;
+
+    import std.exception : assertThrown;
+
+    int x = 5, y = 7;
+    auto a = nullableRef(&x);
+    assert(!a.isNull);
+    assert(a == 5);
+    assert(x == 5);
+    a = 42;
+    assert(x == 42);
+    assert(!a.isNull);
+    assert(a == 42);
+    a.nullify();
+    assert(x == 42);
+    assert(a.isNull);
+    assertThrown!Throwable(a.get);
+    assertThrown!Throwable(a = 71);
+    a.bind(&y);
+    assert(a == 7);
+    y = 135;
+    assert(a == 135);
+}
+
+@system unittest
+{
+    import std.typecons;
+
+    import std.math.traits : isNaN;
+
+    static abstract class C
+    {
+        int m_value;
+        this(int v) { m_value = v; }
+        int value() @property { return m_value; }
+
+        abstract real realValue() @property;
+        abstract void doSomething();
+    }
+
+    auto c = new BlackHole!C(42);
+    assert(c.value == 42);
+
+    // Returns real.init which is NaN
+    assert(c.realValue.isNaN);
+    // Abstract functions are implemented as do-nothing
+    c.doSomething();
+}
+
+@system unittest
+{
+    import std.typecons;
+
+    import std.exception : assertThrown;
+
+    static class C
+    {
+        abstract void notYetImplemented();
+    }
+
+    auto c = new WhiteHole!C;
+    assertThrown!NotImplementedError(c.notYetImplemented()); // throws an Error
+}
+
+@system unittest
+{
+    import std.typecons;
+
+    import std.exception : assertThrown;
+    // nothrow
+    {
+        interface I_1
+        {
+            void foo();
+            void bar() nothrow;
+        }
+        auto o = new WhiteHole!I_1;
+        assertThrown!NotImplementedError(o.foo());
+        assertThrown!NotImplementedError(o.bar());
+    }
+    // doc example
+    {
+        static class C
+        {
+            abstract void notYetImplemented();
+        }
+
+        auto c = new WhiteHole!C;
+        try
+        {
+            c.notYetImplemented();
+            assert(0);
+        }
+        catch (Error e) {}
+    }
+}
+
+@system unittest
+{
+    import std.typecons;
+
+    interface PackageSupplier
+    {
+        int foo();
+        int bar();
+    }
+
+    static abstract class AbstractFallbackPackageSupplier : PackageSupplier
+    {
+        protected PackageSupplier default_, fallback;
+
+        this(PackageSupplier default_, PackageSupplier fallback)
+        {
+            this.default_ = default_;
+            this.fallback = fallback;
+        }
+
+        abstract int foo();
+        abstract int bar();
+    }
+
+    template fallback(T, alias func)
+    {
+        import std.format : format;
+        // for all implemented methods:
+        // - try default first
+        // - only on a failure run & return fallback
+        enum fallback = q{
+            try
+            {
+                return default_.%1$s(args);
+            }
+            catch (Exception)
+            {
+                return fallback.%1$s(args);
+            }
+        }.format(__traits(identifier, func));
+    }
+
+    // combines two classes and use the second one as fallback
+    alias FallbackPackageSupplier = AutoImplement!(AbstractFallbackPackageSupplier, fallback);
+
+    class FailingPackageSupplier : PackageSupplier
+    {
+        int foo(){ throw new Exception("failure"); }
+        int bar(){ return 2;}
+    }
+
+    class BackupPackageSupplier : PackageSupplier
+    {
+        int foo(){ return -1; }
+        int bar(){ return -1;}
+    }
+
+    auto registry = new FallbackPackageSupplier(new FailingPackageSupplier(), new BackupPackageSupplier());
+
+    assert(registry.foo() == -1);
+    assert(registry.bar() == 2);
+}
+
+@system unittest
+{
+    import std.typecons;
+
+    alias BlackHole(Base) = AutoImplement!(Base, generateEmptyFunction);
+
+    interface I
+    {
+        int foo();
+        string bar();
+    }
+
+    auto i = new BlackHole!I();
+    // generateEmptyFunction returns the default value of the return type without doing anything
+    assert(i.foo == 0);
+    assert(i.bar is null);
+}
+
+@system unittest
+{
+    import std.typecons;
+
+    import std.exception : assertThrown;
+
+    alias WhiteHole(Base) = AutoImplement!(Base, generateAssertTrap);
+
+    interface I
+    {
+        int foo();
+        string bar();
+    }
+
+    auto i = new WhiteHole!I();
+    // generateAssertTrap throws an exception for every unimplemented function of the interface
+    assertThrown!NotImplementedError(i.foo);
+    assertThrown!NotImplementedError(i.bar);
+}
+
+@system unittest
+{
+    import std.typecons;
+
+    interface Quack
+    {
+        int quack();
+        @property int height();
+    }
+    interface Flyer
+    {
+        @property int height();
+    }
+    class Duck : Quack
+    {
+        int quack() { return 1; }
+        @property int height() { return 10; }
+    }
+    class Human
+    {
+        int quack() { return 2; }
+        @property int height() { return 20; }
+    }
+
+    Duck d1 = new Duck();
+    Human h1 = new Human();
+
+    interface Refleshable
+    {
+        int reflesh();
+    }
+
+    // does not have structural conformance
+    static assert(!__traits(compiles, d1.wrap!Refleshable));
+    static assert(!__traits(compiles, h1.wrap!Refleshable));
+
+    // strict upcast
+    Quack qd = d1.wrap!Quack;
+    assert(qd is d1);
+    assert(qd.quack() == 1);    // calls Duck.quack
+    // strict downcast
+    Duck d2 = qd.unwrap!Duck;
+    assert(d2 is d1);
+
+    // structural upcast
+    Quack qh = h1.wrap!Quack;
+    assert(qh.quack() == 2);    // calls Human.quack
+    // structural downcast
+    Human h2 = qh.unwrap!Human;
+    assert(h2 is h1);
+
+    // structural upcast (two steps)
+    Quack qx = h1.wrap!Quack;   // Human -> Quack
+    Flyer fx = qx.wrap!Flyer;   // Quack -> Flyer
+    assert(fx.height == 20);    // calls Human.height
+    // structural downcast (two steps)
+    Quack qy = fx.unwrap!Quack; // Flyer -> Quack
+    Human hy = qy.unwrap!Human; // Quack -> Human
+    assert(hy is h1);
+    // structural downcast (one step)
+    Human hz = fx.unwrap!Human; // Flyer -> Human
+    assert(hz is h1);
+}
+
+@system unittest
+{
+    import std.typecons;
+
+    import std.traits : FunctionAttribute, functionAttributes;
+    interface A { int run(); }
+    interface B { int stop(); @property int status(); }
+    class X
+    {
+        int run() { return 1; }
+        int stop() { return 2; }
+        @property int status() { return 3; }
+    }
+
+    auto x = new X();
+    auto ab = x.wrap!(A, B);
+    A a = ab;
+    B b = ab;
+    assert(a.run() == 1);
+    assert(b.stop() == 2);
+    assert(b.status == 3);
+    static assert(functionAttributes!(typeof(ab).status) & FunctionAttribute.property);
+}
+
+@system unittest
+{
+    import std.typecons;
+
+    import core.exception : AssertError;
+    import std.exception : assertThrown;
+
+    struct Foo
+    {
+        int a = 42;
+    }
+
+    SafeRefCounted!(Foo, RefCountedAutoInitialize.yes) rcAuto;
+    SafeRefCounted!(Foo, RefCountedAutoInitialize.no) rcNoAuto;
+
+    assert(rcAuto.refCountedPayload.a == 42);
+
+    assertThrown!AssertError(rcNoAuto.refCountedPayload);
+    rcNoAuto.refCountedStore.ensureInitialized;
+    assert(rcNoAuto.refCountedPayload.a == 42);
+}
+
+pure @system nothrow @nogc unittest
+{
+    import std.typecons;
+
+    // A pair of an `int` and a `size_t` - the latter being the
+    // reference count - will be dynamically allocated
+    auto rc1 = SafeRefCounted!int(5);
+    assert(rc1 == 5);
+    // No more allocation, add just one extra reference count
+    auto rc2 = rc1;
+    // Reference semantics
+    rc2 = 42;
+    assert(rc1 == 42);
+    // the pair will be freed when rc1 and rc2 go out of scope
+}
+
+@safe pure nothrow unittest
+{
+    import std.typecons;
+
+    auto rcInt = safeRefCounted(5);
+    assert(rcInt.borrow!(theInt => theInt) == 5);
+    auto sameInt = rcInt;
+    assert(sameInt.borrow!"a" == 5);
+
+    // using `ref` in the function
+    auto arr = [0, 1, 2, 3, 4, 5, 6];
+    sameInt.borrow!(ref (x) => arr[x]) = 10;
+    assert(arr == [0, 1, 2, 3, 4, 10, 6]);
+
+    // modifying the payload via an alias
+    sameInt.borrow!"a*=2";
+    assert(rcInt.borrow!"a" == 10);
+}
+
+@system unittest
+{
+    import std.typecons;
+
+    static struct File
+    {
+        static size_t nDestroyed;
+        string name;
+        @disable this(this); // not copyable
+        ~this() { name = null; ++nDestroyed; }
+    }
+
+    auto file = File("name");
+    assert(file.name == "name");
+    // file cannot be copied and has unique ownership
+    static assert(!__traits(compiles, {auto file2 = file;}));
+
+    assert(File.nDestroyed == 0);
+
+    // make the file ref counted to share ownership
+    // Note:
+    //   We write a compound statement (brace-delimited scope) in which all `SafeRefCounted!File` handles are created and deleted.
+    //   This allows us to see (after the scope) what happens after all handles have been destroyed.
+    {
+        // We move the content of `file` to a separate (and heap-allocated) `File` object,
+        // managed-and-accessed via one-or-multiple (initially: one) `SafeRefCounted!File` objects ("handles").
+        // This "moving":
+        //   (1) invokes `file`'s destructor (=> `File.nDestroyed` is incremented from 0 to 1 and `file.name` becomes `null`);
+        //   (2) overwrites `file` with `File.init` (=> `file.name` becomes `null`).
+        // It appears that writing `name = null;` in the destructor is redundant,
+        // but please note that (2) is only performed if `File` defines a destructor (or post-blit operator),
+        // and in the absence of the `nDestroyed` instrumentation there would have been no reason to define a destructor.
+        import std.algorithm.mutation : move;
+        auto rcFile = safeRefCounted(move(file));
+        assert(rcFile.name == "name");
+        assert(File.nDestroyed == 1);
+        assert(file.name == null);
+
+        // We create another `SafeRefCounted!File` handle to the same separate `File` object.
+        // While any of the handles is still alive, the `File` object is kept alive (=> `File.nDestroyed` is not modified).
+        auto rcFile2 = rcFile;
+        assert(rcFile.refCountedStore.refCount == 2);
+        assert(File.nDestroyed == 1);
+    }
+    // The separate `File` object is deleted when the last `SafeRefCounted!File` handle is destroyed
+    // (i.e. at the closing brace of the compound statement above, which destroys both handles: `rcFile` and `rcFile2`)
+    // (=> `File.nDestroyed` is incremented again, from 1 to 2):
+    assert(File.nDestroyed == 2);
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    struct MyInt
+    {
+        private int value;
+        mixin Proxy!value;
+
+        this(int n){ value = n; }
+    }
+
+    MyInt n = 10;
+
+    // Enable operations that original type has.
+    ++n;
+    assert(n == 11);
+    assert(n * 2 == 22);
+
+    void func(int n) { }
+
+    // Disable implicit conversions to original type.
+    //int x = n;
+    //func(n);
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    struct NewIntType
+    {
+        //Won't work; the literal '1'
+        //is an rvalue, not an lvalue
+        //mixin Proxy!1;
+
+        //Okay, n is an lvalue
+        int n;
+        mixin Proxy!n;
+
+        this(int n) { this.n = n; }
+    }
+
+    NewIntType nit = 0;
+    nit++;
+    assert(nit == 1);
+
+
+    struct NewObjectType
+    {
+        Object obj;
+        //Ok, obj is an lvalue
+        mixin Proxy!obj;
+
+        this (Object o) { obj = o; }
+    }
+
+    NewObjectType not = new Object();
+    assert(__traits(compiles, not.toHash()));
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    import std.math.traits : isInfinity;
+
+    float f = 1.0;
+    assert(!f.isInfinity);
+
+    struct NewFloat
+    {
+        float _;
+        mixin Proxy!_;
+
+        this(float f) { _ = f; }
+    }
+
+    NewFloat nf = 1.0f;
+    assert(!nf.isInfinity);
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+        import std.conv : to;
+
+        int i = 123;
+        auto td = Typedef!int(i);
+        assert(i.to!string == td.to!string);
+    
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    alias MyInt = Typedef!int;
+    MyInt foo = 10;
+    foo++;
+    assert(foo == 11);
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    alias MyIntInit = Typedef!(int, 42);
+    static assert(is(TypedefType!MyIntInit == int));
+    static assert(MyIntInit() == 42);
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    alias MyInt = Typedef!int;
+    static void takeInt(int) {}
+    static void takeMyInt(MyInt) {}
+
+    int i;
+    takeInt(i);    // ok
+    static assert(!__traits(compiles, takeMyInt(i)));
+
+    MyInt myInt;
+    static assert(!__traits(compiles, takeInt(myInt)));
+    takeMyInt(myInt);  // ok
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    alias TypeInt1 = Typedef!int;
+    alias TypeInt2 = Typedef!int;
+
+    // The two Typedefs are the same type.
+    static assert(is(TypeInt1 == TypeInt2));
+
+    alias MoneyEuros = Typedef!(float, float.init, "euros");
+    alias MoneyDollars = Typedef!(float, float.init, "dollars");
+
+    // The two Typedefs are _not_ the same type.
+    static assert(!is(MoneyEuros == MoneyDollars));
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    import std.conv : to;
+
+    alias MyInt = Typedef!int;
+    static assert(is(TypedefType!MyInt == int));
+
+    /// Instantiating with a non-Typedef will return that type
+    static assert(is(TypedefType!int == int));
+
+    string num = "5";
+
+    // extract the needed type
+    MyInt myInt = MyInt( num.to!(TypedefType!MyInt) );
+    assert(myInt == 5);
+
+    // cast to the underlying type to get the value that's being wrapped
+    int x = cast(TypedefType!MyInt) myInt;
+
+    alias MyIntInit = Typedef!(int, 42);
+    static assert(is(TypedefType!MyIntInit == int));
+    static assert(MyIntInit() == 42);
+}
+
+@system unittest
+{
+    import std.typecons;
+
+    class A
+    {
+        int x;
+        this()     {x = 0;}
+        this(int i){x = i;}
+        ~this()    {}
+    }
+
+    // Standard usage, constructing A on the stack
+    auto a1 = scoped!A();
+    a1.x = 42;
+
+    // Result of `scoped` call implicitly converts to a class reference
+    A aRef = a1;
+    assert(aRef.x == 42);
+
+    // Scoped destruction
+    {
+        auto a2 = scoped!A(1);
+        assert(a2.x == 1);
+        aRef = a2;
+        // a2 is destroyed here, calling A's destructor
+    }
+    // aRef is now an invalid reference
+
+    // Here the temporary scoped A is immediately destroyed.
+    // This means the reference is then invalid.
+    version (Bug)
+    {
+        // Wrong, should use `auto`
+        A invalid = scoped!A();
+    }
+
+    // Restrictions
+    version (Bug)
+    {
+        import std.algorithm.mutation : move;
+        auto invalid = a1.move; // illegal, scoped objects can't be moved
+    }
+    static assert(!is(typeof({
+        auto e1 = a1; // illegal, scoped objects can't be copied
+        assert([a1][0].x == 42); // ditto
+    })));
+    static assert(!is(typeof({
+        alias ScopedObject = typeof(a1);
+        auto e2 = ScopedObject();  // illegal, must be built via scoped!A
+        auto e3 = ScopedObject(1); // ditto
+    })));
+
+    // Use with alias
+    alias makeScopedA = scoped!A;
+    auto a3 = makeScopedA();
+    auto a4 = makeScopedA(1);
+
+    // Use as member variable
+    struct B
+    {
+        typeof(scoped!A()) a; // note the trailing parentheses
+
+        this(int i)
+        {
+            // construct member
+            a = scoped!A(i);
+        }
+    }
+
+    // Stack-allocate
+    auto b1 = B(5);
+    aRef = b1.a;
+    assert(aRef.x == 5);
+    destroy(b1); // calls A's destructor for b1.a
+    // aRef is now an invalid reference
+
+    // Heap-allocate
+    auto b2 = new B(6);
+    assert(b2.a.x == 6);
+    destroy(*b2); // calls A's destructor for b2.a
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    Flag!"abc" flag;
+
+    assert(flag == Flag!"abc".no);
+    assert(flag == No.abc);
+    assert(!flag);
+    if (flag) assert(0);
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    auto flag = Yes.abc;
+
+    assert(flag);
+    assert(flag == Yes.abc);
+    if (!flag) assert(0);
+    if (flag) {} else assert(0);
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    Flag!"abc" flag;
+
+    assert(flag == Flag!"abc".no);
+    assert(flag == No.abc);
+    assert(!flag);
+    if (flag) assert(0);
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    auto flag = Yes.abc;
+
+    assert(flag);
+    assert(flag == Yes.abc);
+    if (!flag) assert(0);
+    if (flag) {} else assert(0);
+}
+
+@safe pure nothrow unittest
+{
+    import std.typecons;
+
+    enum A
+    {
+        None,
+        A = 1 << 0,
+        B = 1 << 1,
+        C = 1 << 2,
+        D = 1 << 3,
+    }
+
+    static assert(isBitFlagEnum!A);
+}
+
+@safe pure nothrow unittest
+{
+    import std.typecons;
+
+    enum B
+    {
+        A,
+        B,
+        C,
+        D // D == 3
+    }
+
+    static assert(!isBitFlagEnum!B);
+}
+
+@safe pure nothrow unittest
+{
+    import std.typecons;
+
+    enum C: double
+    {
+        A = 1 << 0,
+        B = 1 << 1
+    }
+
+    static assert(!isBitFlagEnum!C);
+}
+
+@safe @nogc pure nothrow unittest
+{
+    import std.typecons;
+
+    enum Enum
+    {
+        A = 1 << 0,
+    }
+
+    // A default constructed BitFlags has no value set
+    immutable BitFlags!Enum flags_empty;
+    assert(!flags_empty.A);
+
+    // Value can be set with the | operator
+    immutable flags_A = flags_empty | Enum.A;
+
+    // and tested using property access
+    assert(flags_A.A);
+
+    // or the & operator
+    assert(flags_A & Enum.A);
+    // which commutes.
+    assert(Enum.A & flags_A);
+}
+
+@safe @nogc pure nothrow unittest
+{
+    import std.typecons;
+
+    enum Enum
+    {
+        None,
+        A = 1 << 0,
+        B = 1 << 1,
+        C = 1 << 2
+    }
+
+    immutable BitFlags!Enum flags_empty;
+    assert(!(flags_empty & (Enum.A | Enum.B | Enum.C)));
+    assert(!(flags_empty & Enum.A) && !(flags_empty & Enum.B) && !(flags_empty & Enum.C));
+}
+
+@safe @nogc pure nothrow unittest
+{
+    import std.typecons;
+
+    enum Enum
+    {
+        A = 1 << 0,
+        B = 1 << 1,
+        C = 1 << 2,
+    }
+    immutable BitFlags!Enum flags_AB = BitFlags!Enum(Enum.A, Enum.B);
+    immutable BitFlags!Enum flags_BC = BitFlags!Enum(Enum.B, Enum.C);
+
+    // Use the ~ operator for subtracting flags
+    immutable BitFlags!Enum flags_B = flags_AB & ~BitFlags!Enum(Enum.A);
+    assert(!flags_B.A && flags_B.B && !flags_B.C);
+
+    // use & between BitFlags for intersection
+    assert(flags_B == (flags_BC & flags_AB));
+}
+
+@safe @nogc pure nothrow unittest
+{
+    import std.typecons;
+
+    enum Enum
+    {
+        A = 1 << 0,
+        B = 1 << 1,
+    }
+
+    BitFlags!Enum flags_empty, temp, flags_AB;
+    flags_AB = Enum.A | Enum.B;
+
+    temp |= flags_AB;
+    assert(temp == (flags_empty | flags_AB));
+
+    temp = flags_empty;
+    temp |= Enum.B;
+    assert(temp == (flags_empty | Enum.B));
+
+    temp = flags_empty;
+    temp &= flags_AB;
+    assert(temp == (flags_empty & flags_AB));
+
+    temp = flags_empty;
+    temp &= Enum.A;
+    assert(temp == (flags_empty & Enum.A));
+}
+
+@safe @nogc pure nothrow unittest
+{
+    import std.typecons;
+
+    enum Enum
+    {
+        A = 1 << 0,
+        B = 1 << 1,
+    }
+
+    BitFlags!Enum flags;
+
+    // BitFlags with no value set evaluate to false
+    assert(!flags);
+
+    // BitFlags with at least one value set evaluate to true
+    flags |= Enum.A;
+    assert(flags);
+
+    // This can be useful to check intersection between BitFlags
+    BitFlags!Enum flags_AB = Enum.A | Enum.B;
+    assert(flags & flags_AB);
+    assert(flags & Enum.A);
+
+    // You can of course get you raw value out of flags
+    auto value = cast(int) flags;
+    assert(value == Enum.A);
+}
+
+@safe @nogc pure nothrow unittest
+{
+    import std.typecons;
+
+    enum UnsafeEnum
+    {
+        A = 1,
+        B = 2,
+        C = 4,
+        BC = B|C
+    }
+    static assert(!__traits(compiles, { BitFlags!UnsafeEnum flags; }));
+    BitFlags!(UnsafeEnum, Yes.unsafe) flags;
+
+    // property access tests for exact match of unsafe enums
+    flags.B = true;
+    assert(!flags.BC); // only B
+    flags.C = true;
+    assert(flags.BC); // both B and C
+    flags.B = false;
+    assert(!flags.BC); // only C
+
+    // property access sets all bits of unsafe enum group
+    flags = flags.init;
+    flags.BC = true;
+    assert(!flags.A && flags.B && flags.C);
+    flags.A = true;
+    flags.BC = false;
+    assert(flags.A && !flags.B && !flags.C);
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    static assert(
+        is(ReplaceType!(int, string, int[]) == string[]) &&
+        is(ReplaceType!(int, string, int[int]) == string[string]) &&
+        is(ReplaceType!(int, string, const(int)[]) == const(string)[]) &&
+        is(ReplaceType!(int, string, Tuple!(int[], float))
+            == Tuple!(string[], float))
+    );
+}
+
+@safe unittest
+{
+    import std.typecons;
+
+    import std.traits : isArray;
+
+    static assert(
+        is(ReplaceTypeUnless!(isArray, int, string, int*) == string*) &&
+        is(ReplaceTypeUnless!(isArray, int, string, int[]) == int[]) &&
+        is(ReplaceTypeUnless!(isArray, int, string, Tuple!(int, int[]))
+            == Tuple!(string, int[]))
+   );
+}
+
+@safe @nogc nothrow pure unittest
+{
+    import std.typecons;
+
+    Ternary a;
+    assert(a == Ternary.unknown);
+
+    assert(~Ternary.yes == Ternary.no);
+    assert(~Ternary.no == Ternary.yes);
+    assert(~Ternary.unknown == Ternary.unknown);
+}
+
+pure @system nothrow @nogc unittest
+{
+    import std.typecons;
+
+    auto rc1 = RefCounted!int(5);
+    assert(rc1 == 5);
+    auto rc2 = rc1;
+    rc2 = 42;
+    assert(rc1 == 42);
+}
+
+@system unittest
+{
+    import std.typecons;
+
+    static struct File
+    {
+        static size_t nDestroyed;
+        string name;
+        @disable this(this); // not copyable
+        ~this() { name = null; ++nDestroyed; }
+    }
+
+    auto file = File("name");
+    assert(file.name == "name");
+    static assert(!__traits(compiles, {auto file2 = file;}));
+    assert(File.nDestroyed == 0);
+
+    {
+        import std.algorithm.mutation : move;
+        auto rcFile = refCounted(move(file));
+        assert(rcFile.name == "name");
+        assert(File.nDestroyed == 1);
+        assert(file.name == null);
+
+        auto rcFile2 = rcFile;
+        assert(rcFile.refCountedStore.refCount == 2);
+        assert(File.nDestroyed == 1);
+    }
+
+    assert(File.nDestroyed == 2);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_typetuple.d b/libphobos/testsuite/libphobos.phobos/std_typetuple.d
new file mode 100644
index 0000000000000000000000000000000000000000..e5ccd7637a216059c3f1c6aea61ade121be868fc
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_typetuple.d
@@ -0,0 +1,24 @@
+@safe unittest
+{
+    import std.typetuple;
+
+    import std.typetuple;
+    alias TL = TypeTuple!(int, double);
+
+    int foo(TL td)  // same as int foo(int, double);
+    {
+        return td[0] + cast(int) td[1];
+    }
+    assert(foo(1, 2.5) == 3);
+}
+
+@safe unittest
+{
+    import std.typetuple;
+
+    alias TL = TypeTuple!(int, double);
+
+    alias Types = TypeTuple!(TL, char);
+    static assert(is(Types == TypeTuple!(int, double, char)));
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_uni_package.d b/libphobos/testsuite/libphobos.phobos/std_uni_package.d
new file mode 100644
index 0000000000000000000000000000000000000000..cdb3439c941c75b1941a08a6e98be6b17fc61d5d
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_uni_package.d
@@ -0,0 +1,547 @@
+pure @safe unittest
+{
+    import std.uni;
+
+        import std.algorithm.comparison : equal;
+
+        auto set = CodepointSet('a', 'z'+1, 'а', 'я'+1);
+        foreach (v; 'a'..'z'+1)
+            assert(set[v]);
+        // Cyrillic lowercase interval
+        foreach (v; 'а'..'я'+1)
+            assert(set[v]);
+        //specific order is not required, intervals may interesect
+        auto set2 = CodepointSet('а', 'я'+1, 'a', 'd', 'b', 'z'+1);
+        //the same end result
+        assert(set2.byInterval.equal(set.byInterval));
+        // test constructor this(Range)(Range intervals)
+        auto chessPiecesWhite = CodepointInterval(9812, 9818);
+        auto chessPiecesBlack = CodepointInterval(9818, 9824);
+        auto set3 = CodepointSet([chessPiecesWhite, chessPiecesBlack]);
+        foreach (v; '♔'..'♟'+1)
+            assert(set3[v]);
+    
+}
+
+pure @safe unittest
+{
+    import std.uni;
+
+        auto gothic = unicode.Gothic;
+        // Gothic letter ahsa
+        assert(gothic['\U00010330']);
+        // no ascii in Gothic obviously
+        assert(!gothic['$']);
+    
+}
+
+pure @safe unittest
+{
+    import std.uni;
+
+        import std.algorithm.comparison : equal;
+        import std.range : iota;
+
+        auto lower = unicode.LowerCase;
+        auto upper = unicode.UpperCase;
+        auto ascii = unicode.ASCII;
+
+        assert((lower & upper).empty); // no intersection
+        auto lowerASCII = lower & ascii;
+        assert(lowerASCII.byCodepoint.equal(iota('a', 'z'+1)));
+        // throw away all of the lowercase ASCII
+        assert((ascii - lower).length == 128 - 26);
+
+        auto onlyOneOf = lower ~ ascii;
+        assert(!onlyOneOf['Δ']); // not ASCII and not lowercase
+        assert(onlyOneOf['$']); // ASCII and not lowercase
+        assert(!onlyOneOf['a']); // ASCII and lowercase
+        assert(onlyOneOf['я']); // not ASCII but lowercase
+
+        // throw away all cased letters from ASCII
+        auto noLetters = ascii - (lower | upper);
+        assert(noLetters.length == 128 - 26*2);
+    
+}
+
+pure @safe unittest
+{
+    import std.uni;
+
+        assert('я' in unicode.Cyrillic);
+        assert(!('z' in unicode.Cyrillic));
+    
+}
+
+pure @safe unittest
+{
+    import std.uni;
+
+        import std.algorithm.comparison : equal;
+        import std.range : iota;
+
+        auto set = unicode.ASCII;
+        set.byCodepoint.equal(iota(0, 0x80));
+    
+}
+
+pure @safe unittest
+{
+    import std.uni;
+
+        import std.conv : to;
+        import std.format : format;
+        import std.uni : unicode;
+
+        // This was originally using Cyrillic script.
+        // Unfortunately this is a pretty active range for changes,
+        // and hence broke in an update.
+        // Therefore the range Basic latin was used instead as it
+        // unlikely to ever change.
+
+        assert(unicode.InBasic_latin.to!string == "[0..128)");
+
+        // The specs '%s' and '%d' are equivalent to the to!string call above.
+        assert(format("%d", unicode.InBasic_latin) == unicode.InBasic_latin.to!string);
+
+        assert(format("%#x", unicode.InBasic_latin) == "[0..0x80)");
+        assert(format("%#X", unicode.InBasic_latin) == "[0..0X80)");
+    
+}
+
+pure @safe unittest
+{
+    import std.uni;
+
+        CodepointSet someSet;
+        someSet.add('0', '5').add('A','Z'+1);
+        someSet.add('5', '9'+1);
+        assert(someSet['0']);
+        assert(someSet['5']);
+        assert(someSet['9']);
+        assert(someSet['Z']);
+    
+}
+
+pure @safe unittest
+{
+    import std.uni;
+
+        auto set = unicode.ASCII;
+        // union with the inverse gets all of the code points in the Unicode
+        assert((set | set.inverted).length == 0x110000);
+        // no intersection with the inverse
+        assert((set & set.inverted).empty);
+    
+}
+
+pure @safe unittest
+{
+    import std.uni;
+
+        CodepointSet emptySet;
+        assert(emptySet.length == 0);
+        assert(emptySet.empty);
+    
+}
+
+pure @safe unittest
+{
+    import std.uni;
+
+        string truth = "2² = 4";
+        auto m = utfMatcher!char(unicode.Number);
+        assert(m.match(truth)); // '2' is a number all right
+        assert(truth == "² = 4"); // skips on match
+        assert(m.match(truth)); // so is the superscript '2'
+        assert(!m.match(truth)); // space is not a number
+        assert(truth == " = 4"); // unaffected on no match
+        assert(!m.skip(truth)); // same test ...
+        assert(truth == "= 4"); // but skips a codepoint regardless
+        assert(!m.test(truth)); // '=' is not a number
+        assert(truth == "= 4"); // test never affects argument
+    
+}
+
+@safe unittest
+{
+    import std.uni;
+
+        import std.exception : collectException;
+        auto ascii = unicode.ASCII;
+        assert(ascii['A']);
+        assert(ascii['~']);
+        assert(!ascii['\u00e0']);
+        // matching is case-insensitive
+        assert(ascii == unicode.ascII);
+        assert(!ascii['à']);
+        // underscores, '-' and whitespace in names are ignored too
+        auto latin = unicode.in_latin1_Supplement;
+        assert(latin['à']);
+        assert(!latin['$']);
+        // BTW Latin 1 Supplement is a block, hence "In" prefix
+        assert(latin == unicode("In Latin 1 Supplement"));
+        // run-time look up throws if no such set is found
+        assert(collectException(unicode("InCyrilliac")));
+    
+}
+
+@safe unittest
+{
+    import std.uni;
+
+        // use .block for explicitness
+        assert(unicode.block.Greek_and_Coptic == unicode.InGreek_and_Coptic);
+    
+}
+
+@safe unittest
+{
+    import std.uni;
+
+        auto arabicScript = unicode.script.arabic;
+        auto arabicBlock = unicode.block.arabic;
+        // there is an intersection between script and block
+        assert(arabicBlock['؁']);
+        assert(arabicScript['؁']);
+        // but they are different
+        assert(arabicBlock != arabicScript);
+        assert(arabicBlock == unicode.inArabic);
+        assert(arabicScript == unicode.arabic);
+    
+}
+
+@safe unittest
+{
+    import std.uni;
+
+        // L here is syllable type not Letter as in unicode.L short-cut
+        auto leadingVowel = unicode.hangulSyllableType("L");
+        // check that some leading vowels are present
+        foreach (vowel; '\u1110'..'\u115F')
+            assert(leadingVowel[vowel]);
+        assert(leadingVowel == unicode.hangulSyllableType.L);
+    
+}
+
+@safe unittest
+{
+    import std.uni;
+
+        import std.uni : unicode;
+        string pat = "[a-zA-Z0-9]hello";
+        auto set = unicode.parseSet(pat);
+        // check some of the codepoints
+        assert(set['a'] && set['A'] && set['9']);
+        assert(pat == "hello");
+    
+}
+
+@safe unittest
+{
+    import std.uni;
+
+    assert(graphemeStride("  ", 1) == 1);
+    // A + combing ring above
+    string city = "A\u030Arhus";
+    size_t first = graphemeStride(city, 0);
+    assert(first == 3); //\u030A has 2 UTF-8 code units
+    assert(city[0 .. first] == "A\u030A");
+    assert(city[first..$] == "rhus");
+}
+
+@safe pure unittest
+{
+    import std.uni;
+
+    // Two Union Jacks of the Great Britain in each
+    string s = "\U0001F1EC\U0001F1E7\U0001F1EC\U0001F1E7";
+    wstring ws = "\U0001F1EC\U0001F1E7\U0001F1EC\U0001F1E7";
+    dstring ds = "\U0001F1EC\U0001F1E7\U0001F1EC\U0001F1E7";
+
+    // String pop length in code units, not points.
+    assert(s.popGrapheme() == 8);
+    assert(ws.popGrapheme() == 4);
+    assert(ds.popGrapheme() == 2);
+
+    assert(s == "\U0001F1EC\U0001F1E7");
+    assert(ws == "\U0001F1EC\U0001F1E7");
+    assert(ds == "\U0001F1EC\U0001F1E7");
+
+    import std.algorithm.comparison : equal;
+    import std.algorithm.iteration : filter;
+
+    // Also works for non-random access ranges as long as the
+    // character type is 32-bit.
+    auto testPiece = "\r\nhello!"d.filter!(x => !x.isAlpha);
+    // Windows-style line ending is two code points in a single grapheme.
+    assert(testPiece.popGrapheme() == 2);
+    assert(testPiece.equal("!"d));
+}
+
+@safe unittest
+{
+    import std.uni;
+
+    import std.algorithm.comparison : equal;
+    import std.range.primitives : walkLength;
+    import std.range : take, drop;
+    auto text = "noe\u0308l"; // noël using e + combining diaeresis
+    assert(text.walkLength == 5); // 5 code points
+
+    auto gText = text.byGrapheme;
+    assert(gText.walkLength == 4); // 4 graphemes
+
+    assert(gText.take(3).equal("noe\u0308".byGrapheme));
+    assert(gText.drop(3).equal("l".byGrapheme));
+}
+
+@safe unittest
+{
+    import std.uni;
+
+    import std.array : array;
+    import std.conv : text;
+    import std.range : retro;
+
+    string s = "noe\u0308l"; // noël
+
+    // reverse it and convert the result to a string
+    string reverse = s.byGrapheme
+        .array
+        .retro
+        .byCodePoint
+        .text;
+
+    assert(reverse == "le\u0308on"); // lëon
+}
+
+@safe unittest
+{
+    import std.uni;
+
+        auto g = Grapheme("A\u0302");
+        assert(g[0] == 'A');
+        assert(g.valid);
+        g[1] = '~'; // ASCII tilda is not a combining mark
+        assert(g[1] == '~');
+        assert(!g.valid);
+    
+}
+
+@safe unittest
+{
+    import std.uni;
+
+        import std.algorithm.comparison : equal;
+        auto g = Grapheme("A");
+        assert(g.valid);
+        g ~= '\u0301';
+        assert(g[].equal("A\u0301"));
+        assert(g.valid);
+        g ~= "B";
+        // not a valid grapheme cluster anymore
+        assert(!g.valid);
+        // still could be useful though
+        assert(g[].equal("A\u0301B"));
+    
+}
+
+@safe unittest
+{
+    import std.uni;
+
+    import std.algorithm.comparison : equal;
+    import std.algorithm.iteration : filter;
+    import std.range : isRandomAccessRange;
+
+    string bold = "ku\u0308hn";
+
+    // note that decodeGrapheme takes parameter by ref
+    auto first = decodeGrapheme(bold);
+
+    assert(first.length == 1);
+    assert(first[0] == 'k');
+
+    // the next grapheme is 2 characters long
+    auto wideOne = decodeGrapheme(bold);
+    // slicing a grapheme yields a random-access range of dchar
+    assert(wideOne[].equal("u\u0308"));
+    assert(wideOne.length == 2);
+    static assert(isRandomAccessRange!(typeof(wideOne[])));
+
+    // all of the usual range manipulation is possible
+    assert(wideOne[].filter!isMark().equal("\u0308"));
+
+    auto g = Grapheme("A");
+    assert(g.valid);
+    g ~= '\u0301';
+    assert(g[].equal("A\u0301"));
+    assert(g.valid);
+    g ~= "B";
+    // not a valid grapheme cluster anymore
+    assert(!g.valid);
+    // still could be useful though
+    assert(g[].equal("A\u0301B"));
+}
+
+@safe @nogc pure nothrow unittest
+{
+    import std.uni;
+
+    assert(sicmp("Август", "авгусТ") == 0);
+    // Greek also works as long as there is no 1:M mapping in sight
+    assert(sicmp("ΌΎ", "όύ") == 0);
+    // things like the following won't get matched as equal
+    // Greek small letter iota with dialytika and tonos
+    assert(sicmp("ΐ", "\u03B9\u0308\u0301") != 0);
+
+    // while icmp has no problem with that
+    assert(icmp("ΐ", "\u03B9\u0308\u0301") == 0);
+    assert(icmp("ΌΎ", "όύ") == 0);
+}
+
+@safe @nogc pure nothrow unittest
+{
+    import std.uni;
+
+    assert(icmp("Rußland", "Russland") == 0);
+    assert(icmp("ᾩ -> \u1F70\u03B9", "\u1F61\u03B9 -> ᾲ") == 0);
+}
+
+@safe @nogc nothrow pure unittest
+{
+    import std.uni;
+
+    import std.utf : byDchar;
+
+    assert(icmp("Rußland".byDchar, "Russland".byDchar) == 0);
+    assert(icmp("ᾩ -> \u1F70\u03B9".byDchar, "\u1F61\u03B9 -> ᾲ".byDchar) == 0);
+}
+
+@safe unittest
+{
+    import std.uni;
+
+    // shorten the code
+    alias CC = combiningClass;
+
+    // combining tilda
+    assert(CC('\u0303') == 230);
+    // combining ring below
+    assert(CC('\u0325') == 220);
+    // the simple consequence is that  "tilda" should be
+    // placed after a "ring below" in a sequence
+}
+
+@safe unittest
+{
+    import std.uni;
+
+    assert(compose('A','\u0308') == '\u00C4');
+    assert(compose('A', 'B') == dchar.init);
+    assert(compose('C', '\u0301') == '\u0106');
+    // note that the starter is the first one
+    // thus the following doesn't compose
+    assert(compose('\u0308', 'A') == dchar.init);
+}
+
+@safe unittest
+{
+    import std.uni;
+
+    import std.algorithm.comparison : equal;
+
+    assert(compose('A','\u0308') == '\u00C4');
+    assert(compose('A', 'B') == dchar.init);
+    assert(compose('C', '\u0301') == '\u0106');
+    // note that the starter is the first one
+    // thus the following doesn't compose
+    assert(compose('\u0308', 'A') == dchar.init);
+
+    assert(decompose('Ĉ')[].equal("C\u0302"));
+    assert(decompose('D')[].equal("D"));
+    assert(decompose('\uD4DC')[].equal("\u1111\u1171\u11B7"));
+    assert(decompose!Compatibility('¹')[].equal("1"));
+}
+
+@safe unittest
+{
+    import std.uni;
+
+    import std.algorithm.comparison : equal;
+    assert(decomposeHangul('\uD4DB')[].equal("\u1111\u1171\u11B6"));
+}
+
+@safe unittest
+{
+    import std.uni;
+
+    assert(composeJamo('\u1111', '\u1171', '\u11B6') == '\uD4DB');
+    // leaving out T-vowel, or passing any codepoint
+    // that is not trailing consonant composes an LV-syllable
+    assert(composeJamo('\u1111', '\u1171') == '\uD4CC');
+    assert(composeJamo('\u1111', '\u1171', ' ') == '\uD4CC');
+    assert(composeJamo('\u1111', 'A') == dchar.init);
+    assert(composeJamo('A', '\u1171') == dchar.init);
+}
+
+@safe pure unittest
+{
+    import std.uni;
+
+    // any encoding works
+    wstring greet = "Hello world";
+    assert(normalize(greet) is greet); // the same exact slice
+
+    // An example of a character with all 4 forms being different:
+    // Greek upsilon with acute and hook symbol (code point 0x03D3)
+    assert(normalize!NFC("Ï“") == "\u03D3");
+    assert(normalize!NFD("Ï“") == "\u03D2\u0301");
+    assert(normalize!NFKC("Ï“") == "\u038E");
+    assert(normalize!NFKD("Ï“") == "\u03A5\u0301");
+}
+
+@safe unittest
+{
+    import std.uni;
+
+    // e.g. Cyrillic is always allowed, so is ASCII
+    assert(allowedIn!NFC('я'));
+    assert(allowedIn!NFD('я'));
+    assert(allowedIn!NFKC('я'));
+    assert(allowedIn!NFKD('я'));
+    assert(allowedIn!NFC('Z'));
+}
+
+@safe pure unittest
+{
+    import std.uni;
+
+    import std.algorithm.comparison : equal;
+
+    assert("hEllo".asUpperCase.equal("HELLO"));
+}
+
+@safe pure unittest
+{
+    import std.uni;
+
+    import std.algorithm.comparison : equal;
+
+    assert("hEllo".asCapitalized.equal("Hello"));
+}
+
+@safe unittest
+{
+    import std.uni;
+
+    import std.algorithm.iteration : map;
+    import std.algorithm.mutation : copy;
+    import std.array : appender;
+
+    auto abuf = appender!(char[])();
+    "hello".map!toUpper.copy(abuf);
+    assert(abuf.data == "HELLO");
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_uri.d b/libphobos/testsuite/libphobos.phobos/std_uri.d
new file mode 100644
index 0000000000000000000000000000000000000000..5d57062c42ef24bce7cab3de592369286b693988
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_uri.d
@@ -0,0 +1,71 @@
+@safe unittest
+{
+    import std.uri;
+
+    import std.exception : assertThrown;
+    assertThrown!URIException("%ab".decode);
+}
+
+@safe unittest
+{
+    import std.uri;
+
+    assert("foo%20bar".decode == "foo bar");
+    assert("%3C%3E.@.%E2%84%A2".decode == "<>.@.â„¢");
+    assert("foo&/".decode == "foo&/");
+    assert("!@#$&*(".decode == "!@#$&*(");
+}
+
+@safe unittest
+{
+    import std.uri;
+
+    assert("foo%2F%26".decodeComponent == "foo/&");
+    assert("dl%C3%A4ng%20r%C3%B6cks".decodeComponent == "dläng röcks");
+    assert("!%40%23%24%25%5E%26*(".decodeComponent == "!@#$%^&*(");
+}
+
+@safe unittest
+{
+    import std.uri;
+
+    assert("foo bar".encode == "foo%20bar");
+    assert("<>.@.â„¢".encode == "%3C%3E.@.%E2%84%A2");
+    assert("foo/#?a=1&b=2".encode == "foo/#?a=1&b=2");
+    assert("dlang+rocks!".encode == "dlang+rocks!");
+    assert("!@#$%^&*(".encode == "!@#$%25%5E&*(");
+}
+
+@safe unittest
+{
+    import std.uri;
+
+    assert("!@#$%^&*(".encodeComponent == "!%40%23%24%25%5E%26*(");
+    assert("<>.@.â„¢".encodeComponent == "%3C%3E.%40.%E2%84%A2");
+    assert("foo/&".encodeComponent == "foo%2F%26");
+    assert("dläng röcks".encodeComponent == "dl%C3%A4ng%20r%C3%B6cks");
+    assert("dlang+rocks!".encodeComponent == "dlang%2Brocks!");
+}
+
+@safe pure unittest
+{
+    import std.uri;
+
+    string s1 = "http://www.digitalmars.com/~fred/fredsRX.html#foo end!";
+    assert(uriLength(s1) == 49);
+    string s2 = "no uri here";
+    assert(uriLength(s2) == -1);
+    assert(uriLength("issue 14924") < 0);
+}
+
+@safe pure unittest
+{
+    import std.uri;
+
+    string s1 = "my.e-mail@www.example-domain.com with garbage added";
+    assert(emailLength(s1) == 32);
+    string s2 = "no email address here";
+    assert(emailLength(s2) == -1);
+    assert(emailLength("issue 14924") < 0);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_utf.d b/libphobos/testsuite/libphobos.phobos/std_utf.d
new file mode 100644
index 0000000000000000000000000000000000000000..e758d488ae018e93735b308d119934470fff6129
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_utf.d
@@ -0,0 +1,474 @@
+@safe unittest
+{
+    import std.utf;
+
+    import std.exception : assertThrown;
+
+    char[4] buf;
+    assertThrown!UTFException(encode(buf, cast(dchar) 0xD800));
+    assertThrown!UTFException(encode(buf, cast(dchar) 0xDBFF));
+    assertThrown!UTFException(encode(buf, cast(dchar) 0xDC00));
+    assertThrown!UTFException(encode(buf, cast(dchar) 0xDFFF));
+    assertThrown!UTFException(encode(buf, cast(dchar) 0x110000));
+}
+
+@safe @nogc pure nothrow unittest
+{
+    import std.utf;
+
+    assert( isValidDchar(cast(dchar) 0x41));
+    assert( isValidDchar(cast(dchar) 0x00));
+    assert(!isValidDchar(cast(dchar) 0xD800));
+    assert(!isValidDchar(cast(dchar) 0x11FFFF));
+}
+
+@safe pure nothrow unittest
+{
+    import std.utf;
+
+    assert( isValidCodepoint(cast(char) 0x40));
+    assert(!isValidCodepoint(cast(char) 0x80));
+    assert( isValidCodepoint(cast(wchar) 0x1234));
+    assert(!isValidCodepoint(cast(wchar) 0xD800));
+    assert( isValidCodepoint(cast(dchar) 0x0010FFFF));
+    assert(!isValidCodepoint(cast(dchar) 0x12345678));
+}
+
+@safe unittest
+{
+    import std.utf;
+
+    assert("a".stride == 1);
+    assert("λ".stride == 2);
+    assert("aλ".stride == 1);
+    assert("aλ".stride(1) == 2);
+    assert("𐐷".stride == 4);
+}
+
+@safe unittest
+{
+    import std.utf;
+
+    assert("a".strideBack == 1);
+    assert("λ".strideBack == 2);
+    assert("aλ".strideBack == 2);
+    assert("aλ".strideBack(1) == 1);
+    assert("𐐷".strideBack == 4);
+}
+
+@safe unittest
+{
+    import std.utf;
+
+    assert(toUCSindex(`hello world`, 7) == 7);
+    assert(toUCSindex(`hello world`w, 7) == 7);
+    assert(toUCSindex(`hello world`d, 7) == 7);
+
+    assert(toUCSindex(`Ma Chérie`, 7) == 6);
+    assert(toUCSindex(`Ma Chérie`w, 7) == 7);
+    assert(toUCSindex(`Ma Chérie`d, 7) == 7);
+
+    assert(toUCSindex(`さいごの果実 / ミツバチと科学者`, 9) == 3);
+    assert(toUCSindex(`さいごの果実 / ミツバチと科学者`w, 9) == 9);
+    assert(toUCSindex(`さいごの果実 / ミツバチと科学者`d, 9) == 9);
+}
+
+@safe unittest
+{
+    import std.utf;
+
+    assert(toUTFindex(`hello world`, 7) == 7);
+    assert(toUTFindex(`hello world`w, 7) == 7);
+    assert(toUTFindex(`hello world`d, 7) == 7);
+
+    assert(toUTFindex(`Ma Chérie`, 6) == 7);
+    assert(toUTFindex(`Ma Chérie`w, 7) == 7);
+    assert(toUTFindex(`Ma Chérie`d, 7) == 7);
+
+    assert(toUTFindex(`さいごの果実 / ミツバチと科学者`, 3) == 9);
+    assert(toUTFindex(`さいごの果実 / ミツバチと科学者`w, 9) == 9);
+    assert(toUTFindex(`さいごの果実 / ミツバチと科学者`d, 9) == 9);
+}
+
+@safe pure unittest
+{
+    import std.utf;
+
+    size_t i;
+
+    assert("a".decode(i) == 'a' && i == 1);
+    i = 0;
+    assert("Ã¥".decode(i) == 'Ã¥' && i == 2);
+    i = 1;
+    assert("aå".decode(i) == 'å' && i == 3);
+    i = 0;
+    assert("Ã¥"w.decode(i) == 'Ã¥' && i == 1);
+
+    // ë as a multi-code point grapheme
+    i = 0;
+    assert("e\u0308".decode(i) == 'e' && i == 1);
+    // ë as a single code point grapheme
+    i = 0;
+    assert("ë".decode(i) == 'ë' && i == 2);
+    i = 0;
+    assert("ë"w.decode(i) == 'ë' && i == 1);
+}
+
+@safe pure unittest
+{
+    import std.utf;
+
+    import std.range.primitives;
+    string str = "Hello, World!";
+
+    assert(str.decodeFront == 'H' && str == "ello, World!");
+    str = "Ã¥";
+    assert(str.decodeFront == 'Ã¥' && str.empty);
+    str = "Ã¥";
+    size_t i;
+    assert(str.decodeFront(i) == 'Ã¥' && i == 2 && str.empty);
+}
+
+@system pure unittest
+{
+    import std.utf;
+
+    import std.range.primitives;
+    string str = "Hello, World!";
+
+    assert(str.decodeBack == '!' && str == "Hello, World");
+    str = "Ã¥";
+    assert(str.decodeBack == 'Ã¥' && str.empty);
+    str = "Ã¥";
+    size_t i;
+    assert(str.decodeBack(i) == 'Ã¥' && i == 2 && str.empty);
+}
+
+@safe unittest
+{
+    import std.utf;
+
+    import std.exception : assertThrown;
+    import std.typecons : Yes;
+
+    char[4] buf;
+
+    assert(encode(buf, '\u0000') == 1 && buf[0 .. 1] == "\u0000");
+    assert(encode(buf, '\u007F') == 1 && buf[0 .. 1] == "\u007F");
+    assert(encode(buf, '\u0080') == 2 && buf[0 .. 2] == "\u0080");
+    assert(encode(buf, '\uE000') == 3 && buf[0 .. 3] == "\uE000");
+    assert(encode(buf, 0xFFFE) == 3 && buf[0 .. 3] == "\xEF\xBF\xBE");
+    assertThrown!UTFException(encode(buf, cast(dchar) 0x110000));
+
+    encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000);
+    auto slice = buf[];
+    assert(slice.decodeFront == replacementDchar);
+}
+
+@safe unittest
+{
+    import std.utf;
+
+    import std.exception : assertThrown;
+    import std.typecons : Yes;
+
+    wchar[2] buf;
+
+    assert(encode(buf, '\u0000') == 1 && buf[0 .. 1] == "\u0000");
+    assert(encode(buf, '\uD7FF') == 1 && buf[0 .. 1] == "\uD7FF");
+    assert(encode(buf, '\uE000') == 1 && buf[0 .. 1] == "\uE000");
+    assert(encode(buf, '\U00010000') == 2 && buf[0 .. 2] == "\U00010000");
+    assert(encode(buf, '\U0010FFFF') == 2 && buf[0 .. 2] == "\U0010FFFF");
+    assertThrown!UTFException(encode(buf, cast(dchar) 0xD800));
+
+    encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000);
+    auto slice = buf[];
+    assert(slice.decodeFront == replacementDchar);
+}
+
+@safe unittest
+{
+    import std.utf;
+
+    import std.exception : assertThrown;
+    import std.typecons : Yes;
+
+    dchar[1] buf;
+
+    assert(encode(buf, '\u0000') == 1 && buf[0] == '\u0000');
+    assert(encode(buf, '\uD7FF') == 1 && buf[0] == '\uD7FF');
+    assert(encode(buf, '\uE000') == 1 && buf[0] == '\uE000');
+    assert(encode(buf, '\U0010FFFF') == 1 && buf[0] == '\U0010FFFF');
+    assertThrown!UTFException(encode(buf, cast(dchar) 0xD800));
+
+    encode!(Yes.useReplacementDchar)(buf, cast(dchar) 0x110000);
+    assert(buf[0] == replacementDchar);
+}
+
+@safe unittest
+{
+    import std.utf;
+
+    char[] s = "abcd".dup;
+    dchar d1 = 'a';
+    dchar d2 = 'ø';
+
+    encode(s, d1);
+    assert(s.length == 5);
+    assert(s == "abcda");
+    encode(s, d2);
+    assert(s.length == 7);
+    assert(s == "abcdaø");
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.utf;
+
+    assert(codeLength!char('a') == 1);
+    assert(codeLength!wchar('a') == 1);
+    assert(codeLength!dchar('a') == 1);
+
+    assert(codeLength!char('\U0010FFFF') == 4);
+    assert(codeLength!wchar('\U0010FFFF') == 2);
+    assert(codeLength!dchar('\U0010FFFF') == 1);
+}
+
+@safe unittest
+{
+    import std.utf;
+
+    assert(codeLength!char("hello world") ==
+           "hello world".length);
+    assert(codeLength!wchar("hello world") ==
+           "hello world"w.length);
+    assert(codeLength!dchar("hello world") ==
+           "hello world"d.length);
+
+    assert(codeLength!char(`プログラミング`) ==
+           `プログラミング`.length);
+    assert(codeLength!wchar(`プログラミング`) ==
+           `プログラミング`w.length);
+    assert(codeLength!dchar(`プログラミング`) ==
+           `プログラミング`d.length);
+
+    string haystack = `Être sans la verité, ça, ce ne serait pas bien.`;
+    wstring needle = `Être sans la verité`;
+    assert(haystack[codeLength!char(needle) .. $] ==
+           `, ça, ce ne serait pas bien.`);
+}
+
+@safe unittest
+{
+    import std.utf;
+
+    import std.exception : assertThrown;
+    char[] a = [167, 133, 175];
+    assertThrown!UTFException(validate(a));
+}
+
+@safe pure unittest
+{
+    import std.utf;
+
+    import std.algorithm.comparison : equal;
+
+    // The ö is represented by two UTF-8 code units
+    assert("Hellø"w.toUTF8.equal(['H', 'e', 'l', 'l', 0xC3, 0xB8]));
+
+    // 𐐷 is four code units in UTF-8
+    assert("𐐷"d.toUTF8.equal([0xF0, 0x90, 0x90, 0xB7]));
+}
+
+@safe pure unittest
+{
+    import std.utf;
+
+    import std.algorithm.comparison : equal;
+
+    // these graphemes are two code units in UTF-16 and one in UTF-32
+    assert("𤭢"d.length == 1);
+    assert("𐐷"d.length == 1);
+
+    assert("𤭢"d.toUTF16.equal([0xD852, 0xDF62]));
+    assert("𐐷"d.toUTF16.equal([0xD801, 0xDC37]));
+}
+
+@safe pure unittest
+{
+    import std.utf;
+
+    import std.algorithm.comparison : equal;
+
+    // these graphemes are two code units in UTF-16 and one in UTF-32
+    assert("𤭢"w.length == 2);
+    assert("𐐷"w.length == 2);
+
+    assert("𤭢"w.toUTF32.equal([0x00024B62]));
+    assert("𐐷"w.toUTF32.equal([0x00010437]));
+}
+
+@safe pure unittest
+{
+    import std.utf;
+
+    auto p1 = toUTFz!(char*)("hello world");
+    auto p2 = toUTFz!(const(char)*)("hello world");
+    auto p3 = toUTFz!(immutable(char)*)("hello world");
+    auto p4 = toUTFz!(char*)("hello world"d);
+    auto p5 = toUTFz!(const(wchar)*)("hello world");
+    auto p6 = toUTFz!(immutable(dchar)*)("hello world"w);
+}
+
+@system unittest
+{
+    import std.utf;
+
+    string str = "Hello, World!";
+    const(wchar)* p = str.toUTF16z;
+    assert(p[str.length] == '\0');
+}
+
+@safe pure nothrow @nogc unittest
+{
+    import std.utf;
+
+    assert(count("") == 0);
+    assert(count("a") == 1);
+    assert(count("abc") == 3);
+    assert(count("\u20AC100") == 4);
+}
+
+@safe unittest
+{
+    import std.utf;
+
+    import std.range.primitives;
+    import std.traits : isAutodecodableString;
+
+    auto r = "Hello, World!".byCodeUnit();
+    static assert(hasLength!(typeof(r)));
+    static assert(hasSlicing!(typeof(r)));
+    static assert(isRandomAccessRange!(typeof(r)));
+    static assert(is(ElementType!(typeof(r)) == immutable char));
+
+    // contrast with the range capabilities of standard strings (with or
+    // without autodecoding enabled).
+    auto s = "Hello, World!";
+    static assert(isBidirectionalRange!(typeof(r)));
+    static if (isAutodecodableString!(typeof(s)))
+    {
+        // with autodecoding enabled, strings are non-random-access ranges of
+        // dchar.
+        static assert(is(ElementType!(typeof(s)) == dchar));
+        static assert(!isRandomAccessRange!(typeof(s)));
+        static assert(!hasSlicing!(typeof(s)));
+        static assert(!hasLength!(typeof(s)));
+    }
+    else
+    {
+        // without autodecoding, strings are normal arrays.
+        static assert(is(ElementType!(typeof(s)) == immutable char));
+        static assert(isRandomAccessRange!(typeof(s)));
+        static assert(hasSlicing!(typeof(s)));
+        static assert(hasLength!(typeof(s)));
+    }
+}
+
+@safe unittest
+{
+    import std.utf;
+
+    string noel1 = "noe\u0308l"; // noël using e + combining diaeresis
+    assert(noel1.byCodeUnit[2] != 'ë');
+    assert(noel1.byCodeUnit[2] == 'e');
+
+    string noel2 = "no\u00EBl"; // noël using a precomposed ë character
+    // Because string is UTF-8, the code unit at index 2 is just
+    // the first of a sequence that encodes 'ë'
+    assert(noel2.byCodeUnit[2] != 'ë');
+}
+
+@safe unittest
+{
+    import std.utf;
+
+    import std.algorithm.comparison : equal;
+    import std.range : popFrontN;
+    import std.traits : isAutodecodableString;
+    {
+        auto range = byCodeUnit("hello world");
+        range.popFrontN(3);
+        assert(equal(range.save, "lo world"));
+        static if (isAutodecodableString!string) // only enabled with autodecoding
+        {
+            string str = range.source;
+            assert(str == "lo world");
+        }
+    }
+    // source only exists if the range was wrapped
+    {
+        auto range = byCodeUnit("hello world"d);
+        static assert(!__traits(compiles, range.source));
+    }
+}
+
+@safe pure nothrow unittest
+{
+    import std.utf;
+
+    import std.algorithm.comparison : equal;
+
+    // hellö as a range of `char`s, which are UTF-8
+    assert("hell\u00F6".byUTF!char().equal(['h', 'e', 'l', 'l', 0xC3, 0xB6]));
+
+    // `wchar`s are able to hold the ö in a single element (UTF-16 code unit)
+    assert("hell\u00F6".byUTF!wchar().equal(['h', 'e', 'l', 'l', 'ö']));
+
+    // 𐐷 is four code units in UTF-8, two in UTF-16, and one in UTF-32
+    assert("𐐷".byUTF!char().equal([0xF0, 0x90, 0x90, 0xB7]));
+    assert("𐐷".byUTF!wchar().equal([0xD801, 0xDC37]));
+    assert("𐐷".byUTF!dchar().equal([0x00010437]));
+}
+
+@safe unittest
+{
+    import std.utf;
+
+    import std.algorithm.comparison : equal;
+    import std.exception : assertThrown;
+
+    assert("hello\xF0betty".byChar.byUTF!(dchar, UseReplacementDchar.yes).equal("hello\uFFFDetty"));
+    assertThrown!UTFException("hello\xF0betty".byChar.byUTF!(dchar, UseReplacementDchar.no).equal("hello betty"));
+}
+
+@safe pure nothrow unittest
+{
+    import std.utf;
+
+    import std.range.primitives;
+    wchar[] s = ['ă', 'î'];
+
+    auto rc = s.byUTF!char;
+    static assert(isBidirectionalRange!(typeof(rc)));
+    assert(rc.back == 0xae);
+    rc.popBack;
+    assert(rc.back == 0xc3);
+    rc.popBack;
+    assert(rc.back == 0x83);
+    rc.popBack;
+    assert(rc.back == 0xc4);
+
+    auto rw = s.byUTF!wchar;
+    static assert(isBidirectionalRange!(typeof(rw)));
+    assert(rw.back == 'î');
+    rw.popBack;
+    assert(rw.back == 'ă');
+
+    auto rd = s.byUTF!dchar;
+    static assert(isBidirectionalRange!(typeof(rd)));
+    assert(rd.back == 'î');
+    rd.popBack;
+    assert(rd.back == 'ă');
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_uuid.d b/libphobos/testsuite/libphobos.phobos/std_uuid.d
new file mode 100644
index 0000000000000000000000000000000000000000..2486083cd26cb546f271b2ed87d6ca103181e75b
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_uuid.d
@@ -0,0 +1,252 @@
+@safe unittest
+{
+    import std.uuid;
+
+    import std.uuid;
+
+    UUID[] ids;
+    ids ~= randomUUID();
+    ids ~= md5UUID("test.name.123");
+    ids ~= sha1UUID("test.name.123");
+
+    foreach (entry; ids)
+    {
+        assert(entry.variant == UUID.Variant.rfc4122);
+    }
+    assert(ids[0].uuidVersion == UUID.Version.randomNumberBased);
+    assert(ids[1].toString() == "22390768-cced-325f-8f0f-cfeaa19d0ccd");
+    assert(ids[1].data == [34, 57, 7, 104, 204, 237, 50, 95, 143, 15, 207,
+        234, 161, 157, 12, 205]);
+    UUID id;
+    assert(id.empty);
+}
+
+@safe pure unittest
+{
+    import std.uuid;
+
+            enum ubyte[16] data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
+            auto uuid = UUID(data);
+            enum ctfe = UUID(data);
+            assert(uuid.data == data);
+            assert(ctfe.data == data);
+        
+}
+
+@safe unittest
+{
+    import std.uuid;
+
+            auto tmp = UUID(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15);
+            assert(tmp.data == cast(ubyte[16])[0,1,2,3,4,5,6,7,8,9,10,11,
+                12,13,14,15]);
+        
+}
+
+@safe pure unittest
+{
+    import std.uuid;
+
+            auto id = UUID("8AB3060E-2cba-4f23-b74c-b52db3bdfb46");
+            assert(id.data == [138, 179, 6, 14, 44, 186, 79, 35, 183, 76,
+               181, 45, 179, 189, 251, 70]);
+            assert(id.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
+
+            //Can also be used in CTFE, for example as UUID literals:
+            enum ctfeID = UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
+            //here parsing is done at compile time, no runtime overhead!
+        
+}
+
+@safe pure unittest
+{
+    import std.uuid;
+
+            UUID id;
+            assert(id.empty);
+            id = UUID("00000000-0000-0000-0000-000000000001");
+            assert(!id.empty);
+        
+}
+
+@safe pure unittest
+{
+    import std.uuid;
+
+            assert(UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46").variant
+               == UUID.Variant.rfc4122);
+        
+}
+
+@safe unittest
+{
+    import std.uuid;
+
+            assert(UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46").uuidVersion
+                == UUID.Version.randomNumberBased);
+        
+}
+
+@safe unittest
+{
+    import std.uuid;
+
+            immutable ubyte[16] data = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
+            UUID u1;
+            UUID u2 = UUID(data);
+            u1.swap(u2);
+
+            assert(u1 == UUID(data));
+            assert(u2 == UUID.init);
+        
+}
+
+@safe pure unittest
+{
+    import std.uuid;
+
+            //compare UUIDs
+            assert(UUID("00000000-0000-0000-0000-000000000000") == UUID.init);
+
+            //UUIDs in associative arrays:
+            int[UUID] test = [UUID("8a94f585-d180-44f7-8929-6fca0189c7d0") : 1,
+                UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a") : 2,
+                UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1") : 3];
+
+            assert(test[UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")] == 3);
+
+            //UUIDS can be sorted:
+            import std.algorithm;
+            UUID[] ids = [UUID("8a94f585-d180-44f7-8929-6fca0189c7d0"),
+                          UUID("7c351fd4-b860-4ee3-bbdc-7f79f3dfb00a"),
+                          UUID("9ac0a4e5-10ee-493a-86fc-d29eeb82ecc1")];
+            sort(ids);
+        
+}
+
+@safe pure unittest
+{
+    import std.uuid;
+
+            immutable str = "8ab3060e-2cba-4f23-b74c-b52db3bdfb46";
+            auto id = UUID(str);
+            assert(id.toString() == str);
+        
+}
+
+@safe unittest
+{
+    import std.uuid;
+
+    UUID id;
+    assert(id.empty);
+
+    id = randomUUID;
+    assert(!id.empty);
+
+    id = UUID(cast(ubyte[16]) [138, 179, 6, 14, 44, 186, 79,
+        35, 183, 76, 181, 45, 179, 189, 251, 70]);
+    assert(id.toString() == "8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
+}
+
+@safe unittest
+{
+    import std.uuid;
+
+    //Use default UUID.init namespace
+    auto simpleID = md5UUID("test.uuid.any.string");
+
+    //use a name-based id as namespace
+    auto namespace = md5UUID("my.app");
+    auto id = md5UUID("some-description", namespace);
+}
+
+@safe unittest
+{
+    import std.uuid;
+
+    //Use default UUID.init namespace
+    auto simpleID = sha1UUID("test.uuid.any.string");
+
+    //use a name-based id as namespace
+    auto namespace = sha1UUID("my.app");
+    auto id = sha1UUID("some-description", namespace);
+}
+
+@safe unittest
+{
+    import std.uuid;
+
+    import std.random : Xorshift192, unpredictableSeed;
+
+    //simple call
+    auto uuid = randomUUID();
+
+    //provide a custom RNG. Must be seeded manually.
+    Xorshift192 gen;
+
+    gen.seed(unpredictableSeed);
+    auto uuid3 = randomUUID(gen);
+}
+
+@safe unittest
+{
+    import std.uuid;
+
+    auto id = parseUUID("8AB3060E-2CBA-4F23-b74c-B52Db3BDFB46");
+    //no dashes
+    id = parseUUID("8ab3060e2cba4f23b74cb52db3bdfb46");
+    //dashes at different positions
+    id = parseUUID("8a-b3-06-0e2cba4f23b74c-b52db3bdfb-46");
+    //leading / trailing characters
+    id = parseUUID("{8ab3060e-2cba-4f23-b74c-b52db3bdfb46}");
+    //unicode
+    id = parseUUID("ü8ab3060e2cba4f23b74cb52db3bdfb46ü");
+    //multiple trailing/leading characters
+    id = parseUUID("///8ab3060e2cba4f23b74cb52db3bdfb46||");
+
+    //Can also be used in CTFE, for example as UUID literals:
+    enum ctfeID = parseUUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46");
+    //here parsing is done at compile time, no runtime overhead!
+}
+
+@safe unittest
+{
+    import std.uuid;
+
+    import std.algorithm;
+    import std.regex;
+
+    string test = "Lorem ipsum dolor sit amet, consetetur "~
+    "6ba7b814-9dad-11d1-80b4-00c04fd430c8 sadipscing \n"~
+    "elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore \r\n"~
+    "magna aliquyam erat, sed diam voluptua. "~
+    "8ab3060e-2cba-4f23-b74c-b52db3bdfb46 At vero eos et accusam et "~
+    "justo duo dolores et ea rebum.";
+
+    auto r = regex(uuidRegex, "g");
+    UUID[] found;
+    foreach (c; match(test, r))
+    {
+        found ~= UUID(c.hit);
+    }
+    assert(found == [
+        UUID("6ba7b814-9dad-11d1-80b4-00c04fd430c8"),
+        UUID("8ab3060e-2cba-4f23-b74c-b52db3bdfb46"),
+    ]);
+}
+
+@safe unittest
+{
+    import std.uuid;
+
+    import std.exception : collectException;
+
+    const inputUUID = "this-is-an-invalid-uuid";
+    auto ex = collectException!UUIDParsingException(UUID(inputUUID));
+    assert(ex !is null); // check that exception was thrown
+    assert(ex.input == inputUUID);
+    assert(ex.position == 0);
+    assert(ex.reason == UUIDParsingException.Reason.tooLittle);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_variant.d b/libphobos/testsuite/libphobos.phobos/std_variant.d
new file mode 100644
index 0000000000000000000000000000000000000000..374dd7852aa71b762b0f2e7fbf6483732a9f7b19
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_variant.d
@@ -0,0 +1,291 @@
+@system unittest
+{
+    import std.variant;
+
+    Variant a; // Must assign before use, otherwise exception ensues
+    // Initialize with an integer; make the type int
+    Variant b = 42;
+    assert(b.type == typeid(int));
+    // Peek at the value
+    assert(b.peek!(int) !is null && *b.peek!(int) == 42);
+    // Automatically convert per language rules
+    auto x = b.get!(real);
+
+    // Assign any other type, including other variants
+    a = b;
+    a = 3.14;
+    assert(a.type == typeid(double));
+    // Implicit conversions work just as with built-in types
+    assert(a < b);
+    // Check for convertibility
+    assert(!a.convertsTo!(int)); // double not convertible to int
+    // Strings and all other arrays are supported
+    a = "now I'm a string";
+    assert(a == "now I'm a string");
+
+    // can also assign arrays
+    a = new int[42];
+    assert(a.length == 42);
+    a[5] = 7;
+    assert(a[5] == 7);
+
+    // Can also assign class values
+    class Foo {}
+    auto foo = new Foo;
+    a = foo;
+    assert(*a.peek!(Foo) == foo); // and full type information is preserved
+}
+
+@safe unittest
+{
+    import std.variant;
+
+    struct Cat { int a, b, c; }
+
+    align(1) struct S
+    {
+        long l;
+        ubyte b;
+    }
+
+    align(1) struct T
+    {
+        ubyte b;
+        long l;
+    }
+
+    static assert(maxSize!(int, long) == 8);
+    static assert(maxSize!(bool, byte) == 1);
+    static assert(maxSize!(bool, Cat) == 12);
+    static assert(maxSize!(char) == 1);
+    static assert(maxSize!(char, short, ubyte) == 2);
+    static assert(maxSize!(char, long, ubyte) == 8);
+    import std.algorithm.comparison : max;
+    static assert(maxSize!(long, S) == max(long.sizeof, S.sizeof));
+    static assert(maxSize!(S, T) == max(S.sizeof, T.sizeof));
+    static assert(maxSize!(int, ubyte[7]) == 7);
+    static assert(maxSize!(int, ubyte[3]) == 4);
+    static assert(maxSize!(int, int, ubyte[3]) == 4);
+    static assert(maxSize!(void, int, ubyte[3]) == 4);
+    static assert(maxSize!(void) == 1);
+}
+
+@system unittest
+{
+    import std.variant;
+
+    alias Var = VariantN!(maxSize!(int, double, string));
+
+    Var a; // Must assign before use, otherwise exception ensues
+    // Initialize with an integer; make the type int
+    Var b = 42;
+    assert(b.type == typeid(int));
+    // Peek at the value
+    assert(b.peek!(int) !is null && *b.peek!(int) == 42);
+    // Automatically convert per language rules
+    auto x = b.get!(real);
+
+    // Assign any other type, including other variants
+    a = b;
+    a = 3.14;
+    assert(a.type == typeid(double));
+    // Implicit conversions work just as with built-in types
+    assert(a < b);
+    // Check for convertibility
+    assert(!a.convertsTo!(int)); // double not convertible to int
+    // Strings and all other arrays are supported
+    a = "now I'm a string";
+    assert(a == "now I'm a string");
+}
+
+@system unittest
+{
+    import std.variant;
+
+    alias Var = VariantN!(maxSize!(int[]));
+
+    Var a = new int[42];
+    assert(a.length == 42);
+    a[5] = 7;
+    assert(a[5] == 7);
+}
+
+@system unittest
+{
+    import std.variant;
+
+    alias Var = VariantN!(maxSize!(int*)); // classes are pointers
+    Var a;
+
+    class Foo {}
+    auto foo = new Foo;
+    a = foo;
+    assert(*a.peek!(Foo) == foo); // and full type information is preserved
+}
+
+@system unittest
+{
+    import std.variant;
+
+    auto v = Algebraic!(int, double, string)(5);
+    assert(v.peek!(int));
+    v = 3.14;
+    assert(v.peek!(double));
+    // auto x = v.peek!(long); // won't compile, type long not allowed
+    // v = '1'; // won't compile, type char not allowed
+}
+
+@system unittest
+{
+    import std.variant;
+
+    import std.typecons : Tuple, tuple;
+
+    // A tree is either a leaf or a branch of two other trees
+    alias Tree(Leaf) = Algebraic!(Leaf, Tuple!(This*, This*));
+    Tree!int tree = tuple(new Tree!int(42), new Tree!int(43));
+    Tree!int* right = tree.get!1[1];
+    assert(*right == 43);
+
+    // An object is a double, a string, or a hash of objects
+    alias Obj = Algebraic!(double, string, This[string]);
+    Obj obj = "hello";
+    assert(obj.get!1 == "hello");
+    obj = 42.0;
+    assert(obj.get!0 == 42);
+    obj = ["customer": Obj("John"), "paid": Obj(23.95)];
+    assert(obj.get!2["customer"] == "John");
+}
+
+@system unittest
+{
+    import std.variant;
+
+    Variant a; // Must assign before use, otherwise exception ensues
+    // Initialize with an integer; make the type int
+    Variant b = 42;
+    assert(b.type == typeid(int));
+    // Peek at the value
+    assert(b.peek!(int) !is null && *b.peek!(int) == 42);
+    // Automatically convert per language rules
+    auto x = b.get!(real);
+
+    // Assign any other type, including other variants
+    a = b;
+    a = 3.14;
+    assert(a.type == typeid(double));
+    // Implicit conversions work just as with built-in types
+    assert(a < b);
+    // Check for convertibility
+    assert(!a.convertsTo!(int)); // double not convertible to int
+    // Strings and all other arrays are supported
+    a = "now I'm a string";
+    assert(a == "now I'm a string");
+}
+
+@system unittest
+{
+    import std.variant;
+
+    Variant a = new int[42];
+    assert(a.length == 42);
+    a[5] = 7;
+    assert(a[5] == 7);
+}
+
+@system unittest
+{
+    import std.variant;
+
+    Variant a;
+
+    class Foo {}
+    auto foo = new Foo;
+    a = foo;
+    assert(*a.peek!(Foo) == foo); // and full type information is preserved
+}
+
+@system unittest
+{
+    import std.variant;
+
+    auto a = variantArray(1, 3.14, "Hi!");
+    assert(a[1] == 3.14);
+    auto b = Variant(a); // variant array as variant
+    assert(b[1] == 3.14);
+}
+
+@system unittest
+{
+    import std.variant;
+
+    import std.exception : assertThrown;
+
+    Variant v;
+
+    // uninitialized use
+    assertThrown!VariantException(v + 1);
+    assertThrown!VariantException(v.length);
+
+    // .get with an incompatible target type
+    assertThrown!VariantException(Variant("a").get!int);
+
+    // comparison between incompatible types
+    assertThrown!VariantException(Variant(3) < Variant("a"));
+}
+
+@system unittest
+{
+    import std.variant;
+
+    Algebraic!(int, string) variant;
+
+    variant = 10;
+    assert(variant.visit!((string s) => cast(int) s.length,
+                          (int i)    => i)()
+                          == 10);
+    variant = "string";
+    assert(variant.visit!((int i) => i,
+                          (string s) => cast(int) s.length)()
+                          == 6);
+
+    // Error function usage
+    Algebraic!(int, string) emptyVar;
+    auto rslt = emptyVar.visit!((string s) => cast(int) s.length,
+                          (int i)    => i,
+                          () => -1)();
+    assert(rslt == -1);
+
+    // Generic function usage
+    Algebraic!(int, float, real) number = 2;
+    assert(number.visit!(x => x += 1) == 3);
+
+    // Generic function for int/float with separate behavior for string
+    Algebraic!(int, float, string) something = 2;
+    assert(something.visit!((string s) => s.length, x => x) == 2); // generic
+    something = "asdf";
+    assert(something.visit!((string s) => s.length, x => x) == 4); // string
+
+    // Generic handler and empty handler
+    Algebraic!(int, float, real) empty2;
+    assert(empty2.visit!(x => x + 1, () => -1) == -1);
+}
+
+@system unittest
+{
+    import std.variant;
+
+    Algebraic!(int, string) variant;
+
+    variant = 10;
+    auto which = -1;
+    variant.tryVisit!((int i) { which = 0; })();
+    assert(which == 0);
+
+    // Error function usage
+    variant = "test";
+    variant.tryVisit!((int i) { which = 0; },
+                      ()      { which = -100; })();
+    assert(which == -100);
+}
+
diff --git a/libphobos/testsuite/libphobos.phobos/std_zlib.d b/libphobos/testsuite/libphobos.phobos/std_zlib.d
new file mode 100644
index 0000000000000000000000000000000000000000..3cc9462b68980dbdff4b0b6aad02227683fdc135
--- /dev/null
+++ b/libphobos/testsuite/libphobos.phobos/std_zlib.d
@@ -0,0 +1,29 @@
+@system unittest
+{
+    import std.zlib;
+
+    static ubyte[] data = [1,2,3,4,5,6,7,8,9,10];
+
+    uint adler = adler32(0u, data);
+    assert(adler == 0xdc0037);
+}
+
+@system unittest
+{
+    import std.zlib;
+
+        // some random data
+        ubyte[1024] originalData = void;
+
+        // append garbage data (or don't, this works in both cases)
+        auto compressedData = cast(ubyte[]) compress(originalData) ~ cast(ubyte[]) "whatever";
+
+        auto decompressor = new UnCompress();
+        auto uncompressedData = decompressor.uncompress(compressedData);
+
+        assert(uncompressedData[] == originalData[],
+                "The uncompressed and the original data differ");
+        assert(decompressor.empty, "The UnCompressor reports not being done");
+    
+}
+