--- /dev/null
+# 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
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"]]]
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"]]]
--- /dev/null
+@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")));
+}
+
--- /dev/null
+@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]]));
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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]));
+}
+
--- /dev/null
+@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]));
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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]);
+}
+
--- /dev/null
+@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('4')); // 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('á') == 'á');
+}
+
--- /dev/null
+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"));
+}
+
--- /dev/null
+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);
+}
+
--- /dev/null
+@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]));
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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));
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+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);
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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]));
+}
+
--- /dev/null
+@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));
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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]));
+}
+
--- /dev/null
+@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"));
+}
+
--- /dev/null
+@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");
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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");
+}
+
--- /dev/null
+@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
+}
+
--- /dev/null
+@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));
+}
+
--- /dev/null
+@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");
+ }
+
+}
+
--- /dev/null
+@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);
+ }
+}
+
--- /dev/null
+@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)
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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");
+}
+
--- /dev/null
+@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]);
+}
+
--- /dev/null
+@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());
+}
+
--- /dev/null
+@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");
+}
+
--- /dev/null
+@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");
+}
+
--- /dev/null
+@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));
+ }
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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();
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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();
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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;
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+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);
+ //...
+}
+
--- /dev/null
+@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);
+ //...
+}
+
--- /dev/null
+@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));
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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));
+}
+
--- /dev/null
+@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"));
+}
+
--- /dev/null
+@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"));
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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");
+}
+
--- /dev/null
+@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");
+}
+
--- /dev/null
+@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"]));
+}
+
--- /dev/null
+@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);
+ }
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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.
+}
+
--- /dev/null
+@system unittest
+{
+ import std.internal.scopebuffer;
+
+ ubyte[10] tmpbuf = void;
+ auto sb = scopeBuffer(tmpbuf);
+ scope(exit) sb.free();
+}
+
--- /dev/null
+@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}`)));
+
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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");
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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));
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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;");
+}
+
--- /dev/null
+@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");
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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));
+}
+
--- /dev/null
+@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");
+ }
+}
+
--- /dev/null
+@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]));
+}
+
--- /dev/null
+@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)));
+}
+
--- /dev/null
+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
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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\*`));
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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.");
+ }
+}
+
--- /dev/null
+@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");
+}
+
--- /dev/null
+@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));
+}
+
--- /dev/null
+@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[]));
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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)));
+}
+
--- /dev/null
+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");
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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 == 'ă');
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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);
+}
+
--- /dev/null
+@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");
+
+}
+