aboutsummaryrefslogtreecommitdiff
path: root/libphobos/src/std/container
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gcc.gnu.org>2018-10-28 19:51:47 +0000
committerIain Buclaw <ibuclaw@gcc.gnu.org>2018-10-28 19:51:47 +0000
commitb4c522fabd0df7be08882d2207df8b2765026110 (patch)
treeb5ffc312b0a441c1ba24323152aec463fdbe5e9f /libphobos/src/std/container
parent01ce9e31a02c8039d88e90f983735104417bf034 (diff)
downloadgcc-b4c522fabd0df7be08882d2207df8b2765026110.zip
gcc-b4c522fabd0df7be08882d2207df8b2765026110.tar.gz
gcc-b4c522fabd0df7be08882d2207df8b2765026110.tar.bz2
Add D front-end, libphobos library, and D2 testsuite.
ChangeLog: * Makefile.def (target_modules): Add libphobos. (flags_to_pass): Add GDC, GDCFLAGS, GDC_FOR_TARGET and GDCFLAGS_FOR_TARGET. (dependencies): Make libphobos depend on libatomic, libbacktrace configure, and zlib configure. (language): Add language d. * Makefile.in: Rebuild. * Makefile.tpl (BUILD_EXPORTS): Add GDC and GDCFLAGS. (HOST_EXPORTS): Add GDC. (POSTSTAGE1_HOST_EXPORTS): Add GDC and GDC_FOR_BUILD. (BASE_TARGET_EXPORTS): Add GDC. (GDC_FOR_BUILD, GDC, GDCFLAGS): New variables. (GDC_FOR_TARGET, GDC_FLAGS_FOR_TARGET): New variables. (EXTRA_HOST_FLAGS): Add GDC. (STAGE1_FLAGS_TO_PASS): Add GDC. (EXTRA_TARGET_FLAGS): Add GDC and GDCFLAGS. * config-ml.in: Treat GDC and GDCFLAGS like other compiler/flag environment variables. * configure: Rebuild. * configure.ac: Add target-libphobos to target_libraries. Set and substitute GDC_FOR_BUILD and GDC_FOR_TARGET. config/ChangeLog: * multi.m4: Set GDC. gcc/ChangeLog: * Makefile.in (tm_d_file_list, tm_d_include_list): New variables. (TM_D_H, D_TARGET_DEF, D_TARGET_H, D_TARGET_OBJS): New variables. (tm_d.h, cs-tm_d.h, default-d.o): New rules. (d/d-target-hooks-def.h, s-d-target-hooks-def-h): New rules. (s-tm-texi): Also check timestamp on d-target.def. (generated_files): Add TM_D_H and d-target-hooks-def.h. (build/genhooks.o): Also depend on D_TARGET_DEF. * config.gcc (tm_d_file, d_target_objs, target_has_targetdm): New variables. * config/aarch64/aarch64-d.c: New file. * config/aarch64/aarch64-linux.h (GNU_USER_TARGET_D_CRITSEC_SIZE): Define. * config/aarch64/aarch64-protos.h (aarch64_d_target_versions): New prototype. * config/aarch64/aarch64.h (TARGET_D_CPU_VERSIONS): Define. * config/aarch64/t-aarch64 (aarch64-d.o): New rule. * config/arm/arm-d.c: New file. * config/arm/arm-protos.h (arm_d_target_versions): New prototype. * config/arm/arm.h (TARGET_D_CPU_VERSIONS): Define. * config/arm/linux-eabi.h (EXTRA_TARGET_D_OS_VERSIONS): Define. * config/arm/t-arm (arm-d.o): New rule. * config/default-d.c: New file. * config/glibc-d.c: New file. * config/gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/i386/i386-d.c: New file. * config/i386/i386-protos.h (ix86_d_target_versions): New prototype. * config/i386/i386.h (TARGET_D_CPU_VERSIONS): Define. * config/i386/linux-common.h (EXTRA_TARGET_D_OS_VERSIONS): Define. (GNU_USER_TARGET_D_CRITSEC_SIZE): Define. * config/i386/t-i386 (i386-d.o): New rule. * config/kfreebsd-gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/kopensolaris-gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/linux-android.h (ANDROID_TARGET_D_OS_VERSIONS): Define. * config/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/mips/linux-common.h (EXTRA_TARGET_D_OS_VERSIONS): Define. * config/mips/mips-d.c: New file. * config/mips/mips-protos.h (mips_d_target_versions): New prototype. * config/mips/mips.h (TARGET_D_CPU_VERSIONS): Define. * config/mips/t-mips (mips-d.o): New rule. * config/powerpcspe/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/powerpcspe/linux64.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/powerpcspe/powerpcspe-d.c: New file. * config/powerpcspe/powerpcspe-protos.h (rs6000_d_target_versions): New prototype. * config/powerpcspe/powerpcspe.c (rs6000_output_function_epilogue): Support GNU D by using 0 as the language type. * config/powerpcspe/powerpcspe.h (TARGET_D_CPU_VERSIONS): Define. * config/powerpcspe/t-powerpcspe (powerpcspe-d.o): New rule. * config/riscv/riscv-d.c: New file. * config/riscv/riscv-protos.h (riscv_d_target_versions): New prototype. * config/riscv/riscv.h (TARGET_D_CPU_VERSIONS): Define. * config/riscv/t-riscv (riscv-d.o): New rule. * config/rs6000/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/rs6000/linux64.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/rs6000/rs6000-d.c: New file. * config/rs6000/rs6000-protos.h (rs6000_d_target_versions): New prototype. * config/rs6000/rs6000.c (rs6000_output_function_epilogue): Support GNU D by using 0 as the language type. * config/rs6000/rs6000.h (TARGET_D_CPU_VERSIONS): Define. * config/rs6000/t-rs6000 (rs6000-d.o): New rule. * config/s390/s390-d.c: New file. * config/s390/s390-protos.h (s390_d_target_versions): New prototype. * config/s390/s390.h (TARGET_D_CPU_VERSIONS): Define. * config/s390/t-s390 (s390-d.o): New rule. * config/sparc/sparc-d.c: New file. * config/sparc/sparc-protos.h (sparc_d_target_versions): New prototype. * config/sparc/sparc.h (TARGET_D_CPU_VERSIONS): Define. * config/sparc/t-sparc (sparc-d.o): New rule. * config/t-glibc (glibc-d.o): New rule. * configure: Regenerated. * configure.ac (tm_d_file): New variable. (tm_d_file_list, tm_d_include_list, d_target_objs): Add substitutes. * doc/contrib.texi (Contributors): Add self for the D frontend. * doc/frontends.texi (G++ and GCC): Mention D as a supported language. * doc/install.texi (Configuration): Mention libphobos as an option for --enable-shared. Mention d as an option for --enable-languages. (Testing): Mention check-d as a target. * doc/invoke.texi (Overall Options): Mention .d, .dd, and .di as file name suffixes. Mention d as a -x option. * doc/sourcebuild.texi (Top Level): Mention libphobos. * doc/standards.texi (Standards): Add section on D language. * doc/tm.texi: Regenerated. * doc/tm.texi.in: Add @node for D language and ABI, and @hook for TARGET_CPU_VERSIONS, TARGET_D_OS_VERSIONS, and TARGET_D_CRITSEC_SIZE. * dwarf2out.c (is_dlang): New function. (gen_compile_unit_die): Use DW_LANG_D for D. (declare_in_namespace): Return module die for D, instead of adding extra declarations into the namespace. (gen_namespace_die): Generate DW_TAG_module for D. (gen_decl_die): Handle CONST_DECLSs for D. (dwarf2out_decl): Likewise. (prune_unused_types_walk_local_classes): Handle DW_tag_interface_type. (prune_unused_types_walk): Handle DW_tag_interface_type same as other kinds of aggregates. * gcc.c (default_compilers): Add entries for .d, .dd and .di. * genhooks.c: Include d/d-target.def. gcc/po/ChangeLog: * EXCLUDES: Add sources from d/dmd. gcc/testsuite/ChangeLog: * gcc.misc-tests/help.exp: Add D to option descriptions check. * gdc.dg/asan/asan.exp: New file. * gdc.dg/asan/gdc272.d: New test. * gdc.dg/compilable.d: New test. * gdc.dg/dg.exp: New file. * gdc.dg/gdc254.d: New test. * gdc.dg/gdc260.d: New test. * gdc.dg/gdc270a.d: New test. * gdc.dg/gdc270b.d: New test. * gdc.dg/gdc282.d: New test. * gdc.dg/gdc283.d: New test. * gdc.dg/imports/gdc170.d: New test. * gdc.dg/imports/gdc231.d: New test. * gdc.dg/imports/gdc239.d: New test. * gdc.dg/imports/gdc241a.d: New test. * gdc.dg/imports/gdc241b.d: New test. * gdc.dg/imports/gdc251a.d: New test. * gdc.dg/imports/gdc251b.d: New test. * gdc.dg/imports/gdc253.d: New test. * gdc.dg/imports/gdc254a.d: New test. * gdc.dg/imports/gdc256.d: New test. * gdc.dg/imports/gdc27.d: New test. * gdc.dg/imports/gdcpkg256/package.d: New test. * gdc.dg/imports/runnable.d: New test. * gdc.dg/link.d: New test. * gdc.dg/lto/lto.exp: New file. * gdc.dg/lto/ltotests_0.d: New test. * gdc.dg/lto/ltotests_1.d: New test. * gdc.dg/runnable.d: New test. * gdc.dg/simd.d: New test. * gdc.test/gdc-test.exp: New file. * lib/gdc-dg.exp: New file. * lib/gdc.exp: New file. libphobos/ChangeLog: * Makefile.am: New file. * Makefile.in: New file. * acinclude.m4: New file. * aclocal.m4: New file. * config.h.in: New file. * configure: New file. * configure.ac: New file. * d_rules.am: New file. * libdruntime/Makefile.am: New file. * libdruntime/Makefile.in: New file. * libdruntime/__entrypoint.di: New file. * libdruntime/__main.di: New file. * libdruntime/gcc/attribute.d: New file. * libdruntime/gcc/backtrace.d: New file. * libdruntime/gcc/builtins.d: New file. * libdruntime/gcc/config.d.in: New file. * libdruntime/gcc/deh.d: New file. * libdruntime/gcc/libbacktrace.d.in: New file. * libdruntime/gcc/unwind/arm.d: New file. * libdruntime/gcc/unwind/arm_common.d: New file. * libdruntime/gcc/unwind/c6x.d: New file. * libdruntime/gcc/unwind/generic.d: New file. * libdruntime/gcc/unwind/package.d: New file. * libdruntime/gcc/unwind/pe.d: New file. * m4/autoconf.m4: New file. * m4/druntime.m4: New file. * m4/druntime/cpu.m4: New file. * m4/druntime/libraries.m4: New file. * m4/druntime/os.m4: New file. * m4/gcc_support.m4: New file. * m4/gdc.m4: New file. * m4/libtool.m4: New file. * src/Makefile.am: New file. * src/Makefile.in: New file. * src/libgphobos.spec.in: New file. * testsuite/Makefile.am: New file. * testsuite/Makefile.in: New file. * testsuite/config/default.exp: New file. * testsuite/lib/libphobos-dg.exp: New file. * testsuite/lib/libphobos.exp: New file. * testsuite/testsuite_flags.in: New file. From-SVN: r265573
Diffstat (limited to 'libphobos/src/std/container')
-rw-r--r--libphobos/src/std/container/array.d2419
-rw-r--r--libphobos/src/std/container/binaryheap.d595
-rw-r--r--libphobos/src/std/container/dlist.d1039
-rw-r--r--libphobos/src/std/container/package.d1156
-rw-r--r--libphobos/src/std/container/rbtree.d2065
-rw-r--r--libphobos/src/std/container/slist.d846
-rw-r--r--libphobos/src/std/container/util.d189
7 files changed, 8309 insertions, 0 deletions
diff --git a/libphobos/src/std/container/array.d b/libphobos/src/std/container/array.d
new file mode 100644
index 0000000..eee8901
--- /dev/null
+++ b/libphobos/src/std/container/array.d
@@ -0,0 +1,2419 @@
+/**
+ * This module provides an `Array` type with deterministic memory usage not
+ * reliant on the GC, as an alternative to the built-in arrays.
+ *
+ * This module is a submodule of $(MREF std, container).
+ *
+ * Source: $(PHOBOSSRC std/container/_array.d)
+ *
+ * Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
+ *
+ * License: Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at $(HTTP
+ * boost.org/LICENSE_1_0.txt)).
+ *
+ * Authors: $(HTTP erdani.com, Andrei Alexandrescu)
+ *
+ * $(SCRIPT inhibitQuickIndex = 1;)
+ */
+module std.container.array;
+
+import core.exception : RangeError;
+import std.range.primitives;
+import std.traits;
+
+public import std.container.util;
+
+///
+@system unittest
+{
+ 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);
+}
+
+///
+@system unittest
+{
+ 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]));
+}
+
+/// `Array!bool` packs together values efficiently by allocating one bit per element
+@system unittest
+{
+ Array!bool arr;
+ arr.insert([true, true, false, true, false]);
+ assert(arr.length == 5);
+}
+
+private struct RangeT(A)
+{
+ /* Workaround for Issue 13629 at https://issues.dlang.org/show_bug.cgi?id=13629
+ See also: http://forum.dlang.org/post/vbmwhzvawhnkoxrhbnyb@forum.dlang.org
+ */
+ private A[1] _outer_;
+ private @property ref inout(A) _outer() inout { return _outer_[0]; }
+
+ private size_t _a, _b;
+
+ /* E is different from T when A is more restrictively qualified than T:
+ immutable(Array!int) => T == int, E = immutable(int) */
+ alias E = typeof(_outer_[0]._data._payload[0]);
+
+ private this(ref A data, size_t a, size_t b)
+ {
+ _outer_ = data;
+ _a = a;
+ _b = b;
+ }
+
+ @property RangeT save()
+ {
+ return this;
+ }
+
+ @property bool empty() @safe pure nothrow const
+ {
+ return _a >= _b;
+ }
+
+ @property size_t length() @safe pure nothrow const
+ {
+ return _b - _a;
+ }
+ alias opDollar = length;
+
+ @property ref inout(E) front() inout
+ {
+ assert(!empty, "Attempting to access the front of an empty Array");
+ return _outer[_a];
+ }
+ @property ref inout(E) back() inout
+ {
+ assert(!empty, "Attempting to access the back of an empty Array");
+ return _outer[_b - 1];
+ }
+
+ void popFront() @safe @nogc pure nothrow
+ {
+ assert(!empty, "Attempting to popFront an empty Array");
+ ++_a;
+ }
+
+ void popBack() @safe @nogc pure nothrow
+ {
+ assert(!empty, "Attempting to popBack an empty Array");
+ --_b;
+ }
+
+ static if (isMutable!A)
+ {
+ import std.algorithm.mutation : move;
+
+ E moveFront()
+ {
+ assert(!empty && _a < _outer.length);
+ return move(_outer._data._payload[_a]);
+ }
+
+ E moveBack()
+ {
+ assert(!empty && _b <= _outer.length);
+ return move(_outer._data._payload[_b - 1]);
+ }
+
+ E moveAt(size_t i)
+ {
+ assert(_a + i < _b && _a + i < _outer.length);
+ return move(_outer._data._payload[_a + i]);
+ }
+ }
+
+ ref inout(E) opIndex(size_t i) inout
+ {
+ assert(_a + i < _b);
+ return _outer[_a + i];
+ }
+
+ RangeT opSlice()
+ {
+ return typeof(return)(_outer, _a, _b);
+ }
+
+ RangeT opSlice(size_t i, size_t j)
+ {
+ assert(i <= j && _a + j <= _b);
+ return typeof(return)(_outer, _a + i, _a + j);
+ }
+
+ RangeT!(const(A)) opSlice() const
+ {
+ return typeof(return)(_outer, _a, _b);
+ }
+
+ RangeT!(const(A)) opSlice(size_t i, size_t j) const
+ {
+ assert(i <= j && _a + j <= _b);
+ return typeof(return)(_outer, _a + i, _a + j);
+ }
+
+ static if (isMutable!A)
+ {
+ void opSliceAssign(E value)
+ {
+ assert(_b <= _outer.length);
+ _outer[_a .. _b] = value;
+ }
+
+ void opSliceAssign(E value, size_t i, size_t j)
+ {
+ assert(_a + j <= _b);
+ _outer[_a + i .. _a + j] = value;
+ }
+
+ void opSliceUnary(string op)()
+ if (op == "++" || op == "--")
+ {
+ assert(_b <= _outer.length);
+ mixin(op~"_outer[_a .. _b];");
+ }
+
+ void opSliceUnary(string op)(size_t i, size_t j)
+ if (op == "++" || op == "--")
+ {
+ assert(_a + j <= _b);
+ mixin(op~"_outer[_a + i .. _a + j];");
+ }
+
+ void opSliceOpAssign(string op)(E value)
+ {
+ assert(_b <= _outer.length);
+ mixin("_outer[_a .. _b] "~op~"= value;");
+ }
+
+ void opSliceOpAssign(string op)(E value, size_t i, size_t j)
+ {
+ assert(_a + j <= _b);
+ mixin("_outer[_a + i .. _a + j] "~op~"= value;");
+ }
+ }
+}
+
+/**
+ * _Array type with deterministic control of memory. The memory allocated
+ * for the array is reclaimed as soon as possible; there is no reliance
+ * on the garbage collector. `Array` uses `malloc`, `realloc` and `free`
+ * for managing its own memory.
+ *
+ * This means that pointers to elements of an `Array` will become
+ * dangling as soon as the element is removed from the `Array`. On the other hand
+ * the memory allocated by an `Array` will be scanned by the GC and
+ * GC managed objects referenced from an `Array` will be kept alive.
+ *
+ * Note:
+ *
+ * When using `Array` with range-based functions like those in `std.algorithm`,
+ * `Array` must be sliced to get a range (for example, use `array[].map!`
+ * instead of `array.map!`). The container itself is not a range.
+ */
+struct Array(T)
+if (!is(Unqual!T == bool))
+{
+ import core.stdc.stdlib : malloc, realloc, free;
+ import core.stdc.string : memcpy, memmove, memset;
+
+ import core.memory : GC;
+
+ import std.exception : enforce;
+ import std.typecons : RefCounted, RefCountedAutoInitialize;
+
+ // This structure is not copyable.
+ private struct Payload
+ {
+ size_t _capacity;
+ T[] _payload;
+
+ this(T[] p) { _capacity = p.length; _payload = p; }
+
+ // Destructor releases array memory
+ ~this()
+ {
+ // Warning: destroy would destroy also class instances.
+ // The hasElaborateDestructor protects us here.
+ static if (hasElaborateDestructor!T)
+ foreach (ref e; _payload)
+ .destroy(e);
+
+ static if (hasIndirections!T)
+ GC.removeRange(_payload.ptr);
+
+ free(_payload.ptr);
+ }
+
+ this(this) @disable;
+
+ void opAssign(Payload rhs) @disable;
+
+ @property size_t length() const
+ {
+ return _payload.length;
+ }
+
+ @property void length(size_t newLength)
+ {
+ import std.algorithm.mutation : initializeAll;
+
+ if (length >= newLength)
+ {
+ // shorten
+ static if (hasElaborateDestructor!T)
+ foreach (ref e; _payload.ptr[newLength .. _payload.length])
+ .destroy(e);
+
+ _payload = _payload.ptr[0 .. newLength];
+ return;
+ }
+ immutable startEmplace = length;
+ if (_capacity < newLength)
+ {
+ // enlarge
+ import core.checkedint : mulu;
+
+ bool overflow;
+ const nbytes = mulu(newLength, T.sizeof, overflow);
+ if (overflow)
+ assert(0);
+ _payload = (cast(T*) realloc(_payload.ptr, nbytes))[0 .. newLength];
+ _capacity = newLength;
+ }
+ else
+ {
+ _payload = _payload.ptr[0 .. newLength];
+ }
+ initializeAll(_payload.ptr[startEmplace .. newLength]);
+ }
+
+ @property size_t capacity() const
+ {
+ return _capacity;
+ }
+
+ void reserve(size_t elements)
+ {
+ if (elements <= capacity) return;
+ import core.checkedint : mulu;
+ bool overflow;
+ const sz = mulu(elements, T.sizeof, overflow);
+ if (overflow)
+ assert(0);
+ static if (hasIndirections!T)
+ {
+ /* Because of the transactional nature of this
+ * relative to the garbage collector, ensure no
+ * threading bugs by using malloc/copy/free rather
+ * than realloc.
+ */
+ immutable oldLength = length;
+
+ auto newPayloadPtr = cast(T*) malloc(sz);
+ newPayloadPtr || assert(false, "std.container.Array.reserve failed to allocate memory");
+ auto newPayload = newPayloadPtr[0 .. oldLength];
+
+ // copy old data over to new array
+ memcpy(newPayload.ptr, _payload.ptr, T.sizeof * oldLength);
+ // Zero out unused capacity to prevent gc from seeing false pointers
+ memset(newPayload.ptr + oldLength,
+ 0,
+ (elements - oldLength) * T.sizeof);
+ GC.addRange(newPayload.ptr, sz);
+ GC.removeRange(_payload.ptr);
+ free(_payload.ptr);
+ _payload = newPayload;
+ }
+ else
+ {
+ // These can't have pointers, so no need to zero unused region
+ auto newPayloadPtr = cast(T*) realloc(_payload.ptr, sz);
+ newPayloadPtr || assert(false, "std.container.Array.reserve failed to allocate memory");
+ auto newPayload = newPayloadPtr[0 .. length];
+ _payload = newPayload;
+ }
+ _capacity = elements;
+ }
+
+ // Insert one item
+ size_t insertBack(Elem)(Elem elem)
+ if (isImplicitlyConvertible!(Elem, T))
+ {
+ import std.conv : emplace;
+ if (_capacity == length)
+ {
+ reserve(1 + capacity * 3 / 2);
+ }
+ assert(capacity > length && _payload.ptr);
+ emplace(_payload.ptr + _payload.length, elem);
+ _payload = _payload.ptr[0 .. _payload.length + 1];
+ return 1;
+ }
+
+ // Insert a range of items
+ size_t insertBack(Range)(Range r)
+ if (isInputRange!Range && isImplicitlyConvertible!(ElementType!Range, T))
+ {
+ static if (hasLength!Range)
+ {
+ immutable oldLength = length;
+ reserve(oldLength + r.length);
+ }
+ size_t result;
+ foreach (item; r)
+ {
+ insertBack(item);
+ ++result;
+ }
+ static if (hasLength!Range)
+ {
+ assert(length == oldLength + r.length);
+ }
+ return result;
+ }
+ }
+ private alias Data = RefCounted!(Payload, RefCountedAutoInitialize.no);
+ private Data _data;
+
+ /**
+ * Constructor taking a number of items.
+ */
+ this(U)(U[] values...)
+ if (isImplicitlyConvertible!(U, T))
+ {
+ import core.checkedint : mulu;
+ import std.conv : emplace;
+ bool overflow;
+ const nbytes = mulu(values.length, T.sizeof, overflow);
+ if (overflow) assert(0);
+ auto p = cast(T*) malloc(nbytes);
+ static if (hasIndirections!T)
+ {
+ if (p)
+ GC.addRange(p, T.sizeof * values.length);
+ }
+
+ foreach (i, e; values)
+ {
+ emplace(p + i, e);
+ }
+ _data = Data(p[0 .. values.length]);
+ }
+
+ /**
+ * Constructor taking an input range
+ */
+ this(Range)(Range r)
+ if (isInputRange!Range && isImplicitlyConvertible!(ElementType!Range, T) && !is(Range == T[]))
+ {
+ insertBack(r);
+ }
+
+ /**
+ * Comparison for equality.
+ */
+ bool opEquals(const Array rhs) const
+ {
+ return opEquals(rhs);
+ }
+
+ /// ditto
+ bool opEquals(ref const Array rhs) const
+ {
+ if (empty) return rhs.empty;
+ if (rhs.empty) return false;
+ return _data._payload == rhs._data._payload;
+ }
+
+ /**
+ * Defines the array's primary range, which is a random-access range.
+ *
+ * `ConstRange` is a variant with `const` elements.
+ * `ImmutableRange` is a variant with `immutable` elements.
+ */
+ alias Range = RangeT!Array;
+
+ /// ditto
+ alias ConstRange = RangeT!(const Array);
+
+ /// ditto
+ alias ImmutableRange = RangeT!(immutable Array);
+
+ /**
+ * Duplicates the array. The elements themselves are not transitively
+ * duplicated.
+ *
+ * Complexity: $(BIGOH length).
+ */
+ @property Array dup()
+ {
+ if (!_data.refCountedStore.isInitialized) return this;
+ return Array(_data._payload);
+ }
+
+ /**
+ * Returns: `true` if and only if the array has no elements.
+ *
+ * Complexity: $(BIGOH 1)
+ */
+ @property bool empty() const
+ {
+ return !_data.refCountedStore.isInitialized || _data._payload.empty;
+ }
+
+ /**
+ * Returns: The number of elements in the array.
+ *
+ * Complexity: $(BIGOH 1).
+ */
+ @property size_t length() const
+ {
+ return _data.refCountedStore.isInitialized ? _data._payload.length : 0;
+ }
+
+ /// ditto
+ size_t opDollar() const
+ {
+ return length;
+ }
+
+ /**
+ * Returns: The maximum number of elements the array can store without
+ * reallocating memory and invalidating iterators upon insertion.
+ *
+ * Complexity: $(BIGOH 1)
+ */
+ @property size_t capacity()
+ {
+ return _data.refCountedStore.isInitialized ? _data._capacity : 0;
+ }
+
+ /**
+ * Ensures sufficient capacity to accommodate `e` _elements.
+ * If `e < capacity`, this method does nothing.
+ *
+ * Postcondition: `capacity >= e`
+ *
+ * Note: If the capacity is increased, one should assume that all
+ * iterators to the elements are invalidated.
+ *
+ * Complexity: at most $(BIGOH length) if `e > capacity`, otherwise $(BIGOH 1).
+ */
+ void reserve(size_t elements)
+ {
+ if (!_data.refCountedStore.isInitialized)
+ {
+ if (!elements) return;
+ import core.checkedint : mulu;
+ bool overflow;
+ const sz = mulu(elements, T.sizeof, overflow);
+ if (overflow) assert(0);
+ auto p = malloc(sz);
+ p || assert(false, "std.container.Array.reserve failed to allocate memory");
+ static if (hasIndirections!T)
+ {
+ GC.addRange(p, sz);
+ }
+ _data = Data(cast(T[]) p[0 .. 0]);
+ _data._capacity = elements;
+ }
+ else
+ {
+ _data.reserve(elements);
+ }
+ }
+
+ /**
+ * Returns: A range that iterates over elements of the array in
+ * forward order.
+ *
+ * Complexity: $(BIGOH 1)
+ */
+ Range opSlice()
+ {
+ return typeof(return)(this, 0, length);
+ }
+
+ ConstRange opSlice() const
+ {
+ return typeof(return)(this, 0, length);
+ }
+
+ ImmutableRange opSlice() immutable
+ {
+ return typeof(return)(this, 0, length);
+ }
+
+ /**
+ * Returns: A range that iterates over elements of the array from
+ * index `i` up to (excluding) index `j`.
+ *
+ * Precondition: `i <= j && j <= length`
+ *
+ * Complexity: $(BIGOH 1)
+ */
+ Range opSlice(size_t i, size_t j)
+ {
+ assert(i <= j && j <= length);
+ return typeof(return)(this, i, j);
+ }
+
+ ConstRange opSlice(size_t i, size_t j) const
+ {
+ assert(i <= j && j <= length);
+ return typeof(return)(this, i, j);
+ }
+
+ ImmutableRange opSlice(size_t i, size_t j) immutable
+ {
+ assert(i <= j && j <= length);
+ return typeof(return)(this, i, j);
+ }
+
+ /**
+ * Returns: The first element of the array.
+ *
+ * Precondition: `empty == false`
+ *
+ * Complexity: $(BIGOH 1)
+ */
+ @property ref inout(T) front() inout
+ {
+ assert(_data.refCountedStore.isInitialized);
+ return _data._payload[0];
+ }
+
+ /**
+ * Returns: The last element of the array.
+ *
+ * Precondition: `empty == false`
+ *
+ * Complexity: $(BIGOH 1)
+ */
+ @property ref inout(T) back() inout
+ {
+ assert(_data.refCountedStore.isInitialized);
+ return _data._payload[$ - 1];
+ }
+
+ /**
+ * Returns: The element or a reference to the element at the specified index.
+ *
+ * Precondition: `i < length`
+ *
+ * Complexity: $(BIGOH 1)
+ */
+ ref inout(T) opIndex(size_t i) inout
+ {
+ assert(_data.refCountedStore.isInitialized);
+ return _data._payload[i];
+ }
+
+ /**
+ * Slicing operators executing the specified operation on the entire slice.
+ *
+ * Precondition: `i < j && j < length`
+ *
+ * Complexity: $(BIGOH slice.length)
+ */
+ void opSliceAssign(T value)
+ {
+ if (!_data.refCountedStore.isInitialized) return;
+ _data._payload[] = value;
+ }
+
+ /// ditto
+ void opSliceAssign(T value, size_t i, size_t j)
+ {
+ auto slice = _data.refCountedStore.isInitialized ?
+ _data._payload :
+ T[].init;
+ slice[i .. j] = value;
+ }
+
+ /// ditto
+ void opSliceUnary(string op)()
+ if (op == "++" || op == "--")
+ {
+ if (!_data.refCountedStore.isInitialized) return;
+ mixin(op~"_data._payload[];");
+ }
+
+ /// ditto
+ void opSliceUnary(string op)(size_t i, size_t j)
+ if (op == "++" || op == "--")
+ {
+ auto slice = _data.refCountedStore.isInitialized ? _data._payload : T[].init;
+ mixin(op~"slice[i .. j];");
+ }
+
+ /// ditto
+ void opSliceOpAssign(string op)(T value)
+ {
+ if (!_data.refCountedStore.isInitialized) return;
+ mixin("_data._payload[] "~op~"= value;");
+ }
+
+ /// ditto
+ void opSliceOpAssign(string op)(T value, size_t i, size_t j)
+ {
+ auto slice = _data.refCountedStore.isInitialized ? _data._payload : T[].init;
+ mixin("slice[i .. j] "~op~"= value;");
+ }
+
+ private enum hasSliceWithLength(T) = is(typeof({ T t = T.init; t[].length; }));
+
+ /**
+ * Returns: A new array which is a concatenation of `this` and its argument.
+ *
+ * Complexity:
+ * $(BIGOH length + m), where `m` is the number of elements in `stuff`.
+ */
+ Array opBinary(string op, Stuff)(Stuff stuff)
+ if (op == "~")
+ {
+ Array result;
+
+ static if (hasLength!Stuff || isNarrowString!Stuff)
+ result.reserve(length + stuff.length);
+ else static if (hasSliceWithLength!Stuff)
+ result.reserve(length + stuff[].length);
+ else static if (isImplicitlyConvertible!(Stuff, T))
+ result.reserve(length + 1);
+
+ result.insertBack(this[]);
+ result ~= stuff;
+ return result;
+ }
+
+ /**
+ * Forwards to `insertBack`.
+ */
+ void opOpAssign(string op, Stuff)(auto ref Stuff stuff)
+ if (op == "~")
+ {
+ static if (is(typeof(stuff[])) && isImplicitlyConvertible!(typeof(stuff[0]), T))
+ {
+ insertBack(stuff[]);
+ }
+ else
+ {
+ insertBack(stuff);
+ }
+ }
+
+ /**
+ * Removes all the elements from the array and releases allocated memory.
+ *
+ * Postcondition: `empty == true && capacity == 0`
+ *
+ * Complexity: $(BIGOH length)
+ */
+ void clear()
+ {
+ _data = Data.init;
+ }
+
+ /**
+ * Sets the number of elements in the array to `newLength`. If `newLength`
+ * is greater than `length`, the new elements are added to the end of the
+ * array and initialized with `T.init`.
+ *
+ * Complexity:
+ * Guaranteed $(BIGOH abs(length - newLength)) if `capacity >= newLength`.
+ * If `capacity < newLength` the worst case is $(BIGOH newLength).
+ *
+ * Postcondition: `length == newLength`
+ */
+ @property void length(size_t newLength)
+ {
+ _data.refCountedStore.ensureInitialized();
+ _data.length = newLength;
+ }
+
+ /**
+ * Removes the last element from the array and returns it.
+ * Both stable and non-stable versions behave the same and guarantee
+ * that ranges iterating over the array are never invalidated.
+ *
+ * Precondition: `empty == false`
+ *
+ * Returns: The element removed.
+ *
+ * Complexity: $(BIGOH 1).
+ *
+ * Throws: `Exception` if the array is empty.
+ */
+ T removeAny()
+ {
+ auto result = back;
+ removeBack();
+ return result;
+ }
+
+ /// ditto
+ alias stableRemoveAny = removeAny;
+
+ /**
+ * Inserts the specified elements at the back of the array. `stuff` can be
+ * a value convertible to `T` or a range of objects convertible to `T`.
+ *
+ * Returns: The number of elements inserted.
+ *
+ * Complexity:
+ * $(BIGOH length + m) if reallocation takes place, otherwise $(BIGOH m),
+ * where `m` is the number of elements in `stuff`.
+ */
+ size_t insertBack(Stuff)(Stuff stuff)
+ if (isImplicitlyConvertible!(Stuff, T) ||
+ isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
+ {
+ _data.refCountedStore.ensureInitialized();
+ return _data.insertBack(stuff);
+ }
+
+ /// ditto
+ alias insert = insertBack;
+
+ /**
+ * Removes the value from the back of the array. Both stable and non-stable
+ * versions behave the same and guarantee that ranges iterating over the
+ * array are never invalidated.
+ *
+ * Precondition: `empty == false`
+ *
+ * Complexity: $(BIGOH 1).
+ *
+ * Throws: `Exception` if the array is empty.
+ */
+ void removeBack()
+ {
+ enforce(!empty);
+ static if (hasElaborateDestructor!T)
+ .destroy(_data._payload[$ - 1]);
+
+ _data._payload = _data._payload[0 .. $ - 1];
+ }
+
+ /// ditto
+ alias stableRemoveBack = removeBack;
+
+ /**
+ * Removes `howMany` values from the back of the array.
+ * Unlike the unparameterized versions above, these functions
+ * do not throw if they could not remove `howMany` elements. Instead,
+ * if `howMany > n`, all elements are removed. The returned value is
+ * the effective number of elements removed. Both stable and non-stable
+ * versions behave the same and guarantee that ranges iterating over
+ * the array are never invalidated.
+ *
+ * Returns: The number of elements removed.
+ *
+ * Complexity: $(BIGOH howMany).
+ */
+ size_t removeBack(size_t howMany)
+ {
+ if (howMany > length) howMany = length;
+ static if (hasElaborateDestructor!T)
+ foreach (ref e; _data._payload[$ - howMany .. $])
+ .destroy(e);
+
+ _data._payload = _data._payload[0 .. $ - howMany];
+ return howMany;
+ }
+
+ /// ditto
+ alias stableRemoveBack = removeBack;
+
+ /**
+ * Inserts `stuff` before, after, or instead range `r`, which must
+ * be a valid range previously extracted from this array. `stuff`
+ * can be a value convertible to `T` or a range of objects convertible
+ * to `T`. Both stable and non-stable version behave the same and
+ * guarantee that ranges iterating over the array are never invalidated.
+ *
+ * Returns: The number of values inserted.
+ *
+ * Complexity: $(BIGOH length + m), where `m` is the length of `stuff`.
+ *
+ * Throws: `Exception` if `r` is not a range extracted from this array.
+ */
+ size_t insertBefore(Stuff)(Range r, Stuff stuff)
+ if (isImplicitlyConvertible!(Stuff, T))
+ {
+ import std.conv : emplace;
+ enforce(r._outer._data is _data && r._a <= length);
+ reserve(length + 1);
+ assert(_data.refCountedStore.isInitialized);
+ // Move elements over by one slot
+ memmove(_data._payload.ptr + r._a + 1,
+ _data._payload.ptr + r._a,
+ T.sizeof * (length - r._a));
+ emplace(_data._payload.ptr + r._a, stuff);
+ _data._payload = _data._payload.ptr[0 .. _data._payload.length + 1];
+ return 1;
+ }
+
+ /// ditto
+ size_t insertBefore(Stuff)(Range r, Stuff stuff)
+ if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
+ {
+ import std.conv : emplace;
+ enforce(r._outer._data is _data && r._a <= length);
+ static if (isForwardRange!Stuff)
+ {
+ // Can find the length in advance
+ auto extra = walkLength(stuff);
+ if (!extra) return 0;
+ reserve(length + extra);
+ assert(_data.refCountedStore.isInitialized);
+ // Move elements over by extra slots
+ memmove(_data._payload.ptr + r._a + extra,
+ _data._payload.ptr + r._a,
+ T.sizeof * (length - r._a));
+ foreach (p; _data._payload.ptr + r._a ..
+ _data._payload.ptr + r._a + extra)
+ {
+ emplace(p, stuff.front);
+ stuff.popFront();
+ }
+ _data._payload =
+ _data._payload.ptr[0 .. _data._payload.length + extra];
+ return extra;
+ }
+ else
+ {
+ import std.algorithm.mutation : bringToFront;
+ enforce(_data);
+ immutable offset = r._a;
+ enforce(offset <= length);
+ auto result = insertBack(stuff);
+ bringToFront(this[offset .. length - result],
+ this[length - result .. length]);
+ return result;
+ }
+ }
+
+ /// ditto
+ alias stableInsertBefore = insertBefore;
+
+ /// ditto
+ size_t insertAfter(Stuff)(Range r, Stuff stuff)
+ {
+ import std.algorithm.mutation : bringToFront;
+ enforce(r._outer._data is _data);
+ // TODO: optimize
+ immutable offset = r._b;
+ enforce(offset <= length);
+ auto result = insertBack(stuff);
+ bringToFront(this[offset .. length - result],
+ this[length - result .. length]);
+ return result;
+ }
+
+ /// ditto
+ size_t replace(Stuff)(Range r, Stuff stuff)
+ if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
+ {
+ enforce(r._outer._data is _data);
+ size_t result;
+ for (; !stuff.empty; stuff.popFront())
+ {
+ if (r.empty)
+ {
+ // insert the rest
+ return result + insertBefore(r, stuff);
+ }
+ r.front = stuff.front;
+ r.popFront();
+ ++result;
+ }
+ // Remove remaining stuff in r
+ linearRemove(r);
+ return result;
+ }
+
+ /// ditto
+ size_t replace(Stuff)(Range r, Stuff stuff)
+ if (isImplicitlyConvertible!(Stuff, T))
+ {
+ enforce(r._outer._data is _data);
+ if (r.empty)
+ {
+ insertBefore(r, stuff);
+ }
+ else
+ {
+ r.front = stuff;
+ r.popFront();
+ linearRemove(r);
+ }
+ return 1;
+ }
+
+ /**
+ * Removes all elements belonging to `r`, which must be a range
+ * obtained originally from this array.
+ *
+ * Returns: A range spanning the remaining elements in the array that
+ * initially were right after `r`.
+ *
+ * Complexity: $(BIGOH length)
+ *
+ * Throws: `Exception` if `r` is not a valid range extracted from this array.
+ */
+ Range linearRemove(Range r)
+ {
+ import std.algorithm.mutation : copy;
+
+ enforce(r._outer._data is _data);
+ enforce(_data.refCountedStore.isInitialized);
+ enforce(r._a <= r._b && r._b <= length);
+ immutable offset1 = r._a;
+ immutable offset2 = r._b;
+ immutable tailLength = length - offset2;
+ // Use copy here, not a[] = b[] because the ranges may overlap
+ copy(this[offset2 .. length], this[offset1 .. offset1 + tailLength]);
+ length = offset1 + tailLength;
+ return this[length - tailLength .. length];
+ }
+}
+
+@system unittest
+{
+ Array!int a;
+ assert(a.empty);
+}
+
+@system unittest
+{
+ Array!int a;
+ a.length = 10;
+ assert(a.length == 10);
+ assert(a.capacity >= a.length);
+}
+
+@system unittest
+{
+ struct Dumb { int x = 5; }
+ Array!Dumb a;
+ a.length = 10;
+ assert(a.length == 10);
+ assert(a.capacity >= a.length);
+ immutable cap = a.capacity;
+ foreach (ref e; a)
+ e.x = 10;
+ a.length = 5;
+ assert(a.length == 5);
+ // do not realloc if length decreases
+ assert(a.capacity == cap);
+ foreach (ref e; a)
+ assert(e.x == 10);
+
+ a.length = 8;
+ assert(a.length == 8);
+ // do not realloc if capacity sufficient
+ assert(a.capacity == cap);
+ assert(Dumb.init.x == 5);
+ foreach (i; 0 .. 5)
+ assert(a[i].x == 10);
+ foreach (i; 5 .. a.length)
+ assert(a[i].x == Dumb.init.x);
+
+ // realloc required, check if values properly copied
+ a[] = Dumb(1);
+ a.length = 20;
+ assert(a.capacity >= 20);
+ foreach (i; 0 .. 8)
+ assert(a[i].x == 1);
+ foreach (i; 8 .. a.length)
+ assert(a[i].x == Dumb.init.x);
+
+ // check if overlapping elements properly initialized
+ a.length = 1;
+ a.length = 20;
+ assert(a[0].x == 1);
+ foreach (e; a[1 .. $])
+ assert(e.x == Dumb.init.x);
+}
+
+@system unittest
+{
+ Array!int a = Array!int(1, 2, 3);
+ //a._data._refCountedDebug = true;
+ auto b = a.dup;
+ assert(b == Array!int(1, 2, 3));
+ b.front = 42;
+ assert(b == Array!int(42, 2, 3));
+ assert(a == Array!int(1, 2, 3));
+}
+
+@system unittest
+{
+ auto a = Array!int(1, 2, 3);
+ assert(a.length == 3);
+}
+
+@system unittest
+{
+ const Array!int a = [1, 2];
+
+ assert(a[0] == 1);
+ assert(a.front == 1);
+ assert(a.back == 2);
+
+ static assert(!__traits(compiles, { a[0] = 1; }));
+ static assert(!__traits(compiles, { a.front = 1; }));
+ static assert(!__traits(compiles, { a.back = 1; }));
+
+ auto r = a[];
+ size_t i;
+ foreach (e; r)
+ {
+ assert(e == i + 1);
+ i++;
+ }
+}
+
+@safe unittest
+{
+ // REG https://issues.dlang.org/show_bug.cgi?id=13621
+ import std.container : Array, BinaryHeap;
+ alias Heap = BinaryHeap!(Array!int);
+}
+
+@system unittest
+{
+ Array!int a;
+ a.reserve(1000);
+ assert(a.length == 0);
+ assert(a.empty);
+ assert(a.capacity >= 1000);
+ auto p = a._data._payload.ptr;
+ foreach (i; 0 .. 1000)
+ {
+ a.insertBack(i);
+ }
+ assert(p == a._data._payload.ptr);
+}
+
+@system unittest
+{
+ auto a = Array!int(1, 2, 3);
+ a[1] *= 42;
+ assert(a[1] == 84);
+}
+
+@system unittest
+{
+ auto a = Array!int(1, 2, 3);
+ auto b = Array!int(11, 12, 13);
+ auto c = a ~ b;
+ assert(c == Array!int(1, 2, 3, 11, 12, 13));
+ assert(a ~ b[] == Array!int(1, 2, 3, 11, 12, 13));
+ assert(a ~ [4,5] == Array!int(1,2,3,4,5));
+}
+
+@system unittest
+{
+ auto a = Array!int(1, 2, 3);
+ auto b = Array!int(11, 12, 13);
+ a ~= b;
+ assert(a == Array!int(1, 2, 3, 11, 12, 13));
+}
+
+@system unittest
+{
+ auto a = Array!int(1, 2, 3, 4);
+ assert(a.removeAny() == 4);
+ assert(a == Array!int(1, 2, 3));
+}
+
+@system unittest
+{
+ auto a = Array!int(1, 2, 3, 4, 5);
+ auto r = a[2 .. a.length];
+ assert(a.insertBefore(r, 42) == 1);
+ assert(a == Array!int(1, 2, 42, 3, 4, 5));
+ r = a[2 .. 2];
+ assert(a.insertBefore(r, [8, 9]) == 2);
+ assert(a == Array!int(1, 2, 8, 9, 42, 3, 4, 5));
+}
+
+@system unittest
+{
+ auto a = Array!int(0, 1, 2, 3, 4, 5, 6, 7, 8);
+ a.linearRemove(a[4 .. 6]);
+ assert(a == Array!int(0, 1, 2, 3, 6, 7, 8));
+}
+
+// Give the Range object some testing.
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : retro;
+ auto a = Array!int(0, 1, 2, 3, 4, 5, 6)[];
+ auto b = Array!int(6, 5, 4, 3, 2, 1, 0)[];
+ alias A = typeof(a);
+
+ static assert(isRandomAccessRange!A);
+ static assert(hasSlicing!A);
+ static assert(hasAssignableElements!A);
+ static assert(hasMobileElements!A);
+
+ assert(equal(retro(b), a));
+ assert(a.length == 7);
+ assert(equal(a[1 .. 4], [1, 2, 3]));
+}
+// Test issue 5920
+@system unittest
+{
+ struct structBug5920
+ {
+ int order;
+ uint* pDestructionMask;
+ ~this()
+ {
+ if (pDestructionMask)
+ *pDestructionMask += 1 << order;
+ }
+ }
+
+ alias S = structBug5920;
+ uint dMask;
+
+ auto arr = Array!S(cast(S[])[]);
+ foreach (i; 0 .. 8)
+ arr.insertBack(S(i, &dMask));
+ // don't check dMask now as S may be copied multiple times (it's ok?)
+ {
+ assert(arr.length == 8);
+ dMask = 0;
+ arr.length = 6;
+ assert(arr.length == 6); // make sure shrinking calls the d'tor
+ assert(dMask == 0b1100_0000);
+ arr.removeBack();
+ assert(arr.length == 5); // make sure removeBack() calls the d'tor
+ assert(dMask == 0b1110_0000);
+ arr.removeBack(3);
+ assert(arr.length == 2); // ditto
+ assert(dMask == 0b1111_1100);
+ arr.clear();
+ assert(arr.length == 0); // make sure clear() calls the d'tor
+ assert(dMask == 0b1111_1111);
+ }
+ assert(dMask == 0b1111_1111); // make sure the d'tor is called once only.
+}
+// Test issue 5792 (mainly just to check if this piece of code is compilable)
+@system unittest
+{
+ auto a = Array!(int[])([[1,2],[3,4]]);
+ a.reserve(4);
+ assert(a.capacity >= 4);
+ assert(a.length == 2);
+ assert(a[0] == [1,2]);
+ assert(a[1] == [3,4]);
+ a.reserve(16);
+ assert(a.capacity >= 16);
+ assert(a.length == 2);
+ assert(a[0] == [1,2]);
+ assert(a[1] == [3,4]);
+}
+
+// test replace!Stuff with range Stuff
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ auto a = Array!int([1, 42, 5]);
+ a.replace(a[1 .. 2], [2, 3, 4]);
+ assert(equal(a[], [1, 2, 3, 4, 5]));
+}
+
+// test insertBefore and replace with empty Arrays
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ auto a = Array!int();
+ a.insertBefore(a[], 1);
+ assert(equal(a[], [1]));
+}
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ auto a = Array!int();
+ a.insertBefore(a[], [1, 2]);
+ assert(equal(a[], [1, 2]));
+}
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ auto a = Array!int();
+ a.replace(a[], [1, 2]);
+ assert(equal(a[], [1, 2]));
+}
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ auto a = Array!int();
+ a.replace(a[], 1);
+ assert(equal(a[], [1]));
+}
+// make sure that Array instances refuse ranges that don't belong to them
+@system unittest
+{
+ import std.exception : assertThrown;
+
+ Array!int a = [1, 2, 3];
+ auto r = a.dup[];
+ assertThrown(a.insertBefore(r, 42));
+ assertThrown(a.insertBefore(r, [42]));
+ assertThrown(a.insertAfter(r, 42));
+ assertThrown(a.replace(r, 42));
+ assertThrown(a.replace(r, [42]));
+ assertThrown(a.linearRemove(r));
+}
+@system unittest
+{
+ auto a = Array!int([1, 1]);
+ a[1] = 0; //Check Array.opIndexAssign
+ assert(a[1] == 0);
+ a[1] += 1; //Check Array.opIndexOpAssign
+ assert(a[1] == 1);
+
+ //Check Array.opIndexUnary
+ ++a[0];
+ //a[0]++ //op++ doesn't return, so this shouldn't work, even with 5044 fixed
+ assert(a[0] == 2);
+ assert(+a[0] == +2);
+ assert(-a[0] == -2);
+ assert(~a[0] == ~2);
+
+ auto r = a[];
+ r[1] = 0; //Check Array.Range.opIndexAssign
+ assert(r[1] == 0);
+ r[1] += 1; //Check Array.Range.opIndexOpAssign
+ assert(r[1] == 1);
+
+ //Check Array.Range.opIndexUnary
+ ++r[0];
+ //r[0]++ //op++ doesn't return, so this shouldn't work, even with 5044 fixed
+ assert(r[0] == 3);
+ assert(+r[0] == +3);
+ assert(-r[0] == -3);
+ assert(~r[0] == ~3);
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+
+ //Test "array-wide" operations
+ auto a = Array!int([0, 1, 2]); //Array
+ a[] += 5;
+ assert(a[].equal([5, 6, 7]));
+ ++a[];
+ assert(a[].equal([6, 7, 8]));
+ a[1 .. 3] *= 5;
+ assert(a[].equal([6, 35, 40]));
+ a[0 .. 2] = 0;
+ assert(a[].equal([0, 0, 40]));
+
+ //Test empty array
+ auto a2 = Array!int.init;
+ ++a2[];
+ ++a2[0 .. 0];
+ a2[] = 0;
+ a2[0 .. 0] = 0;
+ a2[] += 0;
+ a2[0 .. 0] += 0;
+
+ //Test "range-wide" operations
+ auto r = Array!int([0, 1, 2])[]; //Array.Range
+ r[] += 5;
+ assert(r.equal([5, 6, 7]));
+ ++r[];
+ assert(r.equal([6, 7, 8]));
+ r[1 .. 3] *= 5;
+ assert(r.equal([6, 35, 40]));
+ r[0 .. 2] = 0;
+ assert(r.equal([0, 0, 40]));
+
+ //Test empty Range
+ auto r2 = Array!int.init[];
+ ++r2[];
+ ++r2[0 .. 0];
+ r2[] = 0;
+ r2[0 .. 0] = 0;
+ r2[] += 0;
+ r2[0 .. 0] += 0;
+}
+
+// Test issue 11194
+@system unittest
+{
+ static struct S {
+ int i = 1337;
+ void* p;
+ this(this) { assert(i == 1337); }
+ ~this() { assert(i == 1337); }
+ }
+ Array!S arr;
+ S s;
+ arr ~= s;
+ arr ~= s;
+}
+
+@safe unittest //11459
+{
+ static struct S
+ {
+ bool b;
+ alias b this;
+ }
+ alias A = Array!S;
+ alias B = Array!(shared bool);
+}
+
+@system unittest //11884
+{
+ import std.algorithm.iteration : filter;
+ auto a = Array!int([1, 2, 2].filter!"true"());
+}
+
+@safe unittest //8282
+{
+ auto arr = new Array!int;
+}
+
+@system unittest //6998
+{
+ static int i = 0;
+ class C
+ {
+ int dummy = 1;
+ this(){++i;}
+ ~this(){--i;}
+ }
+
+ assert(i == 0);
+ auto c = new C();
+ assert(i == 1);
+
+ //scope
+ {
+ auto arr = Array!C(c);
+ assert(i == 1);
+ }
+ //Array should not have destroyed the class instance
+ assert(i == 1);
+
+ //Just to make sure the GC doesn't collect before the above test.
+ assert(c.dummy == 1);
+}
+@system unittest //6998-2
+{
+ static class C {int i;}
+ auto c = new C;
+ c.i = 42;
+ Array!C a;
+ a ~= c;
+ a.clear;
+ assert(c.i == 42); //fails
+}
+
+@safe unittest
+{
+ static assert(is(Array!int.Range));
+ static assert(is(Array!int.ConstRange));
+}
+
+@system unittest // const/immutable Array and Ranges
+{
+ static void test(A, R, E, S)()
+ {
+ A a;
+ R r = a[];
+ assert(r.empty);
+ assert(r.length == 0);
+ static assert(is(typeof(r.front) == E));
+ static assert(is(typeof(r.back) == E));
+ static assert(is(typeof(r[0]) == E));
+ static assert(is(typeof(r[]) == S));
+ static assert(is(typeof(r[0 .. 0]) == S));
+ }
+
+ alias A = Array!int;
+
+ test!(A, A.Range, int, A.Range);
+ test!(A, const A.Range, const int, A.ConstRange);
+
+ test!(const A, A.ConstRange, const int, A.ConstRange);
+ test!(const A, const A.ConstRange, const int, A.ConstRange);
+
+ test!(immutable A, A.ImmutableRange, immutable int, A.ImmutableRange);
+ test!(immutable A, const A.ImmutableRange, immutable int, A.ImmutableRange);
+ test!(immutable A, immutable A.ImmutableRange, immutable int,
+ A.ImmutableRange);
+}
+
+// ensure @nogc
+@nogc @system unittest
+{
+ Array!int ai;
+ ai ~= 1;
+ assert(ai.front == 1);
+
+ ai.reserve(10);
+ assert(ai.capacity == 10);
+
+ static immutable arr = [1, 2, 3];
+ ai.insertBack(arr);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Array!bool
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * _Array specialized for `bool`. Packs together values efficiently by
+ * allocating one bit per element.
+ */
+struct Array(T)
+if (is(Unqual!T == bool))
+{
+ import std.exception : enforce;
+ import std.typecons : RefCounted, RefCountedAutoInitialize;
+
+ static immutable uint bitsPerWord = size_t.sizeof * 8;
+ private static struct Data
+ {
+ Array!size_t.Payload _backend;
+ size_t _length;
+ }
+ private RefCounted!(Data, RefCountedAutoInitialize.no) _store;
+
+ private @property ref size_t[] data()
+ {
+ assert(_store.refCountedStore.isInitialized);
+ return _store._backend._payload;
+ }
+
+ /**
+ * Defines the array's primary range.
+ */
+ struct Range
+ {
+ private Array _outer;
+ private size_t _a, _b;
+ /// Range primitives
+ @property Range save()
+ {
+ version (bug4437)
+ {
+ return this;
+ }
+ else
+ {
+ auto copy = this;
+ return copy;
+ }
+ }
+ /// Ditto
+ @property bool empty()
+ {
+ return _a >= _b || _outer.length < _b;
+ }
+ /// Ditto
+ @property T front()
+ {
+ enforce(!empty, "Attempting to access the front of an empty Array");
+ return _outer[_a];
+ }
+ /// Ditto
+ @property void front(bool value)
+ {
+ enforce(!empty, "Attempting to set the front of an empty Array");
+ _outer[_a] = value;
+ }
+ /// Ditto
+ T moveFront()
+ {
+ enforce(!empty, "Attempting to move the front of an empty Array");
+ return _outer.moveAt(_a);
+ }
+ /// Ditto
+ void popFront()
+ {
+ enforce(!empty, "Attempting to popFront an empty Array");
+ ++_a;
+ }
+ /// Ditto
+ @property T back()
+ {
+ enforce(!empty, "Attempting to access the back of an empty Array");
+ return _outer[_b - 1];
+ }
+ /// Ditto
+ @property void back(bool value)
+ {
+ enforce(!empty, "Attempting to set the back of an empty Array");
+ _outer[_b - 1] = value;
+ }
+ /// Ditto
+ T moveBack()
+ {
+ enforce(!empty, "Attempting to move the back of an empty Array");
+ return _outer.moveAt(_b - 1);
+ }
+ /// Ditto
+ void popBack()
+ {
+ enforce(!empty, "Attempting to popBack an empty Array");
+ --_b;
+ }
+ /// Ditto
+ T opIndex(size_t i)
+ {
+ return _outer[_a + i];
+ }
+ /// Ditto
+ void opIndexAssign(T value, size_t i)
+ {
+ _outer[_a + i] = value;
+ }
+ /// Ditto
+ T moveAt(size_t i)
+ {
+ return _outer.moveAt(_a + i);
+ }
+ /// Ditto
+ @property size_t length() const
+ {
+ assert(_a <= _b);
+ return _b - _a;
+ }
+ alias opDollar = length;
+ /// ditto
+ Range opSlice(size_t low, size_t high)
+ {
+ assert(
+ _a <= low && low <= high && high <= _b,
+ "Using out of bounds indexes on an Array"
+ );
+ return Range(_outer, _a + low, _a + high);
+ }
+ }
+
+ /**
+ * Property returning `true` if and only if the array has
+ * no elements.
+ *
+ * Complexity: $(BIGOH 1)
+ */
+ @property bool empty()
+ {
+ return !length;
+ }
+
+ /**
+ * Returns: A duplicate of the array.
+ *
+ * Complexity: $(BIGOH length).
+ */
+ @property Array dup()
+ {
+ Array result;
+ result.insertBack(this[]);
+ return result;
+ }
+
+ /**
+ * Returns the number of elements in the array.
+ *
+ * Complexity: $(BIGOH 1).
+ */
+ @property size_t length() const
+ {
+ return _store.refCountedStore.isInitialized ? _store._length : 0;
+ }
+ size_t opDollar() const
+ {
+ return length;
+ }
+
+ /**
+ * Returns: The maximum number of elements the array can store without
+ * reallocating memory and invalidating iterators upon insertion.
+ *
+ * Complexity: $(BIGOH 1).
+ */
+ @property size_t capacity()
+ {
+ return _store.refCountedStore.isInitialized
+ ? cast(size_t) bitsPerWord * _store._backend.capacity
+ : 0;
+ }
+
+ /**
+ * Ensures sufficient capacity to accommodate `e` _elements.
+ * If `e < capacity`, this method does nothing.
+ *
+ * Postcondition: `capacity >= e`
+ *
+ * Note: If the capacity is increased, one should assume that all
+ * iterators to the elements are invalidated.
+ *
+ * Complexity: at most $(BIGOH length) if `e > capacity`, otherwise $(BIGOH 1).
+ */
+ void reserve(size_t e)
+ {
+ import std.conv : to;
+ _store.refCountedStore.ensureInitialized();
+ _store._backend.reserve(to!size_t((e + bitsPerWord - 1) / bitsPerWord));
+ }
+
+ /**
+ * Returns: A range that iterates over all elements of the array in forward order.
+ *
+ * Complexity: $(BIGOH 1)
+ */
+ Range opSlice()
+ {
+ return Range(this, 0, length);
+ }
+
+
+ /**
+ * Returns: A range that iterates the array between two specified positions.
+ *
+ * Complexity: $(BIGOH 1)
+ */
+ Range opSlice(size_t a, size_t b)
+ {
+ enforce(a <= b && b <= length);
+ return Range(this, a, b);
+ }
+
+ /**
+ * Returns: The first element of the array.
+ *
+ * Precondition: `empty == false`
+ *
+ * Complexity: $(BIGOH 1)
+ *
+ * Throws: `Exception` if the array is empty.
+ */
+ @property bool front()
+ {
+ enforce(!empty);
+ return data.ptr[0] & 1;
+ }
+
+ /// Ditto
+ @property void front(bool value)
+ {
+ enforce(!empty);
+ if (value) data.ptr[0] |= 1;
+ else data.ptr[0] &= ~cast(size_t) 1;
+ }
+
+ /**
+ * Returns: The last element of the array.
+ *
+ * Precondition: `empty == false`
+ *
+ * Complexity: $(BIGOH 1)
+ *
+ * Throws: `Exception` if the array is empty.
+ */
+ @property bool back()
+ {
+ enforce(!empty);
+ return cast(bool)(data.back & (cast(size_t) 1 << ((_store._length - 1) % bitsPerWord)));
+ }
+
+ /// Ditto
+ @property void back(bool value)
+ {
+ enforce(!empty);
+ if (value)
+ {
+ data.back |= (cast(size_t) 1 << ((_store._length - 1) % bitsPerWord));
+ }
+ else
+ {
+ data.back &=
+ ~(cast(size_t) 1 << ((_store._length - 1) % bitsPerWord));
+ }
+ }
+
+ /**
+ * Indexing operators yielding or modifyng the value at the specified index.
+ *
+ * Precondition: `i < length`
+ *
+ * Complexity: $(BIGOH 1)
+ */
+ bool opIndex(size_t i)
+ {
+ auto div = cast(size_t) (i / bitsPerWord);
+ auto rem = i % bitsPerWord;
+ enforce(div < data.length);
+ return cast(bool)(data.ptr[div] & (cast(size_t) 1 << rem));
+ }
+
+ /// ditto
+ void opIndexAssign(bool value, size_t i)
+ {
+ auto div = cast(size_t) (i / bitsPerWord);
+ auto rem = i % bitsPerWord;
+ enforce(div < data.length);
+ if (value) data.ptr[div] |= (cast(size_t) 1 << rem);
+ else data.ptr[div] &= ~(cast(size_t) 1 << rem);
+ }
+
+ /// ditto
+ void opIndexOpAssign(string op)(bool value, size_t i)
+ {
+ auto div = cast(size_t) (i / bitsPerWord);
+ auto rem = i % bitsPerWord;
+ enforce(div < data.length);
+ auto oldValue = cast(bool) (data.ptr[div] & (cast(size_t) 1 << rem));
+ // Do the deed
+ auto newValue = mixin("oldValue "~op~" value");
+ // Write back the value
+ if (newValue != oldValue)
+ {
+ if (newValue) data.ptr[div] |= (cast(size_t) 1 << rem);
+ else data.ptr[div] &= ~(cast(size_t) 1 << rem);
+ }
+ }
+
+ /// Ditto
+ T moveAt(size_t i)
+ {
+ return this[i];
+ }
+
+ /**
+ * Returns: A new array which is a concatenation of `this` and its argument.
+ *
+ * Complexity:
+ * $(BIGOH length + m), where `m` is the number of elements in `stuff`.
+ */
+ Array!bool opBinary(string op, Stuff)(Stuff rhs)
+ if (op == "~")
+ {
+ Array!bool result;
+
+ static if (hasLength!Stuff)
+ result.reserve(length + rhs.length);
+ else static if (is(typeof(rhs[])) && hasLength!(typeof(rhs[])))
+ result.reserve(length + rhs[].length);
+ else static if (isImplicitlyConvertible!(Stuff, bool))
+ result.reserve(length + 1);
+
+ result.insertBack(this[]);
+ result ~= rhs;
+ return result;
+ }
+
+ /**
+ * Forwards to `insertBack`.
+ */
+ Array!bool opOpAssign(string op, Stuff)(Stuff stuff)
+ if (op == "~")
+ {
+ static if (is(typeof(stuff[]))) insertBack(stuff[]);
+ else insertBack(stuff);
+ return this;
+ }
+
+ /**
+ * Removes all the elements from the array and releases allocated memory.
+ *
+ * Postcondition: `empty == true && capacity == 0`
+ *
+ * Complexity: $(BIGOH length)
+ */
+ void clear()
+ {
+ this = Array();
+ }
+
+ /**
+ * Sets the number of elements in the array to `newLength`. If `newLength`
+ * is greater than `length`, the new elements are added to the end of the
+ * array and initialized with `false`.
+ *
+ * Complexity:
+ * Guaranteed $(BIGOH abs(length - newLength)) if `capacity >= newLength`.
+ * If `capacity < newLength` the worst case is $(BIGOH newLength).
+ *
+ * Postcondition: `length == newLength`
+ */
+ @property void length(size_t newLength)
+ {
+ import std.conv : to;
+ _store.refCountedStore.ensureInitialized();
+ auto newDataLength =
+ to!size_t((newLength + bitsPerWord - 1) / bitsPerWord);
+ _store._backend.length = newDataLength;
+ _store._length = newLength;
+ }
+
+ /**
+ * Removes the last element from the array and returns it.
+ * Both stable and non-stable versions behave the same and guarantee
+ * that ranges iterating over the array are never invalidated.
+ *
+ * Precondition: `empty == false`
+ *
+ * Returns: The element removed.
+ *
+ * Complexity: $(BIGOH 1).
+ *
+ * Throws: `Exception` if the array is empty.
+ */
+ T removeAny()
+ {
+ auto result = back;
+ removeBack();
+ return result;
+ }
+
+ /// ditto
+ alias stableRemoveAny = removeAny;
+
+ /**
+ * Inserts the specified elements at the back of the array. `stuff` can be
+ * a value convertible to `bool` or a range of objects convertible to `bool`.
+ *
+ * Returns: The number of elements inserted.
+ *
+ * Complexity:
+ * $(BIGOH length + m) if reallocation takes place, otherwise $(BIGOH m),
+ * where `m` is the number of elements in `stuff`.
+ */
+ size_t insertBack(Stuff)(Stuff stuff)
+ if (is(Stuff : bool))
+ {
+ _store.refCountedStore.ensureInitialized();
+ auto rem = _store._length % bitsPerWord;
+ if (rem)
+ {
+ // Fits within the current array
+ if (stuff)
+ {
+ data[$ - 1] |= (cast(size_t) 1 << rem);
+ }
+ else
+ {
+ data[$ - 1] &= ~(cast(size_t) 1 << rem);
+ }
+ }
+ else
+ {
+ // Need to add more data
+ _store._backend.insertBack(stuff);
+ }
+ ++_store._length;
+ return 1;
+ }
+
+ /// ditto
+ size_t insertBack(Stuff)(Stuff stuff)
+ if (isInputRange!Stuff && is(ElementType!Stuff : bool))
+ {
+ static if (!hasLength!Stuff) size_t result;
+ for (; !stuff.empty; stuff.popFront())
+ {
+ insertBack(stuff.front);
+ static if (!hasLength!Stuff) ++result;
+ }
+ static if (!hasLength!Stuff) return result;
+ else return stuff.length;
+ }
+
+ /// ditto
+ alias stableInsertBack = insertBack;
+
+ /// ditto
+ alias insert = insertBack;
+
+ /// ditto
+ alias stableInsert = insertBack;
+
+ /// ditto
+ alias linearInsert = insertBack;
+
+ /// ditto
+ alias stableLinearInsert = insertBack;
+
+ /**
+ * Removes the value from the back of the array. Both stable and non-stable
+ * versions behave the same and guarantee that ranges iterating over the
+ * array are never invalidated.
+ *
+ * Precondition: `empty == false`
+ *
+ * Complexity: $(BIGOH 1).
+ *
+ * Throws: `Exception` if the array is empty.
+ */
+ void removeBack()
+ {
+ enforce(_store._length);
+ if (_store._length % bitsPerWord)
+ {
+ // Cool, just decrease the length
+ --_store._length;
+ }
+ else
+ {
+ // Reduce the allocated space
+ --_store._length;
+ _store._backend.length = _store._backend.length - 1;
+ }
+ }
+
+ /// ditto
+ alias stableRemoveBack = removeBack;
+
+ /**
+ * Removes `howMany` values from the back of the array. Unlike the
+ * unparameterized versions above, these functions do not throw if
+ * they could not remove `howMany` elements. Instead, if `howMany > n`,
+ * all elements are removed. The returned value is the effective number
+ * of elements removed. Both stable and non-stable versions behave the same
+ * and guarantee that ranges iterating over the array are never invalidated.
+ *
+ * Returns: The number of elements removed.
+ *
+ * Complexity: $(BIGOH howMany).
+ */
+ size_t removeBack(size_t howMany)
+ {
+ if (howMany >= length)
+ {
+ howMany = length;
+ clear();
+ }
+ else
+ {
+ length = length - howMany;
+ }
+ return howMany;
+ }
+
+ /// ditto
+ alias stableRemoveBack = removeBack;
+
+ /**
+ * Inserts `stuff` before, after, or instead range `r`, which must
+ * be a valid range previously extracted from this array. `stuff`
+ * can be a value convertible to `bool` or a range of objects convertible
+ * to `bool`. Both stable and non-stable version behave the same and
+ * guarantee that ranges iterating over the array are never invalidated.
+ *
+ * Returns: The number of values inserted.
+ *
+ * Complexity: $(BIGOH length + m), where `m` is the length of `stuff`.
+ */
+ size_t insertBefore(Stuff)(Range r, Stuff stuff)
+ {
+ import std.algorithm.mutation : bringToFront;
+ // TODO: make this faster, it moves one bit at a time
+ immutable inserted = stableInsertBack(stuff);
+ immutable tailLength = length - inserted;
+ bringToFront(
+ this[r._a .. tailLength],
+ this[tailLength .. length]);
+ return inserted;
+ }
+
+ /// ditto
+ alias stableInsertBefore = insertBefore;
+
+ /// ditto
+ size_t insertAfter(Stuff)(Range r, Stuff stuff)
+ {
+ import std.algorithm.mutation : bringToFront;
+ // TODO: make this faster, it moves one bit at a time
+ immutable inserted = stableInsertBack(stuff);
+ immutable tailLength = length - inserted;
+ bringToFront(
+ this[r._b .. tailLength],
+ this[tailLength .. length]);
+ return inserted;
+ }
+
+ /// ditto
+ alias stableInsertAfter = insertAfter;
+
+ /// ditto
+ size_t replace(Stuff)(Range r, Stuff stuff)
+ if (is(Stuff : bool))
+ {
+ if (!r.empty)
+ {
+ // There is room
+ r.front = stuff;
+ r.popFront();
+ linearRemove(r);
+ }
+ else
+ {
+ // No room, must insert
+ insertBefore(r, stuff);
+ }
+ return 1;
+ }
+
+ /// ditto
+ alias stableReplace = replace;
+
+ /**
+ * Removes all elements belonging to `r`, which must be a range
+ * obtained originally from this array.
+ *
+ * Returns: A range spanning the remaining elements in the array that
+ * initially were right after `r`.
+ *
+ * Complexity: $(BIGOH length)
+ */
+ Range linearRemove(Range r)
+ {
+ import std.algorithm.mutation : copy;
+ copy(this[r._b .. length], this[r._a .. length]);
+ length = length - r.length;
+ return this[r._a .. length];
+ }
+}
+
+@system unittest
+{
+ Array!bool a;
+ assert(a.empty);
+}
+
+@system unittest
+{
+ Array!bool arr;
+ arr.insert([false, false, false, false]);
+ assert(arr.front == false);
+ assert(arr.back == false);
+ assert(arr[1] == false);
+ auto slice = arr[];
+ slice = arr[0 .. $];
+ slice = slice[1 .. $];
+ slice.front = true;
+ slice.back = true;
+ slice[1] = true;
+ assert(slice.front == true);
+ assert(slice.back == true);
+ assert(slice[1] == true);
+ assert(slice.moveFront == true);
+ assert(slice.moveBack == true);
+ assert(slice.moveAt(1) == true);
+}
+
+// issue 16331 - uncomparable values are valid values for an array
+@system unittest
+{
+ double[] values = [double.nan, double.nan];
+ auto arr = Array!double(values);
+}
+
+@nogc @system unittest
+{
+ auto a = Array!int(0, 1, 2);
+ int[3] b = [3, 4, 5];
+ short[3] ci = [0, 1, 0];
+ auto c = Array!short(ci);
+ assert(Array!int(0, 1, 2, 0, 1, 2) == a ~ a);
+ assert(Array!int(0, 1, 2, 3, 4, 5) == a ~ b);
+ assert(Array!int(0, 1, 2, 3) == a ~ 3);
+ assert(Array!int(0, 1, 2, 0, 1, 0) == a ~ c);
+}
+
+@nogc @system unittest
+{
+ auto a = Array!char('a', 'b');
+ assert(Array!char("abc") == a ~ 'c');
+ import std.utf : byCodeUnit;
+ assert(Array!char("abcd") == a ~ "cd".byCodeUnit);
+}
+
+@nogc @system unittest
+{
+ auto a = Array!dchar("ąćę"d);
+ assert(Array!dchar("ąćęϢϖ"d) == a ~ "Ϣϖ"d);
+ wchar x = 'Ï¢';
+ assert(Array!dchar("ąćęϢz"d) == a ~ x ~ 'z');
+}
+
+@system unittest
+{
+ Array!bool a;
+ assert(a.empty);
+ a.insertBack(false);
+ assert(!a.empty);
+}
+
+@system unittest
+{
+ Array!bool a;
+ assert(a.empty);
+ auto b = a.dup;
+ assert(b.empty);
+ a.insertBack(true);
+ assert(b.empty);
+}
+
+@system unittest
+{
+ import std.conv : to;
+ Array!bool a;
+ assert(a.length == 0);
+ a.insert(true);
+ assert(a.length == 1, to!string(a.length));
+}
+
+@system unittest
+{
+ import std.conv : to;
+ Array!bool a;
+ assert(a.capacity == 0);
+ foreach (i; 0 .. 100)
+ {
+ a.insert(true);
+ assert(a.capacity >= a.length, to!string(a.capacity));
+ }
+}
+
+@system unittest
+{
+ Array!bool a;
+ assert(a.capacity == 0);
+ a.reserve(15657);
+ assert(a.capacity >= 15657);
+ a.reserve(100);
+ assert(a.capacity >= 15657);
+}
+
+@system unittest
+{
+ Array!bool a;
+ a.insertBack([true, false, true, true]);
+ assert(a[0 .. 2].length == 2);
+}
+
+@system unittest
+{
+ Array!bool a;
+ a.insertBack([true, false, true, true]);
+ assert(a[].length == 4);
+}
+
+@system unittest
+{
+ Array!bool a;
+ a.insertBack([true, false, true, true]);
+ assert(a.front);
+ a.front = false;
+ assert(!a.front);
+}
+
+@system unittest
+{
+ Array!bool a;
+ a.insertBack([true, false, true, true]);
+ assert(a[].length == 4);
+}
+
+@system unittest
+{
+ Array!bool a;
+ a.insertBack([true, false, true, true]);
+ assert(a.back);
+ a.back = false;
+ assert(!a.back);
+}
+
+@system unittest
+{
+ Array!bool a;
+ a.insertBack([true, false, true, true]);
+ assert(a[0] && !a[1]);
+ a[0] &= a[1];
+ assert(!a[0]);
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ Array!bool a;
+ a.insertBack([true, false, true, true]);
+ Array!bool b;
+ b.insertBack([true, true, false, true]);
+ assert(equal((a ~ b)[],
+ [true, false, true, true, true, true, false, true]));
+ assert((a ~ [true, false])[].equal([true, false, true, true, true, false]));
+ Array!bool c;
+ c.insertBack(true);
+ assert((c ~ false)[].equal([true, false]));
+}
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ Array!bool a;
+ a.insertBack([true, false, true, true]);
+ Array!bool b;
+ a.insertBack([false, true, false, true, true]);
+ a ~= b;
+ assert(equal(
+ a[],
+ [true, false, true, true, false, true, false, true, true]));
+}
+
+@system unittest
+{
+ Array!bool a;
+ a.insertBack([true, false, true, true]);
+ a.clear();
+ assert(a.capacity == 0);
+}
+
+@system unittest
+{
+ Array!bool a;
+ a.length = 1057;
+ assert(a.length == 1057);
+ assert(a.capacity >= a.length);
+ foreach (e; a)
+ {
+ assert(!e);
+ }
+ immutable cap = a.capacity;
+ a.length = 100;
+ assert(a.length == 100);
+ // do not realloc if length decreases
+ assert(a.capacity == cap);
+}
+
+@system unittest
+{
+ Array!bool a;
+ a.length = 1057;
+ assert(!a.removeAny());
+ assert(a.length == 1056);
+ foreach (e; a)
+ {
+ assert(!e);
+ }
+}
+
+@system unittest
+{
+ Array!bool a;
+ for (int i = 0; i < 100; ++i)
+ a.insertBack(true);
+ foreach (e; a)
+ assert(e);
+}
+
+@system unittest
+{
+ Array!bool a;
+ a.length = 1057;
+ assert(a.removeBack(1000) == 1000);
+ assert(a.length == 57);
+ foreach (e; a)
+ {
+ assert(!e);
+ }
+}
+
+@system unittest
+{
+ import std.conv : to;
+ Array!bool a;
+ version (bugxxxx)
+ {
+ a._store.refCountedDebug = true;
+ }
+ a.insertBefore(a[], true);
+ assert(a.length == 1, to!string(a.length));
+ a.insertBefore(a[], false);
+ assert(a.length == 2, to!string(a.length));
+ a.insertBefore(a[1 .. $], true);
+ import std.algorithm.comparison : equal;
+ assert(a[].equal([false, true, true]));
+}
+
+@system unittest
+{
+ import std.conv : to;
+ Array!bool a;
+ a.length = 10;
+ a.insertAfter(a[0 .. 5], true);
+ assert(a.length == 11, to!string(a.length));
+ assert(a[5]);
+}
+@system unittest
+{
+ alias V3 = int[3];
+ V3 v = [1, 2, 3];
+ Array!V3 arr;
+ arr ~= v;
+ assert(arr[0] == [1, 2, 3]);
+}
+@system unittest
+{
+ alias V3 = int[3];
+ V3[2] v = [[1, 2, 3], [4, 5, 6]];
+ Array!V3 arr;
+ arr ~= v;
+ assert(arr[0] == [1, 2, 3]);
+ assert(arr[1] == [4, 5, 6]);
+}
diff --git a/libphobos/src/std/container/binaryheap.d b/libphobos/src/std/container/binaryheap.d
new file mode 100644
index 0000000..4adf604
--- /dev/null
+++ b/libphobos/src/std/container/binaryheap.d
@@ -0,0 +1,595 @@
+/**
+This module provides a $(D BinaryHeap) (aka priority queue)
+adaptor that makes a binary heap out of any user-provided random-access range.
+
+This module is a submodule of $(MREF std, container).
+
+Source: $(PHOBOSSRC std/container/_binaryheap.d)
+
+Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
+
+License: Distributed under the Boost Software License, Version 1.0.
+(See accompanying file LICENSE_1_0.txt or copy at $(HTTP
+boost.org/LICENSE_1_0.txt)).
+
+Authors: $(HTTP erdani.com, Andrei Alexandrescu)
+*/
+module std.container.binaryheap;
+
+import std.range.primitives;
+import std.traits;
+
+public import std.container.util;
+
+///
+@system unittest
+{
+ 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]));
+}
+
+// BinaryHeap
+/**
+Implements a $(HTTP en.wikipedia.org/wiki/Binary_heap, binary heap)
+container on top of a given random-access range type (usually $(D
+T[])) or a random-access container type (usually $(D Array!T)). The
+documentation of $(D BinaryHeap) will refer to the underlying range or
+container as the $(I store) of the heap.
+
+The binary heap induces structure over the underlying store such that
+accessing the largest element (by using the $(D front) property) is a
+$(BIGOH 1) operation and extracting it (by using the $(D
+removeFront()) method) is done fast in $(BIGOH log n) time.
+
+If $(D less) is the less-than operator, which is the default option,
+then $(D BinaryHeap) defines a so-called max-heap that optimizes
+extraction of the $(I largest) elements. To define a min-heap,
+instantiate BinaryHeap with $(D "a > b") as its predicate.
+
+Simply extracting elements from a $(D BinaryHeap) container is
+tantamount to lazily fetching elements of $(D Store) in descending
+order. Extracting elements from the $(D BinaryHeap) to completion
+leaves the underlying store sorted in ascending order but, again,
+yields elements in descending order.
+
+If $(D Store) is a range, the $(D BinaryHeap) cannot grow beyond the
+size of that range. If $(D Store) is a container that supports $(D
+insertBack), the $(D BinaryHeap) may grow by adding elements to the
+container.
+ */
+struct BinaryHeap(Store, alias less = "a < b")
+if (isRandomAccessRange!(Store) || isRandomAccessRange!(typeof(Store.init[])))
+{
+ import std.algorithm.comparison : min;
+ import std.algorithm.mutation : move, swapAt;
+ import std.algorithm.sorting : HeapOps;
+ import std.exception : enforce;
+ import std.functional : binaryFun;
+ import std.typecons : RefCounted, RefCountedAutoInitialize;
+
+ static if (isRandomAccessRange!Store)
+ alias Range = Store;
+ else
+ alias Range = typeof(Store.init[]);
+ alias percolate = HeapOps!(less, Range).percolate;
+ alias buildHeap = HeapOps!(less, Range).buildHeap;
+
+// Really weird @@BUG@@: if you comment out the "private:" label below,
+// std.algorithm can't unittest anymore
+//private:
+
+ // The payload includes the support store and the effective length
+ private static struct Data
+ {
+ Store _store;
+ size_t _length;
+ }
+ private RefCounted!(Data, RefCountedAutoInitialize.no) _payload;
+ // Comparison predicate
+ private alias comp = binaryFun!(less);
+ // Convenience accessors
+ private @property ref Store _store()
+ {
+ assert(_payload.refCountedStore.isInitialized);
+ return _payload._store;
+ }
+ private @property ref size_t _length()
+ {
+ assert(_payload.refCountedStore.isInitialized);
+ return _payload._length;
+ }
+
+ // Asserts that the heap property is respected.
+ private void assertValid()
+ {
+ debug
+ {
+ import std.conv : to;
+ if (!_payload.refCountedStore.isInitialized) return;
+ if (_length < 2) return;
+ for (size_t n = _length - 1; n >= 1; --n)
+ {
+ auto parentIdx = (n - 1) / 2;
+ assert(!comp(_store[parentIdx], _store[n]), to!string(n));
+ }
+ }
+ }
+
+ // @@@BUG@@@: add private here, std.algorithm doesn't unittest anymore
+ /*private*/ void pop(Store store)
+ {
+ assert(!store.empty, "Cannot pop an empty store.");
+ if (store.length == 1) return;
+ auto t1 = store[].moveFront();
+ auto t2 = store[].moveBack();
+ store.front = move(t2);
+ store.back = move(t1);
+ percolate(store[], 0, store.length - 1);
+ }
+
+public:
+
+ /**
+ Converts the store $(D s) into a heap. If $(D initialSize) is
+ specified, only the first $(D initialSize) elements in $(D s)
+ are transformed into a heap, after which the heap can grow up
+ to $(D r.length) (if $(D Store) is a range) or indefinitely (if
+ $(D Store) is a container with $(D insertBack)). Performs
+ $(BIGOH min(r.length, initialSize)) evaluations of $(D less).
+ */
+ this(Store s, size_t initialSize = size_t.max)
+ {
+ acquire(s, initialSize);
+ }
+
+/**
+Takes ownership of a store. After this, manipulating $(D s) may make
+the heap work incorrectly.
+ */
+ void acquire(Store s, size_t initialSize = size_t.max)
+ {
+ _payload.refCountedStore.ensureInitialized();
+ _store = move(s);
+ _length = min(_store.length, initialSize);
+ if (_length < 2) return;
+ buildHeap(_store[]);
+ assertValid();
+ }
+
+/**
+Takes ownership of a store assuming it already was organized as a
+heap.
+ */
+ void assume(Store s, size_t initialSize = size_t.max)
+ {
+ _payload.refCountedStore.ensureInitialized();
+ _store = s;
+ _length = min(_store.length, initialSize);
+ assertValid();
+ }
+
+/**
+Clears the heap. Returns the portion of the store from $(D 0) up to
+$(D length), which satisfies the $(LINK2 https://en.wikipedia.org/wiki/Heap_(data_structure),
+heap property).
+ */
+ auto release()
+ {
+ if (!_payload.refCountedStore.isInitialized)
+ {
+ return typeof(_store[0 .. _length]).init;
+ }
+ assertValid();
+ auto result = _store[0 .. _length];
+ _payload = _payload.init;
+ return result;
+ }
+
+/**
+Returns $(D true) if the heap is _empty, $(D false) otherwise.
+ */
+ @property bool empty()
+ {
+ return !length;
+ }
+
+/**
+Returns a duplicate of the heap. The $(D dup) method is available only if the
+underlying store supports it.
+ */
+ static if (is(typeof((Store s) { return s.dup; }(Store.init)) == Store))
+ {
+ @property BinaryHeap dup()
+ {
+ BinaryHeap result;
+ if (!_payload.refCountedStore.isInitialized) return result;
+ result.assume(_store.dup, length);
+ return result;
+ }
+ }
+
+/**
+Returns the _length of the heap.
+ */
+ @property size_t length()
+ {
+ return _payload.refCountedStore.isInitialized ? _length : 0;
+ }
+
+/**
+Returns the _capacity of the heap, which is the length of the
+underlying store (if the store is a range) or the _capacity of the
+underlying store (if the store is a container).
+ */
+ @property size_t capacity()
+ {
+ if (!_payload.refCountedStore.isInitialized) return 0;
+ static if (is(typeof(_store.capacity) : size_t))
+ {
+ return _store.capacity;
+ }
+ else
+ {
+ return _store.length;
+ }
+ }
+
+/**
+Returns a copy of the _front of the heap, which is the largest element
+according to $(D less).
+ */
+ @property ElementType!Store front()
+ {
+ enforce(!empty, "Cannot call front on an empty heap.");
+ return _store.front;
+ }
+
+/**
+Clears the heap by detaching it from the underlying store.
+ */
+ void clear()
+ {
+ _payload = _payload.init;
+ }
+
+/**
+Inserts $(D value) into the store. If the underlying store is a range
+and $(D length == capacity), throws an exception.
+ */
+ size_t insert(ElementType!Store value)
+ {
+ static if (is(typeof(_store.insertBack(value))))
+ {
+ _payload.refCountedStore.ensureInitialized();
+ if (length == _store.length)
+ {
+ // reallocate
+ _store.insertBack(value);
+ }
+ else
+ {
+ // no reallocation
+ _store[_length] = value;
+ }
+ }
+ else
+ {
+ import std.traits : isDynamicArray;
+ static if (isDynamicArray!Store)
+ {
+ if (length == _store.length)
+ _store.length = (length < 6 ? 8 : length * 3 / 2);
+ _store[_length] = value;
+ }
+ else
+ {
+ // can't grow
+ enforce(length < _store.length,
+ "Cannot grow a heap created over a range");
+ }
+ }
+
+ // sink down the element
+ for (size_t n = _length; n; )
+ {
+ auto parentIdx = (n - 1) / 2;
+ if (!comp(_store[parentIdx], _store[n])) break; // done!
+ // must swap and continue
+ _store.swapAt(parentIdx, n);
+ n = parentIdx;
+ }
+ ++_length;
+ debug(BinaryHeap) assertValid();
+ return 1;
+ }
+
+/**
+Removes the largest element from the heap.
+ */
+ void removeFront()
+ {
+ enforce(!empty, "Cannot call removeFront on an empty heap.");
+ if (_length > 1)
+ {
+ auto t1 = _store[].moveFront();
+ auto t2 = _store[].moveAt(_length - 1);
+ _store.front = move(t2);
+ _store[_length - 1] = move(t1);
+ }
+ --_length;
+ percolate(_store[], 0, _length);
+ }
+
+ /// ditto
+ alias popFront = removeFront;
+
+/**
+Removes the largest element from the heap and returns a copy of
+it. The element still resides in the heap's store. For performance
+reasons you may want to use $(D removeFront) with heaps of objects
+that are expensive to copy.
+ */
+ ElementType!Store removeAny()
+ {
+ removeFront();
+ return _store[_length];
+ }
+
+/**
+Replaces the largest element in the store with $(D value).
+ */
+ void replaceFront(ElementType!Store value)
+ {
+ // must replace the top
+ assert(!empty, "Cannot call replaceFront on an empty heap.");
+ _store.front = value;
+ percolate(_store[], 0, _length);
+ debug(BinaryHeap) assertValid();
+ }
+
+/**
+If the heap has room to grow, inserts $(D value) into the store and
+returns $(D true). Otherwise, if $(D less(value, front)), calls $(D
+replaceFront(value)) and returns again $(D true). Otherwise, leaves
+the heap unaffected and returns $(D false). This method is useful in
+scenarios where the smallest $(D k) elements of a set of candidates
+must be collected.
+ */
+ bool conditionalInsert(ElementType!Store value)
+ {
+ _payload.refCountedStore.ensureInitialized();
+ if (_length < _store.length)
+ {
+ insert(value);
+ return true;
+ }
+
+ assert(!_store.empty, "Cannot replace front of an empty heap.");
+ if (!comp(value, _store.front)) return false; // value >= largest
+ _store.front = value;
+
+ percolate(_store[], 0, _length);
+ debug(BinaryHeap) assertValid();
+ return true;
+ }
+
+/**
+Swapping is allowed if the heap is full. If $(D less(value, front)), the
+method exchanges store.front and value and returns $(D true). Otherwise, it
+leaves the heap unaffected and returns $(D false).
+ */
+ bool conditionalSwap(ref ElementType!Store value)
+ {
+ _payload.refCountedStore.ensureInitialized();
+ assert(_length == _store.length);
+ assert(!_store.empty, "Cannot swap front of an empty heap.");
+
+ if (!comp(value, _store.front)) return false; // value >= largest
+
+ import std.algorithm.mutation : swap;
+ swap(_store.front, value);
+
+ percolate(_store[], 0, _length);
+ debug(BinaryHeap) assertValid();
+
+ return true;
+ }
+}
+
+/// Example from "Introduction to Algorithms" Cormen et al, p 146
+@system unittest
+{
+ 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 ]));
+}
+
+/// $(D BinaryHeap) implements the standard input range interface, allowing
+/// lazy iteration of the underlying range in descending order.
+@system unittest
+{
+ 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]));
+}
+
+/**
+Convenience function that returns a $(D BinaryHeap!Store) object
+initialized with $(D s) and $(D initialSize).
+ */
+BinaryHeap!(Store, less) heapify(alias less = "a < b", Store)(Store s,
+ size_t initialSize = size_t.max)
+{
+
+ return BinaryHeap!(Store, less)(s, initialSize);
+}
+
+///
+@system unittest
+{
+ 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
+{
+ // Test range interface.
+ import std.algorithm.comparison : equal;
+ int[] a = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7];
+ auto h = heapify(a);
+ static assert(isInputRange!(typeof(h)));
+ assert(h.equal([16, 14, 10, 9, 8, 7, 4, 3, 2, 1]));
+}
+
+@system unittest // 15675
+{
+ import std.container.array : Array;
+
+ Array!int elements = [1, 2, 10, 12];
+ auto heap = heapify(elements);
+ assert(heap.front == 12);
+}
+
+@system unittest // 16072
+{
+ auto q = heapify!"a > b"([2, 4, 5]);
+ q.insert(1);
+ q.insert(6);
+ assert(q.front == 1);
+
+ // test more multiple grows
+ int[] arr;
+ auto r = heapify!"a < b"(arr);
+ foreach (i; 0 .. 100)
+ r.insert(i);
+
+ assert(r.front == 99);
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ int[] a = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7];
+ auto heap = heapify(a);
+ auto dup = heap.dup();
+ assert(dup.equal([16, 14, 10, 9, 8, 7, 4, 3, 2, 1]));
+}
+
+@safe unittest
+{
+ static struct StructWithoutDup
+ {
+ int[] a;
+ @disable StructWithoutDup dup()
+ {
+ StructWithoutDup d;
+ return d;
+ }
+ alias a this;
+ }
+
+ // Assert Binary heap can be created when Store doesn't have dup
+ // if dup is not used.
+ assert(__traits(compiles, ()
+ {
+ auto s = StructWithoutDup([1,2]);
+ auto h = heapify(s);
+ }));
+
+ // Assert dup can't be used on BinaryHeaps when Store doesn't have dup
+ assert(!__traits(compiles, ()
+ {
+ auto s = StructWithoutDup([1,2]);
+ auto h = heapify(s);
+ h.dup();
+ }));
+}
+
+@safe unittest
+{
+ static struct StructWithDup
+ {
+ int[] a;
+ StructWithDup dup()
+ {
+ StructWithDup d;
+ return d;
+ }
+ alias a this;
+ }
+
+ // Assert dup can be used on BinaryHeaps when Store has dup
+ assert(__traits(compiles, ()
+ {
+ auto s = StructWithDup([1, 2]);
+ auto h = heapify(s);
+ h.dup();
+ }));
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.internal.test.dummyrange;
+
+ alias RefRange = DummyRange!(ReturnBy.Reference, Length.Yes, RangeType.Random);
+
+ RefRange a;
+ RefRange b;
+ a.reinit();
+ b.reinit();
+
+ auto heap = heapify(a);
+ foreach (ref elem; b)
+ {
+ heap.conditionalSwap(elem);
+ }
+
+ assert(equal(heap, [ 5, 5, 4, 4, 3, 3, 2, 2, 1, 1]));
+ assert(equal(b, [10, 9, 8, 7, 6, 6, 7, 8, 9, 10]));
+}
+
+@system unittest // Issue 17314
+{
+ import std.algorithm.comparison : equal;
+ int[] a = [5];
+ auto heap = heapify(a);
+ heap.insert(6);
+ assert(equal(heap, [6, 5]));
+}
diff --git a/libphobos/src/std/container/dlist.d b/libphobos/src/std/container/dlist.d
new file mode 100644
index 0000000..633371f
--- /dev/null
+++ b/libphobos/src/std/container/dlist.d
@@ -0,0 +1,1039 @@
+/**
+This module implements a generic doubly-linked list container.
+It can be used as a queue, dequeue or stack.
+
+This module is a submodule of $(MREF std, container).
+
+Source: $(PHOBOSSRC std/container/_dlist.d)
+
+Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
+
+License: Distributed under the Boost Software License, Version 1.0.
+(See accompanying file LICENSE_1_0.txt or copy at $(HTTP
+boost.org/LICENSE_1_0.txt)).
+
+Authors: $(HTTP erdani.com, Andrei Alexandrescu)
+
+$(SCRIPT inhibitQuickIndex = 1;)
+*/
+module std.container.dlist;
+
+///
+@safe unittest
+{
+ 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]));
+}
+
+import std.range.primitives;
+import std.traits;
+
+public import std.container.util;
+
+/+
+A DList Node without payload. Used to handle the sentinel node (henceforth "sentinode").
+
+Also used for parts of the code that don't depend on the payload type.
+ +/
+private struct BaseNode
+{
+ private BaseNode* _prev = null;
+ private BaseNode* _next = null;
+
+ /+
+ Gets the payload associated with this node.
+ This is trusted because all nodes are associated with a payload, even
+ the sentinel node.
+ It is also not possible to mix Nodes in DLists of different types.
+ This function is implemented as a member function here, as UFCS does not
+ work with pointers.
+ +/
+ ref inout(T) getPayload(T)() inout @trusted
+ {
+ return (cast(inout(DList!T.PayNode)*)&this)._payload;
+ }
+
+ // Helper: Given nodes p and n, connects them.
+ static void connect(BaseNode* p, BaseNode* n) @safe nothrow pure
+ {
+ p._next = n;
+ n._prev = p;
+ }
+}
+
+/+
+The base DList Range. Contains Range primitives that don't depend on payload type.
+ +/
+private struct DRange
+{
+ @safe unittest
+ {
+ static assert(isBidirectionalRange!DRange);
+ static assert(is(ElementType!DRange == BaseNode*));
+ }
+
+nothrow @safe pure:
+ private BaseNode* _first;
+ private BaseNode* _last;
+
+ private this(BaseNode* first, BaseNode* last)
+ {
+ assert((first is null) == (last is null), "Dlist.Range.this: Invalid arguments");
+ _first = first;
+ _last = last;
+ }
+ private this(BaseNode* n)
+ {
+ this(n, n);
+ }
+
+ @property
+ bool empty() const
+ {
+ assert((_first is null) == (_last is null), "DList.Range: Invalidated state");
+ return !_first;
+ }
+
+ @property BaseNode* front()
+ {
+ assert(!empty, "DList.Range.front: Range is empty");
+ return _first;
+ }
+
+ void popFront()
+ {
+ assert(!empty, "DList.Range.popFront: Range is empty");
+ if (_first is _last)
+ {
+ _first = _last = null;
+ }
+ else
+ {
+ assert(_first._next && _first is _first._next._prev, "DList.Range: Invalidated state");
+ _first = _first._next;
+ }
+ }
+
+ @property BaseNode* back()
+ {
+ assert(!empty, "DList.Range.front: Range is empty");
+ return _last;
+ }
+
+ void popBack()
+ {
+ assert(!empty, "DList.Range.popBack: Range is empty");
+ if (_first is _last)
+ {
+ _first = _last = null;
+ }
+ else
+ {
+ assert(_last._prev && _last is _last._prev._next, "DList.Range: Invalidated state");
+ _last = _last._prev;
+ }
+ }
+
+ /// Forward range primitive.
+ @property DRange save() { return this; }
+}
+
+/**
+Implements a doubly-linked list.
+
+$(D DList) uses reference semantics.
+ */
+struct DList(T)
+{
+ import std.range : Take;
+
+ /*
+ A Node with a Payload. A PayNode.
+ */
+ struct PayNode
+ {
+ BaseNode _base;
+ alias _base this;
+
+ T _payload = T.init;
+
+ inout(BaseNode)* asBaseNode() inout @trusted
+ {
+ return &_base;
+ }
+ }
+
+ //The sentinel node
+ private BaseNode* _root;
+
+ private
+ {
+ //Construct as new PayNode, and returns it as a BaseNode.
+ static BaseNode* createNode(Stuff)(auto ref Stuff arg, BaseNode* prev = null, BaseNode* next = null)
+ {
+ return (new PayNode(BaseNode(prev, next), arg)).asBaseNode();
+ }
+
+ void initialize() nothrow @safe pure
+ {
+ if (_root) return;
+ //Note: We allocate a PayNode for safety reasons.
+ _root = (new PayNode()).asBaseNode();
+ _root._next = _root._prev = _root;
+ }
+ ref inout(BaseNode*) _first() @property @safe nothrow pure inout
+ {
+ assert(_root);
+ return _root._next;
+ }
+ ref inout(BaseNode*) _last() @property @safe nothrow pure inout
+ {
+ assert(_root);
+ return _root._prev;
+ }
+ } //end private
+
+/**
+Constructor taking a number of nodes
+ */
+ this(U)(U[] values...) if (isImplicitlyConvertible!(U, T))
+ {
+ insertBack(values);
+ }
+
+/**
+Constructor taking an input range
+ */
+ this(Stuff)(Stuff stuff)
+ if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
+ {
+ insertBack(stuff);
+ }
+
+/**
+Comparison for equality.
+
+Complexity: $(BIGOH min(n, n1)) where $(D n1) is the number of
+elements in $(D rhs).
+ */
+ bool opEquals()(ref const DList rhs) const
+ if (is(typeof(front == front)))
+ {
+ const lhs = this;
+ const lroot = lhs._root;
+ const rroot = rhs._root;
+
+ if (lroot is rroot) return true;
+ if (lroot is null) return rroot is rroot._next;
+ if (rroot is null) return lroot is lroot._next;
+
+ const(BaseNode)* pl = lhs._first;
+ const(BaseNode)* pr = rhs._first;
+ while (true)
+ {
+ if (pl is lroot) return pr is rroot;
+ if (pr is rroot) return false;
+
+ // !== because of NaN
+ if (!(pl.getPayload!T() == pr.getPayload!T())) return false;
+
+ pl = pl._next;
+ pr = pr._next;
+ }
+ }
+
+ /**
+ Defines the container's primary range, which embodies a bidirectional range.
+ */
+ struct Range
+ {
+ static assert(isBidirectionalRange!Range);
+
+ DRange _base;
+ alias _base this;
+
+ private this(BaseNode* first, BaseNode* last)
+ {
+ _base = DRange(first, last);
+ }
+ private this(BaseNode* n)
+ {
+ this(n, n);
+ }
+
+ @property ref T front()
+ {
+ return _base.front.getPayload!T();
+ }
+
+ @property ref T back()
+ {
+ return _base.back.getPayload!T();
+ }
+
+ //Note: shadows base DRange.save.
+ //Necessary for static covariance.
+ @property Range save() { return this; }
+ }
+
+/**
+Property returning $(D true) if and only if the container has no
+elements.
+
+Complexity: $(BIGOH 1)
+ */
+ bool empty() @property const nothrow
+ {
+ return _root is null || _root is _first;
+ }
+
+/**
+Removes all contents from the $(D DList).
+
+Postcondition: $(D empty)
+
+Complexity: $(BIGOH 1)
+ */
+ void clear()
+ {
+ //remove actual elements.
+ remove(this[]);
+ }
+
+/**
+Duplicates the container. The elements themselves are not transitively
+duplicated.
+
+Complexity: $(BIGOH n).
+ */
+ @property DList dup()
+ {
+ return DList(this[]);
+ }
+
+/**
+Returns a range that iterates over all elements of the container, in
+forward order.
+
+Complexity: $(BIGOH 1)
+ */
+ Range opSlice()
+ {
+ if (empty)
+ return Range(null, null);
+ else
+ return Range(_first, _last);
+ }
+
+/**
+Forward to $(D opSlice().front).
+
+Complexity: $(BIGOH 1)
+ */
+ @property ref inout(T) front() inout
+ {
+ assert(!empty, "DList.front: List is empty");
+ return _first.getPayload!T();
+ }
+
+/**
+Forward to $(D opSlice().back).
+
+Complexity: $(BIGOH 1)
+ */
+ @property ref inout(T) back() inout
+ {
+ assert(!empty, "DList.back: List is empty");
+ return _last.getPayload!T();
+ }
+
+/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
+/+ BEGIN CONCAT FUNCTIONS HERE +/
+/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
+
+/**
+Returns a new $(D DList) that's the concatenation of $(D this) and its
+argument $(D rhs).
+ */
+ DList opBinary(string op, Stuff)(Stuff rhs)
+ if (op == "~" && is(typeof(insertBack(rhs))))
+ {
+ auto ret = this.dup;
+ ret.insertBack(rhs);
+ return ret;
+ }
+
+/**
+Returns a new $(D DList) that's the concatenation of the argument $(D lhs)
+and $(D this).
+ */
+ DList opBinaryRight(string op, Stuff)(Stuff lhs)
+ if (op == "~" && is(typeof(insertFront(lhs))))
+ {
+ auto ret = this.dup;
+ ret.insertFront(lhs);
+ return ret;
+ }
+
+/**
+Appends the contents of the argument $(D rhs) into $(D this).
+ */
+ DList opOpAssign(string op, Stuff)(Stuff rhs)
+ if (op == "~" && is(typeof(insertBack(rhs))))
+ {
+ insertBack(rhs);
+ return this;
+ }
+
+/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
+/+ BEGIN INSERT FUNCTIONS HERE +/
+/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
+
+/**
+Inserts $(D stuff) to the front/back of the container. $(D stuff) can be a
+value convertible to $(D T) or a range of objects convertible to $(D
+T). The stable version behaves the same, but guarantees that ranges
+iterating over the container are never invalidated.
+
+Returns: The number of elements inserted
+
+Complexity: $(BIGOH log(n))
+ */
+ size_t insertFront(Stuff)(Stuff stuff)
+ {
+ initialize();
+ return insertAfterNode(_root, stuff);
+ }
+
+ /// ditto
+ size_t insertBack(Stuff)(Stuff stuff)
+ {
+ initialize();
+ return insertBeforeNode(_root, stuff);
+ }
+
+ /// ditto
+ alias insert = insertBack;
+
+ /// ditto
+ alias stableInsert = insert;
+
+ /// ditto
+ alias stableInsertFront = insertFront;
+
+ /// ditto
+ alias stableInsertBack = insertBack;
+
+/**
+Inserts $(D stuff) after range $(D r), which must be a non-empty range
+previously extracted from this container.
+
+$(D stuff) can be a value convertible to $(D T) or a range of objects
+convertible to $(D T). The stable version behaves the same, but
+guarantees that ranges iterating over the container are never
+invalidated.
+
+Returns: The number of values inserted.
+
+Complexity: $(BIGOH k + m), where $(D k) is the number of elements in
+$(D r) and $(D m) is the length of $(D stuff).
+ */
+ size_t insertBefore(Stuff)(Range r, Stuff stuff)
+ {
+ if (r._first)
+ return insertBeforeNode(r._first, stuff);
+ else
+ {
+ initialize();
+ return insertAfterNode(_root, stuff);
+ }
+ }
+
+ /// ditto
+ alias stableInsertBefore = insertBefore;
+
+ /// ditto
+ size_t insertAfter(Stuff)(Range r, Stuff stuff)
+ {
+ if (r._last)
+ return insertAfterNode(r._last, stuff);
+ else
+ {
+ initialize();
+ return insertBeforeNode(_root, stuff);
+ }
+ }
+
+ /// ditto
+ alias stableInsertAfter = insertAfter;
+
+/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
+/+ BEGIN REMOVE FUNCTIONS HERE +/
+/+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
+
+/**
+Picks one value in an unspecified position in the container, removes
+it from the container, and returns it. The stable version behaves the same,
+but guarantees that ranges iterating over the container are never invalidated.
+
+Precondition: $(D !empty)
+
+Returns: The element removed.
+
+Complexity: $(BIGOH 1).
+ */
+ T removeAny()
+ {
+ import std.algorithm.mutation : move;
+
+ assert(!empty, "DList.removeAny: List is empty");
+ auto result = move(back);
+ removeBack();
+ return result;
+ }
+ /// ditto
+ alias stableRemoveAny = removeAny;
+
+/**
+Removes the value at the front/back of the container. The stable version
+behaves the same, but guarantees that ranges iterating over the
+container are never invalidated.
+
+Precondition: $(D !empty)
+
+Complexity: $(BIGOH 1).
+ */
+ void removeFront()
+ {
+ assert(!empty, "DList.removeFront: List is empty");
+ assert(_root is _first._prev, "DList: Inconsistent state");
+ BaseNode.connect(_root, _first._next);
+ }
+
+ /// ditto
+ alias stableRemoveFront = removeFront;
+
+ /// ditto
+ void removeBack()
+ {
+ assert(!empty, "DList.removeBack: List is empty");
+ assert(_last._next is _root, "DList: Inconsistent state");
+ BaseNode.connect(_last._prev, _root);
+ }
+
+ /// ditto
+ alias stableRemoveBack = removeBack;
+
+/**
+Removes $(D howMany) values at the front or back of the
+container. Unlike the unparameterized versions above, these functions
+do not throw if they could not remove $(D howMany) elements. Instead,
+if $(D howMany > n), all elements are removed. The returned value is
+the effective number of elements removed. The stable version behaves
+the same, but guarantees that ranges iterating over the container are
+never invalidated.
+
+Returns: The number of elements removed
+
+Complexity: $(BIGOH howMany).
+ */
+ size_t removeFront(size_t howMany)
+ {
+ if (!_root) return 0;
+ size_t result;
+ auto p = _first;
+ while (p !is _root && result < howMany)
+ {
+ p = p._next;
+ ++result;
+ }
+ BaseNode.connect(_root, p);
+ return result;
+ }
+
+ /// ditto
+ alias stableRemoveFront = removeFront;
+
+ /// ditto
+ size_t removeBack(size_t howMany)
+ {
+ if (!_root) return 0;
+ size_t result;
+ auto p = _last;
+ while (p !is _root && result < howMany)
+ {
+ p = p._prev;
+ ++result;
+ }
+ BaseNode.connect(p, _root);
+ return result;
+ }
+
+ /// ditto
+ alias stableRemoveBack = removeBack;
+
+/**
+Removes all elements belonging to $(D r), which must be a range
+obtained originally from this container.
+
+Returns: A range spanning the remaining elements in the container that
+initially were right after $(D r).
+
+Complexity: $(BIGOH 1)
+ */
+ Range remove(Range r)
+ {
+ if (r.empty)
+ return r;
+
+ assert(_root !is null, "Cannot remove from an un-initialized List");
+ assert(r._first, "Remove: Range is empty");
+
+ BaseNode.connect(r._first._prev, r._last._next);
+ auto after = r._last._next;
+ if (after is _root)
+ return Range(null, null);
+ else
+ return Range(after, _last);
+ }
+
+ /// ditto
+ Range linearRemove(Range r)
+ {
+ return remove(r);
+ }
+
+/**
+Removes first element of $(D r), wich must be a range obtained originally
+from this container, from both DList instance and range $(D r).
+
+Compexity: $(BIGOH 1)
+ */
+ void popFirstOf(ref Range r)
+ {
+ assert(_root !is null, "Cannot remove from an un-initialized List");
+ assert(r._first, "popFirstOf: Range is empty");
+ auto prev = r._first._prev;
+ auto next = r._first._next;
+ r.popFront();
+ BaseNode.connect(prev, next);
+ }
+
+/**
+Removes last element of $(D r), wich must be a range obtained originally
+from this container, from both DList instance and range $(D r).
+
+Compexity: $(BIGOH 1)
+ */
+ void popLastOf(ref Range r)
+ {
+ assert(_root !is null, "Cannot remove from an un-initialized List");
+ assert(r._first, "popLastOf: Range is empty");
+ auto prev = r._last._prev;
+ auto next = r._last._next;
+ r.popBack();
+ BaseNode.connect(prev, next);
+ }
+
+/**
+$(D linearRemove) functions as $(D remove), but also accepts ranges that are
+result the of a $(D take) operation. This is a convenient way to remove a
+fixed amount of elements from the range.
+
+Complexity: $(BIGOH r.walkLength)
+ */
+ Range linearRemove(Take!Range r)
+ {
+ assert(_root !is null, "Cannot remove from an un-initialized List");
+ assert(r.source._first, "Remove: Range is empty");
+
+ BaseNode* first = r.source._first;
+ BaseNode* last = null;
+ do
+ {
+ last = r.source._first;
+ r.popFront();
+ } while ( !r.empty );
+
+ return remove(Range(first, last));
+ }
+
+ /// ditto
+ alias stableRemove = remove;
+ /// ditto
+ alias stableLinearRemove = linearRemove;
+
+private:
+
+ // Helper: Inserts stuff before the node n.
+ size_t insertBeforeNode(Stuff)(BaseNode* n, ref Stuff stuff)
+ if (isImplicitlyConvertible!(Stuff, T))
+ {
+ auto p = createNode(stuff, n._prev, n);
+ n._prev._next = p;
+ n._prev = p;
+ return 1;
+ }
+ // ditto
+ size_t insertBeforeNode(Stuff)(BaseNode* n, ref Stuff stuff)
+ if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
+ {
+ if (stuff.empty) return 0;
+ size_t result;
+ Range r = createRange(stuff, result);
+ BaseNode.connect(n._prev, r._first);
+ BaseNode.connect(r._last, n);
+ return result;
+ }
+
+ // Helper: Inserts stuff after the node n.
+ size_t insertAfterNode(Stuff)(BaseNode* n, ref Stuff stuff)
+ if (isImplicitlyConvertible!(Stuff, T))
+ {
+ auto p = createNode(stuff, n, n._next);
+ n._next._prev = p;
+ n._next = p;
+ return 1;
+ }
+ // ditto
+ size_t insertAfterNode(Stuff)(BaseNode* n, ref Stuff stuff)
+ if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
+ {
+ if (stuff.empty) return 0;
+ size_t result;
+ Range r = createRange(stuff, result);
+ BaseNode.connect(r._last, n._next);
+ BaseNode.connect(n, r._first);
+ return result;
+ }
+
+ // Helper: Creates a chain of nodes from the range stuff.
+ Range createRange(Stuff)(ref Stuff stuff, ref size_t result)
+ {
+ BaseNode* first = createNode(stuff.front);
+ BaseNode* last = first;
+ ++result;
+ for ( stuff.popFront() ; !stuff.empty ; stuff.popFront() )
+ {
+ auto p = createNode(stuff.front, last);
+ last = last._next = p;
+ ++result;
+ }
+ return Range(first, last);
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ //Tests construction signatures
+ alias IntList = DList!int;
+ auto a0 = IntList();
+ auto a1 = IntList(0);
+ auto a2 = IntList(0, 1);
+ auto a3 = IntList([0]);
+ auto a4 = IntList([0, 1]);
+
+ assert(a0[].empty);
+ assert(equal(a1[], [0]));
+ assert(equal(a2[], [0, 1]));
+ assert(equal(a3[], [0]));
+ assert(equal(a4[], [0, 1]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ alias IntList = DList!int;
+ IntList list = IntList([0,1,2,3]);
+ assert(equal(list[],[0,1,2,3]));
+ list.insertBack([4,5,6,7]);
+ assert(equal(list[],[0,1,2,3,4,5,6,7]));
+
+ list = IntList();
+ list.insertFront([0,1,2,3]);
+ assert(equal(list[],[0,1,2,3]));
+ list.insertFront([4,5,6,7]);
+ assert(equal(list[],[4,5,6,7,0,1,2,3]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : take;
+
+ alias IntList = DList!int;
+ IntList list = IntList([0,1,2,3]);
+ auto range = list[];
+ for ( ; !range.empty; range.popFront())
+ {
+ int item = range.front;
+ if (item == 2)
+ {
+ list.stableLinearRemove(take(range, 1));
+ break;
+ }
+ }
+ assert(equal(list[],[0,1,3]));
+
+ list = IntList([0,1,2,3]);
+ range = list[];
+ for ( ; !range.empty; range.popFront())
+ {
+ int item = range.front;
+ if (item == 2)
+ {
+ list.stableLinearRemove(take(range,2));
+ break;
+ }
+ }
+ assert(equal(list[],[0,1]));
+
+ list = IntList([0,1,2,3]);
+ range = list[];
+ for ( ; !range.empty; range.popFront())
+ {
+ int item = range.front;
+ if (item == 0)
+ {
+ list.stableLinearRemove(take(range,2));
+ break;
+ }
+ }
+ assert(equal(list[],[2,3]));
+
+ list = IntList([0,1,2,3]);
+ range = list[];
+ for ( ; !range.empty; range.popFront())
+ {
+ int item = range.front;
+ if (item == 1)
+ {
+ list.stableLinearRemove(take(range,2));
+ break;
+ }
+ }
+ assert(equal(list[],[0,3]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto dl = DList!int([1, 2, 3, 4, 5]);
+ auto r = dl[];
+ r.popFront();
+ dl.popFirstOf(r);
+ assert(equal(dl[], [1, 3, 4, 5]));
+ assert(equal(r, [3, 4, 5]));
+ r.popBack();
+ dl.popLastOf(r);
+ assert(equal(dl[], [1, 3, 5]));
+ assert(equal(r, [3]));
+ dl = DList!int([0]);
+ r = dl[];
+ dl.popFirstOf(r);
+ assert(dl.empty);
+ dl = DList!int([0]);
+ r = dl[];
+ dl.popLastOf(r);
+ assert(dl.empty);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto dl = DList!string(["a", "b", "d"]);
+ dl.insertAfter(dl[], "e"); // insert at the end
+ assert(equal(dl[], ["a", "b", "d", "e"]));
+ auto dlr = dl[];
+ dlr.popBack(); dlr.popBack();
+ dl.insertAfter(dlr, "c"); // insert after "b"
+ assert(equal(dl[], ["a", "b", "c", "d", "e"]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto dl = DList!string(["a", "b", "d"]);
+ dl.insertBefore(dl[], "e"); // insert at the front
+ assert(equal(dl[], ["e", "a", "b", "d"]));
+ auto dlr = dl[];
+ dlr.popFront(); dlr.popFront();
+ dl.insertBefore(dlr, "c"); // insert before "b"
+ assert(equal(dl[], ["e", "a", "c", "b", "d"]));
+}
+
+@safe unittest
+{
+ auto d = DList!int([1, 2, 3]);
+ d.front = 5; //test frontAssign
+ assert(d.front == 5);
+ auto r = d[];
+ r.back = 1;
+ assert(r.back == 1);
+}
+
+// Issue 8895
+@safe unittest
+{
+ auto a = make!(DList!int)(1,2,3,4);
+ auto b = make!(DList!int)(1,2,3,4);
+ auto c = make!(DList!int)(1,2,3,5);
+ auto d = make!(DList!int)(1,2,3,4,5);
+ assert(a == b); // this better terminate!
+ assert(!(a == c));
+ assert(!(a == d));
+}
+
+@safe unittest
+{
+ auto d = DList!int([1, 2, 3]);
+ d.front = 5; //test frontAssign
+ assert(d.front == 5);
+ auto r = d[];
+ r.back = 1;
+ assert(r.back == 1);
+}
+
+@safe unittest
+{
+ auto a = DList!int();
+ assert(a.removeFront(10) == 0);
+ a.insert([1, 2, 3]);
+ assert(a.removeFront(10) == 3);
+ assert(a[].empty);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ //Verify all flavors of ~
+ auto a = DList!int();
+ auto b = DList!int();
+ auto c = DList!int([1, 2, 3]);
+ auto d = DList!int([4, 5, 6]);
+
+ assert((a ~ b[])[].empty);
+ assert((c ~ d[])[].equal([1, 2, 3, 4, 5, 6]));
+ assert(c[].equal([1, 2, 3]));
+ assert(d[].equal([4, 5, 6]));
+
+ assert((c[] ~ d)[].equal([1, 2, 3, 4, 5, 6]));
+ assert(c[].equal([1, 2, 3]));
+ assert(d[].equal([4, 5, 6]));
+
+ a~=c[];
+ assert(a[].equal([1, 2, 3]));
+ assert(c[].equal([1, 2, 3]));
+
+ a~=d[];
+ assert(a[].equal([1, 2, 3, 4, 5, 6]));
+ assert(d[].equal([4, 5, 6]));
+
+ a~=[7, 8, 9];
+ assert(a[].equal([1, 2, 3, 4, 5, 6, 7, 8, 9]));
+
+ //trick test:
+ auto r = c[];
+ c.removeFront();
+ c.removeBack();
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ //8905
+ auto a = DList!int([1, 2, 3, 4]);
+ auto r = a[];
+ a.stableRemoveBack();
+ a.stableInsertBack(7);
+ assert(a[].equal([1, 2, 3, 7]));
+}
+
+@safe unittest //12566
+{
+ auto dl2 = DList!int([2,7]);
+ dl2.removeFront();
+ assert(dl2[].walkLength == 1);
+ dl2.removeBack();
+ assert(dl2.empty, "not empty?!");
+}
+
+@safe unittest //13076
+{
+ DList!int list;
+ assert(list.empty);
+ list.clear();
+}
+
+@safe unittest //13425
+{
+ import std.range : drop, take;
+ auto list = DList!int([1,2,3,4,5]);
+ auto r = list[].drop(4); // r is a view of the last element of list
+ assert(r.front == 5 && r.walkLength == 1);
+ r = list.linearRemove(r.take(1));
+ assert(r.empty); // fails
+}
+
+@safe unittest //14300
+{
+ interface ITest {}
+ static class Test : ITest {}
+
+ DList!ITest().insertBack(new Test());
+}
+
+@safe unittest //15263
+{
+ import std.range : iota;
+ auto a = DList!int();
+ a.insertFront(iota(0, 5)); // can insert range with non-ref front
+ assert(a.front == 0 && a.back == 4);
+}
diff --git a/libphobos/src/std/container/package.d b/libphobos/src/std/container/package.d
new file mode 100644
index 0000000..fa00505
--- /dev/null
+++ b/libphobos/src/std/container/package.d
@@ -0,0 +1,1156 @@
+// Written in the D programming language.
+
+/**
+This module defines generic containers.
+
+Construction:
+
+To implement the different containers both struct and class based
+approaches have been used. $(REF make, std,_container,util) allows for
+uniform construction with either approach.
+
+---
+import std.container;
+// Construct a red-black tree and an array both containing the values 1, 2, 3.
+// RedBlackTree should typically be allocated using `new`
+RedBlackTree!int rbTree = new RedBlackTree!int(1, 2, 3);
+// But `new` should not be used with Array
+Array!int array = Array!int(1, 2, 3);
+// `make` hides the differences
+RedBlackTree!int rbTree2 = make!(RedBlackTree!int)(1, 2, 3);
+Array!int array2 = make!(Array!int)(1, 2, 3);
+---
+
+Note that $(D make) can infer the element type from the given arguments.
+
+---
+import std.container;
+auto rbTree = make!RedBlackTree(1, 2, 3); // RedBlackTree!int
+auto array = make!Array("1", "2", "3"); // Array!string
+---
+
+Reference_semantics:
+
+All containers have reference semantics, which means that after
+assignment both variables refer to the same underlying data.
+
+To make a copy of a _container, use the $(D c._dup) _container primitive.
+---
+import std.container, std.range;
+Array!int originalArray = make!(Array!int)(1, 2, 3);
+Array!int secondArray = originalArray;
+assert(equal(originalArray[], secondArray[]));
+
+// changing one instance changes the other one as well!
+originalArray[0] = 12;
+assert(secondArray[0] == 12);
+
+// secondArray now refers to an independent copy of originalArray
+secondArray = originalArray.dup;
+secondArray[0] = 1;
+// assert that originalArray has not been affected
+assert(originalArray[0] == 12);
+---
+
+$(B Attention:) If the _container is implemented as a class, using an
+uninitialized instance can cause a null pointer dereference.
+
+---
+import std.container;
+
+RedBlackTree!int rbTree;
+rbTree.insert(5); // null pointer dereference
+---
+
+Using an uninitialized struct-based _container will work, because the struct
+intializes itself upon use; however, up to this point the _container will not
+have an identity and assignment does not create two references to the same
+data.
+
+---
+import std.container;
+
+// create an uninitialized array
+Array!int array1;
+// array2 does _not_ refer to array1
+Array!int array2 = array1;
+array2.insertBack(42);
+// thus array1 will not be affected
+assert(array1.empty);
+
+// after initialization reference semantics work as expected
+array1 = array2;
+// now affects array2 as well
+array1.removeBack();
+assert(array2.empty);
+---
+It is therefore recommended to always construct containers using
+$(REF make, std,_container,util).
+
+This is in fact necessary to put containers into another _container.
+For example, to construct an $(D Array) of ten empty $(D Array)s, use
+the following that calls $(D make) ten times.
+
+---
+import std.container, std.range;
+
+auto arrOfArrs = make!Array(generate!(() => make!(Array!int)).take(10));
+---
+
+Submodules:
+
+This module consists of the following submodules:
+
+$(UL
+ $(LI
+ The $(MREF std, _container, array) module provides
+ an array type with deterministic control of memory, not reliant on
+ the GC unlike built-in arrays.
+ )
+ $(LI
+ The $(MREF std, _container, binaryheap) module
+ provides a binary heap implementation that can be applied to any
+ user-provided random-access range.
+ )
+ $(LI
+ The $(MREF std, _container, dlist) module provides
+ a doubly-linked list implementation.
+ )
+ $(LI
+ The $(MREF std, _container, rbtree) module
+ implements red-black trees.
+ )
+ $(LI
+ The $(MREF std, _container, slist) module
+ implements singly-linked lists.
+ )
+ $(LI
+ The $(MREF std, _container, util) module contains
+ some generic tools commonly used by _container implementations.
+ )
+)
+
+The_primary_range_of_a_container:
+
+While some _containers offer direct access to their elements e.g. via
+$(D opIndex), $(D c.front) or $(D c.back), access
+and modification of a _container's contents is generally done through
+its primary $(MREF_ALTTEXT range, std, range) type,
+which is aliased as $(D C.Range). For example, the primary range type of
+$(D Array!int) is $(D Array!int.Range).
+
+If the documentation of a member function of a _container takes
+a parameter of type $(D Range), then it refers to the primary range type of
+this _container. Oftentimes $(D Take!Range) will be used, in which case
+the range refers to a span of the elements in the _container. Arguments to
+these parameters $(B must) be obtained from the same _container instance
+as the one being worked with. It is important to note that many generic range
+algorithms return the same range type as their input range.
+
+---
+import std.algorithm.comparison : equal;
+import std.algorithm.iteration : find;
+import std.container;
+import std.range : take;
+
+auto array = make!Array(1, 2, 3);
+
+// `find` returns an Array!int.Range advanced to the element "2"
+array.linearRemove(array[].find(2));
+
+assert(array[].equal([1]));
+
+array = make!Array(1, 2, 3);
+
+// the range given to `linearRemove` is a Take!(Array!int.Range)
+// spanning just the element "2"
+array.linearRemove(array[].find(2).take(1));
+
+assert(array[].equal([1, 3]));
+---
+
+When any $(MREF_ALTTEXT range, std, range) can be passed as an argument to
+a member function, the documention usually refers to the parameter's templated
+type as $(D Stuff).
+
+---
+import std.algorithm.comparison : equal;
+import std.container;
+import std.range : iota;
+
+auto array = make!Array(1, 2);
+
+// the range type returned by `iota` is completely unrelated to Array,
+// which is fine for Array.insertBack:
+array.insertBack(iota(3, 10));
+
+assert(array[].equal([1, 2, 3, 4, 5, 6, 7, 8, 9]));
+---
+
+Container_primitives:
+
+Containers do not form a class hierarchy, instead they implement a
+common set of primitives (see table below). These primitives each guarantee
+a specific worst case complexity and thus allow generic code to be written
+independently of the _container implementation.
+
+For example the primitives $(D c.remove(r)) and $(D c.linearRemove(r)) both
+remove the sequence of elements in range $(D r) from the _container $(D c).
+The primitive $(D c.remove(r)) guarantees
+$(BIGOH n$(SUBSCRIPT r) log n$(SUBSCRIPT c)) complexity in the worst case and
+$(D c.linearRemove(r)) relaxes this guarantee to $(BIGOH n$(SUBSCRIPT c)).
+
+Since a sequence of elements can be removed from a $(MREF_ALTTEXT doubly linked list,std,_container,dlist)
+in constant time, $(D DList) provides the primitive $(D c.remove(r))
+as well as $(D c.linearRemove(r)). On the other hand
+$(MREF_ALTTEXT Array, std,_container, array) only offers $(D c.linearRemove(r)).
+
+The following table describes the common set of primitives that containers
+implement. A _container need not implement all primitives, but if a
+primitive is implemented, it must support the syntax described in the $(B
+syntax) column with the semantics described in the $(B description) column, and
+it must not have a worst-case complexity worse than denoted in big-O notation in
+the $(BIGOH &middot;) column. Below, $(D C) means a _container type, $(D c) is
+a value of _container type, $(D n$(SUBSCRIPT x)) represents the effective length of
+value $(D x), which could be a single element (in which case $(D n$(SUBSCRIPT x)) is
+$(D 1)), a _container, or a range.
+
+$(BOOKTABLE Container primitives,
+$(TR
+ $(TH Syntax)
+ $(TH $(BIGOH &middot;))
+ $(TH Description)
+)
+$(TR
+ $(TDNW $(D C(x)))
+ $(TDNW $(D n$(SUBSCRIPT x)))
+ $(TD Creates a _container of type $(D C) from either another _container or a range.
+ The created _container must not be a null reference even if x is empty.)
+)
+$(TR
+ $(TDNW $(D c.dup))
+ $(TDNW $(D n$(SUBSCRIPT c)))
+ $(TD Returns a duplicate of the _container.)
+)
+$(TR
+ $(TDNW $(D c ~ x))
+ $(TDNW $(D n$(SUBSCRIPT c) + n$(SUBSCRIPT x)))
+ $(TD Returns the concatenation of $(D c) and $(D r). $(D x) may be a single
+ element or an input range.)
+)
+$(TR
+ $(TDNW $(D x ~ c))
+ $(TDNW $(D n$(SUBSCRIPT c) + n$(SUBSCRIPT x)))
+ $(TD Returns the concatenation of $(D x) and $(D c). $(D x) may be a
+ single element or an input range type.)
+)
+$(LEADINGROWN 3, Iteration
+)
+$(TR
+ $(TD $(D c.Range))
+ $(TD)
+ $(TD The primary range type associated with the _container.)
+)
+$(TR
+ $(TD $(D c[]))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Returns a range
+ iterating over the entire _container, in a _container-defined order.)
+)
+$(TR
+ $(TDNW $(D c[a .. b]))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Fetches a portion of the _container from key $(D a) to key $(D b).)
+)
+$(LEADINGROWN 3, Capacity
+)
+$(TR
+ $(TD $(D c.empty))
+ $(TD $(D 1))
+ $(TD Returns $(D true) if the _container has no elements, $(D false) otherwise.)
+)
+$(TR
+ $(TD $(D c.length))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Returns the number of elements in the _container.)
+)
+$(TR
+ $(TDNW $(D c.length = n))
+ $(TDNW $(D n$(SUBSCRIPT c) + n))
+ $(TD Forces the number of elements in the _container to $(D n).
+ If the _container ends up growing, the added elements are initialized
+ in a _container-dependent manner (usually with $(D T.init)).)
+)
+$(TR
+ $(TD $(D c.capacity))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Returns the maximum number of elements that can be stored in the
+ _container without triggering a reallocation.)
+)
+$(TR
+ $(TD $(D c.reserve(x)))
+ $(TD $(D n$(SUBSCRIPT c)))
+ $(TD Forces $(D capacity) to at least $(D x) without reducing it.)
+)
+$(LEADINGROWN 3, Access
+)
+$(TR
+ $(TDNW $(D c.front))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Returns the first element of the _container, in a _container-defined order.)
+)
+$(TR
+ $(TDNW $(D c.moveFront))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Destructively reads and returns the first element of the
+ _container. The slot is not removed from the _container; it is left
+ initialized with $(D T.init). This routine need not be defined if $(D
+ front) returns a $(D ref).)
+)
+$(TR
+ $(TDNW $(D c.front = v))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Assigns $(D v) to the first element of the _container.)
+)
+$(TR
+ $(TDNW $(D c.back))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Returns the last element of the _container, in a _container-defined order.)
+)
+$(TR
+ $(TDNW $(D c.moveBack))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Destructively reads and returns the last element of the
+ _container. The slot is not removed from the _container; it is left
+ initialized with $(D T.init). This routine need not be defined if $(D
+ front) returns a $(D ref).)
+)
+$(TR
+ $(TDNW $(D c.back = v))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Assigns $(D v) to the last element of the _container.)
+)
+$(TR
+ $(TDNW $(D c[x]))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Provides indexed access into the _container. The index type is
+ _container-defined. A _container may define several index types (and
+ consequently overloaded indexing).)
+)
+$(TR
+ $(TDNW $(D c.moveAt(x)))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Destructively reads and returns the value at position $(D x). The slot
+ is not removed from the _container; it is left initialized with $(D
+ T.init).)
+)
+$(TR
+ $(TDNW $(D c[x] = v))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Sets element at specified index into the _container.)
+)
+$(TR
+ $(TDNW $(D c[x] $(I op)= v))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Performs read-modify-write operation at specified index into the
+ _container.)
+)
+$(LEADINGROWN 3, Operations
+)
+$(TR
+ $(TDNW $(D e in c))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Returns nonzero if e is found in $(D c).)
+)
+$(TR
+ $(TDNW $(D c.lowerBound(v)))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Returns a range of all elements strictly less than $(D v).)
+)
+$(TR
+ $(TDNW $(D c.upperBound(v)))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Returns a range of all elements strictly greater than $(D v).)
+)
+$(TR
+ $(TDNW $(D c.equalRange(v)))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Returns a range of all elements in $(D c) that are equal to $(D v).)
+)
+$(LEADINGROWN 3, Modifiers
+)
+$(TR
+ $(TDNW $(D c ~= x))
+ $(TDNW $(D n$(SUBSCRIPT c) + n$(SUBSCRIPT x)))
+ $(TD Appends $(D x) to $(D c). $(D x) may be a single element or an input range type.)
+)
+$(TR
+ $(TDNW $(D c.clear()))
+ $(TDNW $(D n$(SUBSCRIPT c)))
+ $(TD Removes all elements in $(D c).)
+)
+$(TR
+ $(TDNW $(D c.insert(x)))
+ $(TDNW $(D n$(SUBSCRIPT x) * log n$(SUBSCRIPT c)))
+ $(TD Inserts $(D x) in $(D c) at a position (or positions) chosen by $(D c).)
+)
+$(TR
+ $(TDNW $(D c.stableInsert(x)))
+ $(TDNW $(D n$(SUBSCRIPT x) * log n$(SUBSCRIPT c)))
+ $(TD Same as $(D c.insert(x)), but is guaranteed to not invalidate any ranges.)
+)
+$(TR
+ $(TDNW $(D c.linearInsert(v)))
+ $(TDNW $(D n$(SUBSCRIPT c)))
+ $(TD Same as $(D c.insert(v)) but relaxes complexity to linear.)
+)
+$(TR
+ $(TDNW $(D c.stableLinearInsert(v)))
+ $(TDNW $(D n$(SUBSCRIPT c)))
+ $(TD Same as $(D c.stableInsert(v)) but relaxes complexity to linear.)
+)
+$(TR
+ $(TDNW $(D c.removeAny()))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Removes some element from $(D c) and returns it.)
+)
+$(TR
+ $(TDNW $(D c.stableRemoveAny()))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Same as $(D c.removeAny()), but is guaranteed to not invalidate any
+ iterators.)
+)
+$(TR
+ $(TDNW $(D c.insertFront(v)))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Inserts $(D v) at the front of $(D c).)
+)
+$(TR
+ $(TDNW $(D c.stableInsertFront(v)))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Same as $(D c.insertFront(v)), but guarantees no ranges will be
+ invalidated.)
+)
+$(TR
+ $(TDNW $(D c.insertBack(v)))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Inserts $(D v) at the back of $(D c).)
+)
+$(TR
+ $(TDNW $(D c.stableInsertBack(v)))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Same as $(D c.insertBack(v)), but guarantees no ranges will be
+ invalidated.)
+)
+$(TR
+ $(TDNW $(D c.removeFront()))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Removes the element at the front of $(D c).)
+)
+$(TR
+ $(TDNW $(D c.stableRemoveFront()))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Same as $(D c.removeFront()), but guarantees no ranges will be
+ invalidated.)
+)
+$(TR
+ $(TDNW $(D c.removeBack()))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Removes the value at the back of $(D c).)
+)
+$(TR
+ $(TDNW $(D c.stableRemoveBack()))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Same as $(D c.removeBack()), but guarantees no ranges will be
+ invalidated.)
+)
+$(TR
+ $(TDNW $(D c.remove(r)))
+ $(TDNW $(D n$(SUBSCRIPT r) * log n$(SUBSCRIPT c)))
+ $(TD Removes range $(D r) from $(D c).)
+)
+$(TR
+ $(TDNW $(D c.stableRemove(r)))
+ $(TDNW $(D n$(SUBSCRIPT r) * log n$(SUBSCRIPT c)))
+ $(TD Same as $(D c.remove(r)), but guarantees iterators are not
+ invalidated.)
+)
+$(TR
+ $(TDNW $(D c.linearRemove(r)))
+ $(TDNW $(D n$(SUBSCRIPT c)))
+ $(TD Removes range $(D r) from $(D c).)
+)
+$(TR
+ $(TDNW $(D c.stableLinearRemove(r)))
+ $(TDNW $(D n$(SUBSCRIPT c)))
+ $(TD Same as $(D c.linearRemove(r)), but guarantees iterators are not
+ invalidated.)
+)
+$(TR
+ $(TDNW $(D c.removeKey(k)))
+ $(TDNW $(D log n$(SUBSCRIPT c)))
+ $(TD Removes an element from $(D c) by using its key $(D k).
+ The key's type is defined by the _container.)
+)
+$(TR
+ $(TDNW $(D ))
+ $(TDNW $(D ))
+ $(TD )
+)
+)
+
+Source: $(PHOBOSSRC std/_container/package.d)
+
+Copyright: Red-black tree code copyright (C) 2008- by Steven Schveighoffer. Other code
+copyright 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
+
+License: Distributed under the Boost Software License, Version 1.0.
+(See accompanying file LICENSE_1_0.txt or copy at $(HTTP
+boost.org/LICENSE_1_0.txt)).
+
+Authors: Steven Schveighoffer, $(HTTP erdani.com, Andrei Alexandrescu)
+ */
+
+module std.container;
+
+public import std.container.array;
+public import std.container.binaryheap;
+public import std.container.dlist;
+public import std.container.rbtree;
+public import std.container.slist;
+
+import std.meta;
+
+
+/* The following documentation and type $(D TotalContainer) are
+intended for developers only.
+
+$(D TotalContainer) is an unimplemented container that illustrates a
+host of primitives that a container may define. It is to some extent
+the bottom of the conceptual container hierarchy. A given container
+most often will choose to only implement a subset of these primitives,
+and define its own additional ones. Adhering to the standard primitive
+names below allows generic code to work independently of containers.
+
+Things to remember: any container must be a reference type, whether
+implemented as a $(D class) or $(D struct). No primitive below
+requires the container to escape addresses of elements, which means
+that compliant containers can be defined to use reference counting or
+other deterministic memory management techniques.
+
+A container may choose to define additional specific operations. The
+only requirement is that those operations bear different names than
+the ones below, lest user code gets confused.
+
+Complexity of operations should be interpreted as "at least as good
+as". If an operation is required to have $(BIGOH n) complexity, it
+could have anything lower than that, e.g. $(BIGOH log(n)). Unless
+specified otherwise, $(D n) inside a $(BIGOH) expression stands for
+the number of elements in the container.
+ */
+struct TotalContainer(T)
+{
+/**
+If the container has a notion of key-value mapping, $(D KeyType)
+defines the type of the key of the container.
+ */
+ alias KeyType = T;
+
+/**
+If the container has a notion of multikey-value mapping, $(D
+KeyTypes[k]), where $(D k) is a zero-based unsigned number, defines
+the type of the $(D k)th key of the container.
+
+A container may define both $(D KeyType) and $(D KeyTypes), e.g. in
+the case it has the notion of primary/preferred key.
+ */
+ alias KeyTypes = AliasSeq!T;
+
+/**
+If the container has a notion of key-value mapping, $(D ValueType)
+defines the type of the value of the container. Typically, a map-style
+container mapping values of type $(D K) to values of type $(D V)
+defines $(D KeyType) to be $(D K) and $(D ValueType) to be $(D V).
+ */
+ alias ValueType = T;
+
+/**
+Defines the container's primary range, which embodies one of the
+ranges defined in $(MREF std,range).
+
+Generally a container may define several types of ranges.
+ */
+ struct Range
+ {
+ /++
+ Range primitives.
+ +/
+ @property bool empty()
+ {
+ assert(0);
+ }
+ /// Ditto
+ @property ref T front() //ref return optional
+ {
+ assert(0);
+ }
+ /// Ditto
+ @property void front(T value) //Only when front does not return by ref
+ {
+ assert(0);
+ }
+ /// Ditto
+ T moveFront()
+ {
+ assert(0);
+ }
+ /// Ditto
+ void popFront()
+ {
+ assert(0);
+ }
+ /// Ditto
+ @property ref T back() //ref return optional
+ {
+ assert(0);
+ }
+ /// Ditto
+ @property void back(T value) //Only when front does not return by ref
+ {
+ assert(0);
+ }
+ /// Ditto
+ T moveBack()
+ {
+ assert(0);
+ }
+ /// Ditto
+ void popBack()
+ {
+ assert(0);
+ }
+ /// Ditto
+ T opIndex(size_t i) //ref return optional
+ {
+ assert(0);
+ }
+ /// Ditto
+ void opIndexAssign(size_t i, T value) //Only when front does not return by ref
+ {
+ assert(0);
+ }
+ /// Ditto
+ T opIndexUnary(string op)(size_t i) //Only when front does not return by ref
+ {
+ assert(0);
+ }
+ /// Ditto
+ void opIndexOpAssign(string op)(size_t i, T value) //Only when front does not return by ref
+ {
+ assert(0);
+ }
+ /// Ditto
+ T moveAt(size_t i)
+ {
+ assert(0);
+ }
+ /// Ditto
+ @property size_t length()
+ {
+ assert(0);
+ }
+ }
+
+/**
+Property returning $(D true) if and only if the container has no
+elements.
+
+Complexity: $(BIGOH 1)
+ */
+ @property bool empty()
+ {
+ assert(0);
+ }
+
+/**
+Returns a duplicate of the container. The elements themselves are not
+transitively duplicated.
+
+Complexity: $(BIGOH n).
+ */
+ @property TotalContainer dup()
+ {
+ assert(0);
+ }
+
+/**
+Returns the number of elements in the container.
+
+Complexity: $(BIGOH log(n)).
+*/
+ @property size_t length()
+ {
+ assert(0);
+ }
+
+/**
+Returns the maximum number of elements the container can store without
+(a) allocating memory, (b) invalidating iterators upon insertion.
+
+Complexity: $(BIGOH log(n)).
+ */
+ @property size_t capacity()
+ {
+ assert(0);
+ }
+
+/**
+Ensures sufficient capacity to accommodate $(D n) elements.
+
+Postcondition: $(D capacity >= n)
+
+Complexity: $(BIGOH log(e - capacity)) if $(D e > capacity), otherwise
+$(BIGOH 1).
+ */
+ void reserve(size_t e)
+ {
+ assert(0);
+ }
+
+/**
+Returns a range that iterates over all elements of the container, in a
+container-defined order. The container should choose the most
+convenient and fast method of iteration for $(D opSlice()).
+
+Complexity: $(BIGOH log(n))
+ */
+ Range opSlice()
+ {
+ assert(0);
+ }
+
+ /**
+ Returns a range that iterates the container between two
+ specified positions.
+
+ Complexity: $(BIGOH log(n))
+ */
+ Range opSlice(size_t a, size_t b)
+ {
+ assert(0);
+ }
+
+/**
+Forward to $(D opSlice().front) and $(D opSlice().back), respectively.
+
+Complexity: $(BIGOH log(n))
+ */
+ @property ref T front() //ref return optional
+ {
+ assert(0);
+ }
+ /// Ditto
+ @property void front(T value) //Only when front does not return by ref
+ {
+ assert(0);
+ }
+ /// Ditto
+ T moveFront()
+ {
+ assert(0);
+ }
+ /// Ditto
+ @property ref T back() //ref return optional
+ {
+ assert(0);
+ }
+ /// Ditto
+ @property void back(T value) //Only when front does not return by ref
+ {
+ assert(0);
+ }
+ /// Ditto
+ T moveBack()
+ {
+ assert(0);
+ }
+
+/**
+Indexing operators yield or modify the value at a specified index.
+ */
+ ref T opIndex(KeyType) //ref return optional
+ {
+ assert(0);
+ }
+ /// ditto
+ void opIndexAssign(KeyType i, T value) //Only when front does not return by ref
+ {
+ assert(0);
+ }
+ /// ditto
+ T opIndexUnary(string op)(KeyType i) //Only when front does not return by ref
+ {
+ assert(0);
+ }
+ /// ditto
+ void opIndexOpAssign(string op)(KeyType i, T value) //Only when front does not return by ref
+ {
+ assert(0);
+ }
+ /// ditto
+ T moveAt(KeyType i)
+ {
+ assert(0);
+ }
+
+/**
+$(D k in container) returns true if the given key is in the container.
+ */
+ bool opBinaryRight(string op)(KeyType k) if (op == "in")
+ {
+ assert(0);
+ }
+
+/**
+Returns a range of all elements containing $(D k) (could be empty or a
+singleton range).
+ */
+ Range equalRange(KeyType k)
+ {
+ assert(0);
+ }
+
+/**
+Returns a range of all elements with keys less than $(D k) (could be
+empty or a singleton range). Only defined by containers that store
+data sorted at all times.
+ */
+ Range lowerBound(KeyType k)
+ {
+ assert(0);
+ }
+
+/**
+Returns a range of all elements with keys larger than $(D k) (could be
+empty or a singleton range). Only defined by containers that store
+data sorted at all times.
+ */
+ Range upperBound(KeyType k)
+ {
+ assert(0);
+ }
+
+/**
+Returns a new container that's the concatenation of $(D this) and its
+argument. $(D opBinaryRight) is only defined if $(D Stuff) does not
+define $(D opBinary).
+
+Complexity: $(BIGOH n + m), where m is the number of elements in $(D
+stuff)
+ */
+ TotalContainer opBinary(string op)(Stuff rhs) if (op == "~")
+ {
+ assert(0);
+ }
+
+ /// ditto
+ TotalContainer opBinaryRight(string op)(Stuff lhs) if (op == "~")
+ {
+ assert(0);
+ }
+
+/**
+Forwards to $(D insertAfter(this[], stuff)).
+ */
+ void opOpAssign(string op)(Stuff stuff) if (op == "~")
+ {
+ assert(0);
+ }
+
+/**
+Removes all contents from the container. The container decides how $(D
+capacity) is affected.
+
+Postcondition: $(D empty)
+
+Complexity: $(BIGOH n)
+ */
+ void clear()
+ {
+ assert(0);
+ }
+
+/**
+Sets the number of elements in the container to $(D newSize). If $(D
+newSize) is greater than $(D length), the added elements are added to
+unspecified positions in the container and initialized with $(D
+.init).
+
+Complexity: $(BIGOH abs(n - newLength))
+
+Postcondition: $(D _length == newLength)
+ */
+ @property void length(size_t newLength)
+ {
+ assert(0);
+ }
+
+/**
+Inserts $(D stuff) in an unspecified position in the
+container. Implementations should choose whichever insertion means is
+the most advantageous for the container, but document the exact
+behavior. $(D stuff) can be a value convertible to the element type of
+the container, or a range of values convertible to it.
+
+The $(D stable) version guarantees that ranges iterating over the
+container are never invalidated. Client code that counts on
+non-invalidating insertion should use $(D stableInsert). Such code would
+not compile against containers that don't support it.
+
+Returns: The number of elements added.
+
+Complexity: $(BIGOH m * log(n)), where $(D m) is the number of
+elements in $(D stuff)
+ */
+ size_t insert(Stuff)(Stuff stuff)
+ {
+ assert(0);
+ }
+ ///ditto
+ size_t stableInsert(Stuff)(Stuff stuff)
+ {
+ assert(0);
+ }
+
+/**
+Same as $(D insert(stuff)) and $(D stableInsert(stuff)) respectively,
+but relax the complexity constraint to linear.
+ */
+ size_t linearInsert(Stuff)(Stuff stuff)
+ {
+ assert(0);
+ }
+ ///ditto
+ size_t stableLinearInsert(Stuff)(Stuff stuff)
+ {
+ assert(0);
+ }
+
+/**
+Picks one value in an unspecified position in the container, removes
+it from the container, and returns it. Implementations should pick the
+value that's the most advantageous for the container. The stable version
+behaves the same, but guarantees that ranges iterating over the container
+are never invalidated.
+
+Precondition: $(D !empty)
+
+Returns: The element removed.
+
+Complexity: $(BIGOH log(n)).
+ */
+ T removeAny()
+ {
+ assert(0);
+ }
+ /// ditto
+ T stableRemoveAny()
+ {
+ assert(0);
+ }
+
+/**
+Inserts $(D value) to the front or back of the container. $(D stuff)
+can be a value convertible to the container's element type or a range
+of values convertible to it. The stable version behaves the same, but
+guarantees that ranges iterating over the container are never
+invalidated.
+
+Returns: The number of elements inserted
+
+Complexity: $(BIGOH log(n)).
+ */
+ size_t insertFront(Stuff)(Stuff stuff)
+ {
+ assert(0);
+ }
+ /// ditto
+ size_t stableInsertFront(Stuff)(Stuff stuff)
+ {
+ assert(0);
+ }
+ /// ditto
+ size_t insertBack(Stuff)(Stuff stuff)
+ {
+ assert(0);
+ }
+ /// ditto
+ size_t stableInsertBack(T value)
+ {
+ assert(0);
+ }
+
+/**
+Removes the value at the front or back of the container. The stable
+version behaves the same, but guarantees that ranges iterating over
+the container are never invalidated. The optional parameter $(D
+howMany) instructs removal of that many elements. If $(D howMany > n),
+all elements are removed and no exception is thrown.
+
+Precondition: $(D !empty)
+
+Complexity: $(BIGOH log(n)).
+ */
+ void removeFront()
+ {
+ assert(0);
+ }
+ /// ditto
+ void stableRemoveFront()
+ {
+ assert(0);
+ }
+ /// ditto
+ void removeBack()
+ {
+ assert(0);
+ }
+ /// ditto
+ void stableRemoveBack()
+ {
+ assert(0);
+ }
+
+/**
+Removes $(D howMany) values at the front or back of the
+container. Unlike the unparameterized versions above, these functions
+do not throw if they could not remove $(D howMany) elements. Instead,
+if $(D howMany > n), all elements are removed. The returned value is
+the effective number of elements removed. The stable version behaves
+the same, but guarantees that ranges iterating over the container are
+never invalidated.
+
+Returns: The number of elements removed
+
+Complexity: $(BIGOH howMany * log(n)).
+ */
+ size_t removeFront(size_t howMany)
+ {
+ assert(0);
+ }
+ /// ditto
+ size_t stableRemoveFront(size_t howMany)
+ {
+ assert(0);
+ }
+ /// ditto
+ size_t removeBack(size_t howMany)
+ {
+ assert(0);
+ }
+ /// ditto
+ size_t stableRemoveBack(size_t howMany)
+ {
+ assert(0);
+ }
+
+/**
+Removes all values corresponding to key $(D k).
+
+Complexity: $(BIGOH m * log(n)), where $(D m) is the number of
+elements with the same key.
+
+Returns: The number of elements removed.
+ */
+ size_t removeKey(KeyType k)
+ {
+ assert(0);
+ }
+
+/**
+Inserts $(D stuff) before, after, or instead range $(D r), which must
+be a valid range previously extracted from this container. $(D stuff)
+can be a value convertible to the container's element type or a range
+of objects convertible to it. The stable version behaves the same, but
+guarantees that ranges iterating over the container are never
+invalidated.
+
+Returns: The number of values inserted.
+
+Complexity: $(BIGOH n + m), where $(D m) is the length of $(D stuff)
+ */
+ size_t insertBefore(Stuff)(Range r, Stuff stuff)
+ {
+ assert(0);
+ }
+ /// ditto
+ size_t stableInsertBefore(Stuff)(Range r, Stuff stuff)
+ {
+ assert(0);
+ }
+ /// ditto
+ size_t insertAfter(Stuff)(Range r, Stuff stuff)
+ {
+ assert(0);
+ }
+ /// ditto
+ size_t stableInsertAfter(Stuff)(Range r, Stuff stuff)
+ {
+ assert(0);
+ }
+ /// ditto
+ size_t replace(Stuff)(Range r, Stuff stuff)
+ {
+ assert(0);
+ }
+ /// ditto
+ size_t stableReplace(Stuff)(Range r, Stuff stuff)
+ {
+ assert(0);
+ }
+
+/**
+Removes all elements belonging to $(D r), which must be a range
+obtained originally from this container. The stable version behaves the
+same, but guarantees that ranges iterating over the container are
+never invalidated.
+
+Returns: A range spanning the remaining elements in the container that
+initially were right after $(D r).
+
+Complexity: $(BIGOH m * log(n)), where $(D m) is the number of
+elements in $(D r)
+ */
+ Range remove(Range r)
+ {
+ assert(0);
+ }
+ /// ditto
+ Range stableRemove(Range r)
+ {
+ assert(0);
+ }
+
+/**
+Same as $(D remove) above, but has complexity relaxed to linear.
+
+Returns: A range spanning the remaining elements in the container that
+initially were right after $(D r).
+
+Complexity: $(BIGOH n)
+ */
+ Range linearRemove(Range r)
+ {
+ assert(0);
+ }
+ /// ditto
+ Range stableLinearRemove(Range r)
+ {
+ assert(0);
+ }
+}
+
+@safe unittest
+{
+ TotalContainer!int test;
+}
diff --git a/libphobos/src/std/container/rbtree.d b/libphobos/src/std/container/rbtree.d
new file mode 100644
index 0000000..861da5e
--- /dev/null
+++ b/libphobos/src/std/container/rbtree.d
@@ -0,0 +1,2065 @@
+/**
+This module implements a red-black tree container.
+
+This module is a submodule of $(MREF std, container).
+
+Source: $(PHOBOSSRC std/container/_rbtree.d)
+
+Copyright: Red-black tree code copyright (C) 2008- by Steven Schveighoffer. Other code
+copyright 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
+
+License: Distributed under the Boost Software License, Version 1.0.
+(See accompanying file LICENSE_1_0.txt or copy at $(HTTP
+boost.org/LICENSE_1_0.txt)).
+
+Authors: Steven Schveighoffer, $(HTTP erdani.com, Andrei Alexandrescu)
+*/
+module std.container.rbtree;
+
+///
+@safe pure unittest
+{
+ 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]));
+}
+
+import std.format;
+import std.functional : binaryFun;
+
+public import std.container.util;
+
+version (unittest) debug = RBDoChecks;
+
+//debug = RBDoChecks;
+
+/*
+ * Implementation for a Red Black node for use in a Red Black Tree (see below)
+ *
+ * this implementation assumes we have a marker Node that is the parent of the
+ * root Node. This marker Node is not a valid Node, but marks the end of the
+ * collection. The root is the left child of the marker Node, so it is always
+ * last in the collection. The marker Node is passed in to the setColor
+ * function, and the Node which has this Node as its parent is assumed to be
+ * the root Node.
+ *
+ * A Red Black tree should have O(lg(n)) insertion, removal, and search time.
+ */
+struct RBNode(V)
+{
+ /*
+ * Convenience alias
+ */
+ alias Node = RBNode*;
+
+ private Node _left;
+ private Node _right;
+ private Node _parent;
+
+ /**
+ * The value held by this node
+ */
+ V value;
+
+ /**
+ * Enumeration determining what color the node is. Null nodes are assumed
+ * to be black.
+ */
+ enum Color : byte
+ {
+ Red,
+ Black
+ }
+
+ /**
+ * The color of the node.
+ */
+ Color color;
+
+ /**
+ * Get the left child
+ */
+ @property inout(RBNode)* left() inout
+ {
+ return _left;
+ }
+
+ /**
+ * Get the right child
+ */
+ @property inout(RBNode)* right() inout
+ {
+ return _right;
+ }
+
+ /**
+ * Get the parent
+ */
+ @property inout(RBNode)* parent() inout
+ {
+ return _parent;
+ }
+
+ /**
+ * Set the left child. Also updates the new child's parent node. This
+ * does not update the previous child.
+ *
+ * Returns newNode
+ */
+ @property Node left(Node newNode)
+ {
+ _left = newNode;
+ if (newNode !is null)
+ newNode._parent = &this;
+ return newNode;
+ }
+
+ /**
+ * Set the right child. Also updates the new child's parent node. This
+ * does not update the previous child.
+ *
+ * Returns newNode
+ */
+ @property Node right(Node newNode)
+ {
+ _right = newNode;
+ if (newNode !is null)
+ newNode._parent = &this;
+ return newNode;
+ }
+
+ // assume _left is not null
+ //
+ // performs rotate-right operation, where this is T, _right is R, _left is
+ // L, _parent is P:
+ //
+ // P P
+ // | -> |
+ // T L
+ // / \ / \
+ // L R a T
+ // / \ / \
+ // a b b R
+ //
+ /**
+ * Rotate right. This performs the following operations:
+ * - The left child becomes the parent of this node.
+ * - This node becomes the new parent's right child.
+ * - The old right child of the new parent becomes the left child of this
+ * node.
+ */
+ Node rotateR()
+ in
+ {
+ assert(_left !is null);
+ }
+ body
+ {
+ // sets _left._parent also
+ if (isLeftNode)
+ parent.left = _left;
+ else
+ parent.right = _left;
+ Node tmp = _left._right;
+
+ // sets _parent also
+ _left.right = &this;
+
+ // sets tmp._parent also
+ left = tmp;
+
+ return &this;
+ }
+
+ // assumes _right is non null
+ //
+ // performs rotate-left operation, where this is T, _right is R, _left is
+ // L, _parent is P:
+ //
+ // P P
+ // | -> |
+ // T R
+ // / \ / \
+ // L R T b
+ // / \ / \
+ // a b L a
+ //
+ /**
+ * Rotate left. This performs the following operations:
+ * - The right child becomes the parent of this node.
+ * - This node becomes the new parent's left child.
+ * - The old left child of the new parent becomes the right child of this
+ * node.
+ */
+ Node rotateL()
+ in
+ {
+ assert(_right !is null);
+ }
+ body
+ {
+ // sets _right._parent also
+ if (isLeftNode)
+ parent.left = _right;
+ else
+ parent.right = _right;
+ Node tmp = _right._left;
+
+ // sets _parent also
+ _right.left = &this;
+
+ // sets tmp._parent also
+ right = tmp;
+ return &this;
+ }
+
+
+ /**
+ * Returns true if this node is a left child.
+ *
+ * Note that this should always return a value because the root has a
+ * parent which is the marker node.
+ */
+ @property bool isLeftNode() const
+ in
+ {
+ assert(_parent !is null);
+ }
+ body
+ {
+ return _parent._left is &this;
+ }
+
+ /**
+ * Set the color of the node after it is inserted. This performs an
+ * update to the whole tree, possibly rotating nodes to keep the Red-Black
+ * properties correct. This is an O(lg(n)) operation, where n is the
+ * number of nodes in the tree.
+ *
+ * end is the marker node, which is the parent of the topmost valid node.
+ */
+ void setColor(Node end)
+ {
+ // test against the marker node
+ if (_parent !is end)
+ {
+ if (_parent.color == Color.Red)
+ {
+ Node cur = &this;
+ while (true)
+ {
+ // because root is always black, _parent._parent always exists
+ if (cur._parent.isLeftNode)
+ {
+ // parent is left node, y is 'uncle', could be null
+ Node y = cur._parent._parent._right;
+ if (y !is null && y.color == Color.Red)
+ {
+ cur._parent.color = Color.Black;
+ y.color = Color.Black;
+ cur = cur._parent._parent;
+ if (cur._parent is end)
+ {
+ // root node
+ cur.color = Color.Black;
+ break;
+ }
+ else
+ {
+ // not root node
+ cur.color = Color.Red;
+ if (cur._parent.color == Color.Black)
+ // satisfied, exit the loop
+ break;
+ }
+ }
+ else
+ {
+ if (!cur.isLeftNode)
+ cur = cur._parent.rotateL();
+ cur._parent.color = Color.Black;
+ cur = cur._parent._parent.rotateR();
+ cur.color = Color.Red;
+ // tree should be satisfied now
+ break;
+ }
+ }
+ else
+ {
+ // parent is right node, y is 'uncle'
+ Node y = cur._parent._parent._left;
+ if (y !is null && y.color == Color.Red)
+ {
+ cur._parent.color = Color.Black;
+ y.color = Color.Black;
+ cur = cur._parent._parent;
+ if (cur._parent is end)
+ {
+ // root node
+ cur.color = Color.Black;
+ break;
+ }
+ else
+ {
+ // not root node
+ cur.color = Color.Red;
+ if (cur._parent.color == Color.Black)
+ // satisfied, exit the loop
+ break;
+ }
+ }
+ else
+ {
+ if (cur.isLeftNode)
+ cur = cur._parent.rotateR();
+ cur._parent.color = Color.Black;
+ cur = cur._parent._parent.rotateL();
+ cur.color = Color.Red;
+ // tree should be satisfied now
+ break;
+ }
+ }
+ }
+
+ }
+ }
+ else
+ {
+ //
+ // this is the root node, color it black
+ //
+ color = Color.Black;
+ }
+ }
+
+ /**
+ * Remove this node from the tree. The 'end' node is used as the marker
+ * which is root's parent. Note that this cannot be null!
+ *
+ * Returns the next highest valued node in the tree after this one, or end
+ * if this was the highest-valued node.
+ */
+ Node remove(Node end)
+ {
+ //
+ // remove this node from the tree, fixing the color if necessary.
+ //
+ Node x;
+ Node ret = next;
+
+ // if this node has 2 children
+ if (_left !is null && _right !is null)
+ {
+ //
+ // normally, we can just swap this node's and y's value, but
+ // because an iterator could be pointing to y and we don't want to
+ // disturb it, we swap this node and y's structure instead. This
+ // can also be a benefit if the value of the tree is a large
+ // struct, which takes a long time to copy.
+ //
+ Node yp, yl, yr;
+ Node y = ret; // y = next
+ yp = y._parent;
+ yl = y._left;
+ yr = y._right;
+ auto yc = y.color;
+ auto isyleft = y.isLeftNode;
+
+ //
+ // replace y's structure with structure of this node.
+ //
+ if (isLeftNode)
+ _parent.left = y;
+ else
+ _parent.right = y;
+ //
+ // need special case so y doesn't point back to itself
+ //
+ y.left = _left;
+ if (_right is y)
+ y.right = &this;
+ else
+ y.right = _right;
+ y.color = color;
+
+ //
+ // replace this node's structure with structure of y.
+ //
+ left = yl;
+ right = yr;
+ if (_parent !is y)
+ {
+ if (isyleft)
+ yp.left = &this;
+ else
+ yp.right = &this;
+ }
+ color = yc;
+ }
+
+ // if this has less than 2 children, remove it
+ if (_left !is null)
+ x = _left;
+ else
+ x = _right;
+
+ bool deferedUnlink = false;
+ if (x is null)
+ {
+ // pretend this is a null node, defer unlinking the node
+ x = &this;
+ deferedUnlink = true;
+ }
+ else if (isLeftNode)
+ _parent.left = x;
+ else
+ _parent.right = x;
+
+ // if the color of this is black, then it needs to be fixed
+ if (color == color.Black)
+ {
+ // need to recolor the tree.
+ while (x._parent !is end && x.color == Node.Color.Black)
+ {
+ if (x.isLeftNode)
+ {
+ // left node
+ Node w = x._parent._right;
+ if (w.color == Node.Color.Red)
+ {
+ w.color = Node.Color.Black;
+ x._parent.color = Node.Color.Red;
+ x._parent.rotateL();
+ w = x._parent._right;
+ }
+ Node wl = w.left;
+ Node wr = w.right;
+ if ((wl is null || wl.color == Node.Color.Black) &&
+ (wr is null || wr.color == Node.Color.Black))
+ {
+ w.color = Node.Color.Red;
+ x = x._parent;
+ }
+ else
+ {
+ if (wr is null || wr.color == Node.Color.Black)
+ {
+ // wl cannot be null here
+ wl.color = Node.Color.Black;
+ w.color = Node.Color.Red;
+ w.rotateR();
+ w = x._parent._right;
+ }
+
+ w.color = x._parent.color;
+ x._parent.color = Node.Color.Black;
+ w._right.color = Node.Color.Black;
+ x._parent.rotateL();
+ x = end.left; // x = root
+ }
+ }
+ else
+ {
+ // right node
+ Node w = x._parent._left;
+ if (w.color == Node.Color.Red)
+ {
+ w.color = Node.Color.Black;
+ x._parent.color = Node.Color.Red;
+ x._parent.rotateR();
+ w = x._parent._left;
+ }
+ Node wl = w.left;
+ Node wr = w.right;
+ if ((wl is null || wl.color == Node.Color.Black) &&
+ (wr is null || wr.color == Node.Color.Black))
+ {
+ w.color = Node.Color.Red;
+ x = x._parent;
+ }
+ else
+ {
+ if (wl is null || wl.color == Node.Color.Black)
+ {
+ // wr cannot be null here
+ wr.color = Node.Color.Black;
+ w.color = Node.Color.Red;
+ w.rotateL();
+ w = x._parent._left;
+ }
+
+ w.color = x._parent.color;
+ x._parent.color = Node.Color.Black;
+ w._left.color = Node.Color.Black;
+ x._parent.rotateR();
+ x = end.left; // x = root
+ }
+ }
+ }
+ x.color = Node.Color.Black;
+ }
+
+ if (deferedUnlink)
+ {
+ //
+ // unlink this node from the tree
+ //
+ if (isLeftNode)
+ _parent.left = null;
+ else
+ _parent.right = null;
+ }
+
+ // clean references to help GC - Bugzilla 12915
+ _left = _right = _parent = null;
+
+ return ret;
+ }
+
+ /**
+ * Return the leftmost descendant of this node.
+ */
+ @property inout(RBNode)* leftmost() inout
+ {
+ inout(RBNode)* result = &this;
+ while (result._left !is null)
+ result = result._left;
+ return result;
+ }
+
+ /**
+ * Return the rightmost descendant of this node
+ */
+ @property inout(RBNode)* rightmost() inout
+ {
+ inout(RBNode)* result = &this;
+ while (result._right !is null)
+ result = result._right;
+ return result;
+ }
+
+ /**
+ * Returns the next valued node in the tree.
+ *
+ * You should never call this on the marker node, as it is assumed that
+ * there is a valid next node.
+ */
+ @property inout(RBNode)* next() inout
+ {
+ inout(RBNode)* n = &this;
+ if (n.right is null)
+ {
+ while (!n.isLeftNode)
+ n = n._parent;
+ return n._parent;
+ }
+ else
+ return n.right.leftmost;
+ }
+
+ /**
+ * Returns the previous valued node in the tree.
+ *
+ * You should never call this on the leftmost node of the tree as it is
+ * assumed that there is a valid previous node.
+ */
+ @property inout(RBNode)* prev() inout
+ {
+ inout(RBNode)* n = &this;
+ if (n.left is null)
+ {
+ while (n.isLeftNode)
+ n = n._parent;
+ return n._parent;
+ }
+ else
+ return n.left.rightmost;
+ }
+
+ Node dup(scope Node delegate(V v) alloc)
+ {
+ //
+ // duplicate this and all child nodes
+ //
+ // The recursion should be lg(n), so we shouldn't have to worry about
+ // stack size.
+ //
+ Node copy = alloc(value);
+ copy.color = color;
+ if (_left !is null)
+ copy.left = _left.dup(alloc);
+ if (_right !is null)
+ copy.right = _right.dup(alloc);
+ return copy;
+ }
+
+ Node dup()
+ {
+ Node copy = new RBNode!V(null, null, null, value);
+ copy.color = color;
+ if (_left !is null)
+ copy.left = _left.dup();
+ if (_right !is null)
+ copy.right = _right.dup();
+ return copy;
+ }
+}
+
+//constness checks
+@safe pure unittest
+{
+ const RBNode!int n;
+ static assert(is(typeof(n.leftmost)));
+ static assert(is(typeof(n.rightmost)));
+ static assert(is(typeof(n.next)));
+ static assert(is(typeof(n.prev)));
+}
+
+private struct RBRange(N)
+{
+ alias Node = N;
+ alias Elem = typeof(Node.value);
+
+ private Node _begin;
+ private Node _end;
+
+ private this(Node b, Node e)
+ {
+ _begin = b;
+ _end = e;
+ }
+
+ /**
+ * Returns $(D true) if the range is _empty
+ */
+ @property bool empty() const
+ {
+ return _begin is _end;
+ }
+
+ /**
+ * Returns the first element in the range
+ */
+ @property Elem front()
+ {
+ return _begin.value;
+ }
+
+ /**
+ * Returns the last element in the range
+ */
+ @property Elem back()
+ {
+ return _end.prev.value;
+ }
+
+ /**
+ * pop the front element from the range
+ *
+ * Complexity: amortized $(BIGOH 1)
+ */
+ void popFront()
+ {
+ _begin = _begin.next;
+ }
+
+ /**
+ * pop the back element from the range
+ *
+ * Complexity: amortized $(BIGOH 1)
+ */
+ void popBack()
+ {
+ _end = _end.prev;
+ }
+
+ /**
+ * Trivial _save implementation, needed for $(D isForwardRange).
+ */
+ @property RBRange save()
+ {
+ return this;
+ }
+}
+
+/**
+ * Implementation of a $(LINK2 https://en.wikipedia.org/wiki/Red%E2%80%93black_tree,
+ * red-black tree) container.
+ *
+ * All inserts, removes, searches, and any function in general has complexity
+ * of $(BIGOH lg(n)).
+ *
+ * To use a different comparison than $(D "a < b"), pass a different operator string
+ * that can be used by $(REF binaryFun, std,functional), or pass in a
+ * function, delegate, functor, or any type where $(D less(a, b)) results in a $(D bool)
+ * value.
+ *
+ * Note that less should produce a strict ordering. That is, for two unequal
+ * elements $(D a) and $(D b), $(D less(a, b) == !less(b, a)). $(D less(a, a)) should
+ * always equal $(D false).
+ *
+ * If $(D allowDuplicates) is set to $(D true), then inserting the same element more than
+ * once continues to add more elements. If it is $(D false), duplicate elements are
+ * ignored on insertion. If duplicates are allowed, then new elements are
+ * inserted after all existing duplicate elements.
+ */
+final class RedBlackTree(T, alias less = "a < b", bool allowDuplicates = false)
+if (is(typeof(binaryFun!less(T.init, T.init))))
+{
+ import std.meta : allSatisfy;
+ import std.range : Take;
+ import std.range.primitives : isInputRange, walkLength;
+ import std.traits : isIntegral, isDynamicArray, isImplicitlyConvertible;
+
+ alias _less = binaryFun!less;
+
+ version (unittest)
+ {
+ static if (is(typeof(less) == string))
+ {
+ private enum doUnittest = isIntegral!T && (less == "a < b" || less == "a > b");
+ }
+ else
+ enum doUnittest = false;
+
+ // note, this must be final so it does not affect the vtable layout
+ bool arrayEqual(T[] arr)
+ {
+ if (walkLength(this[]) == arr.length)
+ {
+ foreach (v; arr)
+ {
+ if (!(v in this))
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+ }
+ else
+ {
+ private enum doUnittest = false;
+ }
+
+ /**
+ * Element type for the tree
+ */
+ alias Elem = T;
+
+ // used for convenience
+ private alias RBNode = .RBNode!Elem;
+ private alias Node = RBNode.Node;
+
+ private Node _end;
+ private Node _begin;
+ private size_t _length;
+
+ private void _setup()
+ {
+ assert(!_end); //Make sure that _setup isn't run more than once.
+ _begin = _end = allocate();
+ }
+
+ static private Node allocate()
+ {
+ return new RBNode;
+ }
+
+ static private Node allocate(Elem v)
+ {
+ return new RBNode(null, null, null, v);
+ }
+
+ /**
+ * The range types for $(D RedBlackTree)
+ */
+ alias Range = RBRange!(RBNode*);
+ alias ConstRange = RBRange!(const(RBNode)*); /// Ditto
+ alias ImmutableRange = RBRange!(immutable(RBNode)*); /// Ditto
+
+ static if (doUnittest) @safe pure unittest
+ {
+ import std.algorithm.comparison : equal;
+ import std.range.primitives;
+ auto ts = new RedBlackTree(1, 2, 3, 4, 5);
+ assert(ts.length == 5);
+ auto r = ts[];
+
+ static if (less == "a < b")
+ auto vals = [1, 2, 3, 4, 5];
+ else
+ auto vals = [5, 4, 3, 2, 1];
+ assert(equal(r, vals));
+
+ assert(r.front == vals.front);
+ assert(r.back != r.front);
+ auto oldfront = r.front;
+ auto oldback = r.back;
+ r.popFront();
+ r.popBack();
+ assert(r.front != r.back);
+ assert(r.front != oldfront);
+ assert(r.back != oldback);
+ assert(ts.length == 5);
+ }
+
+ // find a node based on an element value
+ private inout(RBNode)* _find(Elem e) inout
+ {
+ static if (allowDuplicates)
+ {
+ inout(RBNode)* cur = _end.left;
+ inout(RBNode)* result = null;
+ while (cur)
+ {
+ if (_less(cur.value, e))
+ cur = cur.right;
+ else if (_less(e, cur.value))
+ cur = cur.left;
+ else
+ {
+ // want to find the left-most element
+ result = cur;
+ cur = cur.left;
+ }
+ }
+ return result;
+ }
+ else
+ {
+ inout(RBNode)* cur = _end.left;
+ while (cur)
+ {
+ if (_less(cur.value, e))
+ cur = cur.right;
+ else if (_less(e, cur.value))
+ cur = cur.left;
+ else
+ return cur;
+ }
+ return null;
+ }
+ }
+
+ // add an element to the tree, returns the node added, or the existing node
+ // if it has already been added and allowDuplicates is false
+ private auto _add(Elem n)
+ {
+ Node result;
+ static if (!allowDuplicates)
+ bool added = true;
+
+ if (!_end.left)
+ {
+ _end.left = _begin = result = allocate(n);
+ }
+ else
+ {
+ Node newParent = _end.left;
+ Node nxt;
+ while (true)
+ {
+ if (_less(n, newParent.value))
+ {
+ nxt = newParent.left;
+ if (nxt is null)
+ {
+ //
+ // add to right of new parent
+ //
+ newParent.left = result = allocate(n);
+ break;
+ }
+ }
+ else
+ {
+ static if (!allowDuplicates)
+ {
+ if (!_less(newParent.value, n))
+ {
+ result = newParent;
+ added = false;
+ break;
+ }
+ }
+ nxt = newParent.right;
+ if (nxt is null)
+ {
+ //
+ // add to right of new parent
+ //
+ newParent.right = result = allocate(n);
+ break;
+ }
+ }
+ newParent = nxt;
+ }
+ if (_begin.left)
+ _begin = _begin.left;
+ }
+
+ static if (allowDuplicates)
+ {
+ result.setColor(_end);
+ debug(RBDoChecks)
+ check();
+ ++_length;
+ return result;
+ }
+ else
+ {
+ import std.typecons : Tuple;
+
+ if (added)
+ {
+ ++_length;
+ result.setColor(_end);
+ }
+ debug(RBDoChecks)
+ check();
+ return Tuple!(bool, "added", Node, "n")(added, result);
+ }
+ }
+
+
+ /**
+ * Check if any elements exist in the container. Returns $(D false) if at least
+ * one element exists.
+ */
+ @property bool empty()
+ {
+ return _end.left is null;
+ }
+
+ /++
+ Returns the number of elements in the container.
+
+ Complexity: $(BIGOH 1).
+ +/
+ @property size_t length() const
+ {
+ return _length;
+ }
+
+ /**
+ * Duplicate this container. The resulting container contains a shallow
+ * copy of the elements.
+ *
+ * Complexity: $(BIGOH n)
+ */
+ @property RedBlackTree dup()
+ {
+ return new RedBlackTree(_end.dup(), _length);
+ }
+
+ static if (doUnittest) @safe pure unittest
+ {
+ import std.algorithm.comparison : equal;
+ auto ts = new RedBlackTree(1, 2, 3, 4, 5);
+ assert(ts.length == 5);
+ auto ts2 = ts.dup;
+ assert(ts2.length == 5);
+ assert(equal(ts[], ts2[]));
+ ts2.insert(cast(Elem) 6);
+ assert(!equal(ts[], ts2[]));
+ assert(ts.length == 5 && ts2.length == 6);
+ }
+
+ /**
+ * Fetch a range that spans all the elements in the container.
+ *
+ * Complexity: $(BIGOH 1)
+ */
+ Range opSlice()
+ {
+ return Range(_begin, _end);
+ }
+
+ /// Ditto
+ ConstRange opSlice() const
+ {
+ return ConstRange(_begin, _end);
+ }
+
+ /// Ditto
+ ImmutableRange opSlice() immutable
+ {
+ return ImmutableRange(_begin, _end);
+ }
+
+ /**
+ * The front element in the container
+ *
+ * Complexity: $(BIGOH 1)
+ */
+ Elem front()
+ {
+ return _begin.value;
+ }
+
+ /**
+ * The last element in the container
+ *
+ * Complexity: $(BIGOH log(n))
+ */
+ Elem back()
+ {
+ return _end.prev.value;
+ }
+
+ /++
+ $(D in) operator. Check to see if the given element exists in the
+ container.
+
+ Complexity: $(BIGOH log(n))
+ +/
+ bool opBinaryRight(string op)(Elem e) const if (op == "in")
+ {
+ return _find(e) !is null;
+ }
+
+ static if (doUnittest) @safe pure unittest
+ {
+ auto ts = new RedBlackTree(1, 2, 3, 4, 5);
+ assert(cast(Elem) 3 in ts);
+ assert(cast(Elem) 6 !in ts);
+ }
+
+ /**
+ * Compares two trees for equality.
+ *
+ * Complexity: $(BIGOH n)
+ */
+ override bool opEquals(Object rhs)
+ {
+ import std.algorithm.comparison : equal;
+
+ RedBlackTree that = cast(RedBlackTree) rhs;
+ if (that is null) return false;
+
+ // If there aren't the same number of nodes, we can't be equal.
+ if (this._length != that._length) return false;
+
+ auto thisRange = this[];
+ auto thatRange = that[];
+ return equal!(function(Elem a, Elem b) => !_less(a,b) && !_less(b,a))
+ (thisRange, thatRange);
+ }
+
+ static if (doUnittest) @system unittest
+ {
+ auto t1 = new RedBlackTree(1,2,3,4);
+ auto t2 = new RedBlackTree(1,2,3,4);
+ auto t3 = new RedBlackTree(1,2,3,5);
+ auto t4 = new RedBlackTree(1,2,3,4,5);
+ auto o = new Object();
+
+ assert(t1 == t1);
+ assert(t1 == t2);
+ assert(t1 != t3);
+ assert(t1 != t4);
+ assert(t1 != o); // pathological case, must not crash
+ }
+
+ /**
+ * Removes all elements from the container.
+ *
+ * Complexity: $(BIGOH 1)
+ */
+ void clear()
+ {
+ _end.left = null;
+ _begin = _end;
+ _length = 0;
+ }
+
+ static if (doUnittest) @safe pure unittest
+ {
+ auto ts = new RedBlackTree(1,2,3,4,5);
+ assert(ts.length == 5);
+ ts.clear();
+ assert(ts.empty && ts.length == 0);
+ }
+
+ /**
+ * Insert a single element in the container. Note that this does not
+ * invalidate any ranges currently iterating the container.
+ *
+ * Returns: The number of elements inserted.
+ *
+ * Complexity: $(BIGOH log(n))
+ */
+ size_t stableInsert(Stuff)(Stuff stuff) if (isImplicitlyConvertible!(Stuff, Elem))
+ {
+ static if (allowDuplicates)
+ {
+ _add(stuff);
+ return 1;
+ }
+ else
+ {
+ return(_add(stuff).added ? 1 : 0);
+ }
+ }
+
+ /**
+ * Insert a range of elements in the container. Note that this does not
+ * invalidate any ranges currently iterating the container.
+ *
+ * Returns: The number of elements inserted.
+ *
+ * Complexity: $(BIGOH m * log(n))
+ */
+ size_t stableInsert(Stuff)(Stuff stuff) if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, Elem))
+ {
+ size_t result = 0;
+ static if (allowDuplicates)
+ {
+ foreach (e; stuff)
+ {
+ ++result;
+ _add(e);
+ }
+ }
+ else
+ {
+ foreach (e; stuff)
+ {
+ if (_add(e).added)
+ ++result;
+ }
+ }
+ return result;
+ }
+
+ /// ditto
+ alias insert = stableInsert;
+
+ static if (doUnittest) @safe pure unittest
+ {
+ auto ts = new RedBlackTree(2,1,3,4,5,2,5);
+ static if (allowDuplicates)
+ {
+ assert(ts.length == 7);
+ assert(ts.stableInsert(cast(Elem[])[7, 8, 6, 9, 10, 8]) == 6);
+ assert(ts.length == 13);
+ assert(ts.stableInsert(cast(Elem) 11) == 1 && ts.length == 14);
+ assert(ts.stableInsert(cast(Elem) 7) == 1 && ts.length == 15);
+
+ static if (less == "a < b")
+ assert(ts.arrayEqual([1,2,2,3,4,5,5,6,7,7,8,8,9,10,11]));
+ else
+ assert(ts.arrayEqual([11,10,9,8,8,7,7,6,5,5,4,3,2,2,1]));
+ }
+ else
+ {
+ assert(ts.length == 5);
+ Elem[] elems = [7, 8, 6, 9, 10, 8];
+ assert(ts.stableInsert(elems) == 5);
+ assert(ts.length == 10);
+ assert(ts.stableInsert(cast(Elem) 11) == 1 && ts.length == 11);
+ assert(ts.stableInsert(cast(Elem) 7) == 0 && ts.length == 11);
+
+ static if (less == "a < b")
+ assert(ts.arrayEqual([1,2,3,4,5,6,7,8,9,10,11]));
+ else
+ assert(ts.arrayEqual([11,10,9,8,7,6,5,4,3,2,1]));
+ }
+ }
+
+ /**
+ * Remove an element from the container and return its value.
+ *
+ * Complexity: $(BIGOH log(n))
+ */
+ Elem removeAny()
+ {
+ scope(success)
+ --_length;
+ auto n = _begin;
+ auto result = n.value;
+ _begin = n.remove(_end);
+ debug(RBDoChecks)
+ check();
+ return result;
+ }
+
+ static if (doUnittest) @safe pure unittest
+ {
+ auto ts = new RedBlackTree(1,2,3,4,5);
+ assert(ts.length == 5);
+ auto x = ts.removeAny();
+ assert(ts.length == 4);
+ Elem[] arr;
+ foreach (Elem i; 1 .. 6)
+ if (i != x) arr ~= i;
+ assert(ts.arrayEqual(arr));
+ }
+
+ /**
+ * Remove the front element from the container.
+ *
+ * Complexity: $(BIGOH log(n))
+ */
+ void removeFront()
+ {
+ scope(success)
+ --_length;
+ _begin = _begin.remove(_end);
+ debug(RBDoChecks)
+ check();
+ }
+
+ /**
+ * Remove the back element from the container.
+ *
+ * Complexity: $(BIGOH log(n))
+ */
+ void removeBack()
+ {
+ scope(success)
+ --_length;
+ auto lastnode = _end.prev;
+ if (lastnode is _begin)
+ _begin = _begin.remove(_end);
+ else
+ lastnode.remove(_end);
+ debug(RBDoChecks)
+ check();
+ }
+
+ static if (doUnittest) @safe pure unittest
+ {
+ auto ts = new RedBlackTree(1,2,3,4,5);
+ assert(ts.length == 5);
+ ts.removeBack();
+ assert(ts.length == 4);
+
+ static if (less == "a < b")
+ assert(ts.arrayEqual([1,2,3,4]));
+ else
+ assert(ts.arrayEqual([2,3,4,5]));
+
+ ts.removeFront();
+ assert(ts.arrayEqual([2,3,4]) && ts.length == 3);
+ }
+
+ /++
+ Removes the given range from the container.
+
+ Returns: A range containing all of the elements that were after the
+ given range.
+
+ Complexity: $(BIGOH m * log(n)) (where m is the number of elements in
+ the range)
+ +/
+ Range remove(Range r)
+ {
+ auto b = r._begin;
+ auto e = r._end;
+ if (_begin is b)
+ _begin = e;
+ while (b !is e)
+ {
+ b = b.remove(_end);
+ --_length;
+ }
+ debug(RBDoChecks)
+ check();
+ return Range(e, _end);
+ }
+
+ static if (doUnittest) @safe pure unittest
+ {
+ import std.algorithm.comparison : equal;
+ auto ts = new RedBlackTree(1,2,3,4,5);
+ assert(ts.length == 5);
+ auto r = ts[];
+ r.popFront();
+ r.popBack();
+ assert(ts.length == 5);
+ auto r2 = ts.remove(r);
+ assert(ts.length == 2);
+ assert(ts.arrayEqual([1,5]));
+
+ static if (less == "a < b")
+ assert(equal(r2, [5]));
+ else
+ assert(equal(r2, [1]));
+ }
+
+ /++
+ Removes the given $(D Take!Range) from the container
+
+ Returns: A range containing all of the elements that were after the
+ given range.
+
+ Complexity: $(BIGOH m * log(n)) (where m is the number of elements in
+ the range)
+ +/
+ Range remove(Take!Range r)
+ {
+ immutable isBegin = (r.source._begin is _begin);
+ auto b = r.source._begin;
+
+ while (!r.empty)
+ {
+ r.popFront();
+ b = b.remove(_end);
+ --_length;
+ }
+
+ if (isBegin)
+ _begin = b;
+
+ return Range(b, _end);
+ }
+
+ static if (doUnittest) @safe pure unittest
+ {
+ import std.algorithm.comparison : equal;
+ import std.range : take;
+ auto ts = new RedBlackTree(1,2,3,4,5);
+ auto r = ts[];
+ r.popFront();
+ assert(ts.length == 5);
+ auto r2 = ts.remove(take(r, 0));
+
+ static if (less == "a < b")
+ {
+ assert(equal(r2, [2,3,4,5]));
+ auto r3 = ts.remove(take(r, 2));
+ assert(ts.arrayEqual([1,4,5]) && ts.length == 3);
+ assert(equal(r3, [4,5]));
+ }
+ else
+ {
+ assert(equal(r2, [4,3,2,1]));
+ auto r3 = ts.remove(take(r, 2));
+ assert(ts.arrayEqual([5,2,1]) && ts.length == 3);
+ assert(equal(r3, [2,1]));
+ }
+ }
+
+ /++
+ Removes elements from the container that are equal to the given values
+ according to the less comparator. One element is removed for each value
+ given which is in the container. If $(D allowDuplicates) is true,
+ duplicates are removed only if duplicate values are given.
+
+ Returns: The number of elements removed.
+
+ Complexity: $(BIGOH m log(n)) (where m is the number of elements to remove)
+
+ Example:
+--------------------
+auto rbt = redBlackTree!true(0, 1, 1, 1, 4, 5, 7);
+rbt.removeKey(1, 4, 7);
+assert(equal(rbt[], [0, 1, 1, 5]));
+rbt.removeKey(1, 1, 0);
+assert(equal(rbt[], [5]));
+--------------------
+ +/
+ size_t removeKey(U...)(U elems)
+ if (allSatisfy!(isImplicitlyConvertibleToElem, U))
+ {
+ Elem[U.length] toRemove = [elems];
+ return removeKey(toRemove[]);
+ }
+
+ /++ Ditto +/
+ size_t removeKey(U)(U[] elems)
+ if (isImplicitlyConvertible!(U, Elem))
+ {
+ immutable lenBefore = length;
+
+ foreach (e; elems)
+ {
+ auto beg = _firstGreaterEqual(e);
+ if (beg is _end || _less(e, beg.value))
+ // no values are equal
+ continue;
+ immutable isBegin = (beg is _begin);
+ beg = beg.remove(_end);
+ if (isBegin)
+ _begin = beg;
+ --_length;
+ }
+
+ return lenBefore - length;
+ }
+
+ /++ Ditto +/
+ size_t removeKey(Stuff)(Stuff stuff)
+ if (isInputRange!Stuff &&
+ isImplicitlyConvertible!(ElementType!Stuff, Elem) &&
+ !isDynamicArray!Stuff)
+ {
+ import std.array : array;
+ //We use array in case stuff is a Range from this RedBlackTree - either
+ //directly or indirectly.
+ return removeKey(array(stuff));
+ }
+
+ //Helper for removeKey.
+ private template isImplicitlyConvertibleToElem(U)
+ {
+ enum isImplicitlyConvertibleToElem = isImplicitlyConvertible!(U, Elem);
+ }
+
+ static if (doUnittest) @safe pure unittest
+ {
+ import std.algorithm.comparison : equal;
+ import std.range : take;
+ auto rbt = new RedBlackTree(5, 4, 3, 7, 2, 1, 7, 6, 2, 19, 45);
+
+ //The cast(Elem) is because these tests are instantiated with a variety
+ //of numeric types, and the literals are all int, which is not always
+ //implicitly convertible to Elem (e.g. short).
+ static if (allowDuplicates)
+ {
+ assert(rbt.length == 11);
+ assert(rbt.removeKey(cast(Elem) 4) == 1 && rbt.length == 10);
+ assert(rbt.arrayEqual([1,2,2,3,5,6,7,7,19,45]) && rbt.length == 10);
+
+ assert(rbt.removeKey(cast(Elem) 6, cast(Elem) 2, cast(Elem) 1) == 3);
+ assert(rbt.arrayEqual([2,3,5,7,7,19,45]) && rbt.length == 7);
+
+ assert(rbt.removeKey(cast(Elem)(42)) == 0 && rbt.length == 7);
+ assert(rbt.removeKey(take(rbt[], 3)) == 3 && rbt.length == 4);
+
+ static if (less == "a < b")
+ assert(equal(rbt[], [7,7,19,45]));
+ else
+ assert(equal(rbt[], [7,5,3,2]));
+ }
+ else
+ {
+ assert(rbt.length == 9);
+ assert(rbt.removeKey(cast(Elem) 4) == 1 && rbt.length == 8);
+ assert(rbt.arrayEqual([1,2,3,5,6,7,19,45]));
+
+ assert(rbt.removeKey(cast(Elem) 6, cast(Elem) 2, cast(Elem) 1) == 3);
+ assert(rbt.arrayEqual([3,5,7,19,45]) && rbt.length == 5);
+
+ assert(rbt.removeKey(cast(Elem)(42)) == 0 && rbt.length == 5);
+ assert(rbt.removeKey(take(rbt[], 3)) == 3 && rbt.length == 2);
+
+ static if (less == "a < b")
+ assert(equal(rbt[], [19,45]));
+ else
+ assert(equal(rbt[], [5,3]));
+ }
+ }
+
+ // find the first node where the value is > e
+ private inout(RBNode)* _firstGreater(Elem e) inout
+ {
+ // can't use _find, because we cannot return null
+ auto cur = _end.left;
+ inout(RBNode)* result = _end;
+ while (cur)
+ {
+ if (_less(e, cur.value))
+ {
+ result = cur;
+ cur = cur.left;
+ }
+ else
+ cur = cur.right;
+ }
+ return result;
+ }
+
+ // find the first node where the value is >= e
+ private inout(RBNode)* _firstGreaterEqual(Elem e) inout
+ {
+ // can't use _find, because we cannot return null.
+ auto cur = _end.left;
+ inout(RBNode)* result = _end;
+ while (cur)
+ {
+ if (_less(cur.value, e))
+ cur = cur.right;
+ else
+ {
+ result = cur;
+ cur = cur.left;
+ }
+
+ }
+ return result;
+ }
+
+ /**
+ * Get a range from the container with all elements that are > e according
+ * to the less comparator
+ *
+ * Complexity: $(BIGOH log(n))
+ */
+ Range upperBound(Elem e)
+ {
+ return Range(_firstGreater(e), _end);
+ }
+
+ /// Ditto
+ ConstRange upperBound(Elem e) const
+ {
+ return ConstRange(_firstGreater(e), _end);
+ }
+
+ /// Ditto
+ ImmutableRange upperBound(Elem e) immutable
+ {
+ return ImmutableRange(_firstGreater(e), _end);
+ }
+
+ /**
+ * Get a range from the container with all elements that are < e according
+ * to the less comparator
+ *
+ * Complexity: $(BIGOH log(n))
+ */
+ Range lowerBound(Elem e)
+ {
+ return Range(_begin, _firstGreaterEqual(e));
+ }
+
+ /// Ditto
+ ConstRange lowerBound(Elem e) const
+ {
+ return ConstRange(_begin, _firstGreaterEqual(e));
+ }
+
+ /// Ditto
+ ImmutableRange lowerBound(Elem e) immutable
+ {
+ return ImmutableRange(_begin, _firstGreaterEqual(e));
+ }
+
+ /**
+ * Get a range from the container with all elements that are == e according
+ * to the less comparator
+ *
+ * Complexity: $(BIGOH log(n))
+ */
+ auto equalRange(this This)(Elem e)
+ {
+ auto beg = _firstGreaterEqual(e);
+ alias RangeType = RBRange!(typeof(beg));
+ if (beg is _end || _less(e, beg.value))
+ // no values are equal
+ return RangeType(beg, beg);
+ static if (allowDuplicates)
+ {
+ return RangeType(beg, _firstGreater(e));
+ }
+ else
+ {
+ // no sense in doing a full search, no duplicates are allowed,
+ // so we just get the next node.
+ return RangeType(beg, beg.next);
+ }
+ }
+
+ static if (doUnittest) @safe pure unittest
+ {
+ import std.algorithm.comparison : equal;
+ auto ts = new RedBlackTree(1, 2, 3, 4, 5);
+ auto rl = ts.lowerBound(3);
+ auto ru = ts.upperBound(3);
+ auto re = ts.equalRange(3);
+
+ static if (less == "a < b")
+ {
+ assert(equal(rl, [1,2]));
+ assert(equal(ru, [4,5]));
+ }
+ else
+ {
+ assert(equal(rl, [5,4]));
+ assert(equal(ru, [2,1]));
+ }
+
+ assert(equal(re, [3]));
+ }
+
+ debug(RBDoChecks)
+ {
+ /*
+ * Print the tree. This prints a sideways view of the tree in ASCII form,
+ * with the number of indentations representing the level of the nodes.
+ * It does not print values, only the tree structure and color of nodes.
+ */
+ void printTree(Node n, int indent = 0)
+ {
+ import std.stdio : write, writeln;
+ if (n !is null)
+ {
+ printTree(n.right, indent + 2);
+ for (int i = 0; i < indent; i++)
+ write(".");
+ writeln(n.color == n.color.Black ? "B" : "R");
+ printTree(n.left, indent + 2);
+ }
+ else
+ {
+ for (int i = 0; i < indent; i++)
+ write(".");
+ writeln("N");
+ }
+ if (indent is 0)
+ writeln();
+ }
+
+ /*
+ * Check the tree for validity. This is called after every add or remove.
+ * This should only be enabled to debug the implementation of the RB Tree.
+ */
+ void check()
+ {
+ //
+ // check implementation of the tree
+ //
+ int recurse(Node n, string path)
+ {
+ import std.stdio : writeln;
+ if (n is null)
+ return 1;
+ if (n.parent.left !is n && n.parent.right !is n)
+ throw new Exception("Node at path " ~ path ~ " has inconsistent pointers");
+ Node next = n.next;
+ static if (allowDuplicates)
+ {
+ if (next !is _end && _less(next.value, n.value))
+ throw new Exception("ordering invalid at path " ~ path);
+ }
+ else
+ {
+ if (next !is _end && !_less(n.value, next.value))
+ throw new Exception("ordering invalid at path " ~ path);
+ }
+ if (n.color == n.color.Red)
+ {
+ if ((n.left !is null && n.left.color == n.color.Red) ||
+ (n.right !is null && n.right.color == n.color.Red))
+ throw new Exception("Node at path " ~ path ~ " is red with a red child");
+ }
+
+ int l = recurse(n.left, path ~ "L");
+ int r = recurse(n.right, path ~ "R");
+ if (l != r)
+ {
+ writeln("bad tree at:");
+ debug printTree(n);
+ throw new Exception(
+ "Node at path " ~ path ~ " has different number of black nodes on left and right paths"
+ );
+ }
+ return l + (n.color == n.color.Black ? 1 : 0);
+ }
+
+ try
+ {
+ recurse(_end.left, "");
+ }
+ catch (Exception e)
+ {
+ debug printTree(_end.left, 0);
+ throw e;
+ }
+ }
+ }
+
+ /**
+ Formats the RedBlackTree into a sink function. For more info see $(D
+ std.format.formatValue). Note that this only is available when the
+ element type can be formatted. Otherwise, the default toString from
+ Object is used.
+ */
+ static if (is(typeof((){FormatSpec!(char) fmt; formatValue((const(char)[]) {}, ConstRange.init, fmt);})))
+ {
+ void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) const {
+ sink("RedBlackTree(");
+ sink.formatValue(this[], fmt);
+ sink(")");
+ }
+ }
+
+ /**
+ * Constructor. Pass in an array of elements, or individual elements to
+ * initialize the tree with.
+ */
+ this(Elem[] elems...)
+ {
+ _setup();
+ stableInsert(elems);
+ }
+
+ /**
+ * Constructor. Pass in a range of elements to initialize the tree with.
+ */
+ this(Stuff)(Stuff stuff) if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, Elem))
+ {
+ _setup();
+ stableInsert(stuff);
+ }
+
+ ///
+ this()
+ {
+ _setup();
+ }
+
+ private this(Node end, size_t length)
+ {
+ _end = end;
+ _begin = end.leftmost;
+ _length = length;
+ }
+}
+
+//Verify Example for removeKey.
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+ auto rbt = redBlackTree!true(0, 1, 1, 1, 4, 5, 7);
+ rbt.removeKey(1, 4, 7);
+ assert(equal(rbt[], [0, 1, 1, 5]));
+ rbt.removeKey(1, 1, 0);
+ assert(equal(rbt[], [5]));
+}
+
+//Tests for removeKey
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+ {
+ auto rbt = redBlackTree(["hello", "world", "foo", "bar"]);
+ assert(equal(rbt[], ["bar", "foo", "hello", "world"]));
+ assert(rbt.removeKey("hello") == 1);
+ assert(equal(rbt[], ["bar", "foo", "world"]));
+ assert(rbt.removeKey("hello") == 0);
+ assert(equal(rbt[], ["bar", "foo", "world"]));
+ assert(rbt.removeKey("hello", "foo", "bar") == 2);
+ assert(equal(rbt[], ["world"]));
+ assert(rbt.removeKey(["", "world", "hello"]) == 1);
+ assert(rbt.empty);
+ }
+
+ {
+ auto rbt = redBlackTree([1, 2, 12, 27, 4, 500]);
+ assert(equal(rbt[], [1, 2, 4, 12, 27, 500]));
+ assert(rbt.removeKey(1u) == 1);
+ assert(equal(rbt[], [2, 4, 12, 27, 500]));
+ assert(rbt.removeKey(cast(byte) 1) == 0);
+ assert(equal(rbt[], [2, 4, 12, 27, 500]));
+ assert(rbt.removeKey(1, 12u, cast(byte) 27) == 2);
+ assert(equal(rbt[], [2, 4, 500]));
+ assert(rbt.removeKey([cast(short) 0, cast(short) 500, cast(short) 1]) == 1);
+ assert(equal(rbt[], [2, 4]));
+ }
+}
+
+@safe pure unittest
+{
+ void test(T)()
+ {
+ auto rt1 = new RedBlackTree!(T, "a < b", false)();
+ auto rt2 = new RedBlackTree!(T, "a < b", true)();
+ auto rt3 = new RedBlackTree!(T, "a > b", false)();
+ auto rt4 = new RedBlackTree!(T, "a > b", true)();
+ }
+
+ test!long();
+ test!ulong();
+ test!int();
+ test!uint();
+ test!short();
+ test!ushort();
+ test!byte();
+ test!byte();
+}
+
+import std.range.primitives : isInputRange, isSomeString, ElementType;
+import std.traits : isArray;
+
+/++
+ Convenience function for creating a $(D RedBlackTree!E) from a list of
+ values.
+
+ Params:
+ allowDuplicates = Whether duplicates should be allowed (optional, default: false)
+ less = predicate to sort by (optional)
+ elems = elements to insert into the rbtree (variadic arguments)
+ range = range elements to insert into the rbtree (alternative to elems)
+ +/
+auto redBlackTree(E)(E[] elems...)
+{
+ return new RedBlackTree!E(elems);
+}
+
+/++ Ditto +/
+auto redBlackTree(bool allowDuplicates, E)(E[] elems...)
+{
+ return new RedBlackTree!(E, "a < b", allowDuplicates)(elems);
+}
+
+/++ Ditto +/
+auto redBlackTree(alias less, E)(E[] elems...)
+if (is(typeof(binaryFun!less(E.init, E.init))))
+{
+ return new RedBlackTree!(E, less)(elems);
+}
+
+/++ Ditto +/
+auto redBlackTree(alias less, bool allowDuplicates, E)(E[] elems...)
+if (is(typeof(binaryFun!less(E.init, E.init))))
+{
+ //We shouldn't need to instantiate less here, but for some reason,
+ //dmd can't handle it if we don't (even though the template which
+ //takes less but not allowDuplicates works just fine).
+ return new RedBlackTree!(E, binaryFun!less, allowDuplicates)(elems);
+}
+
+/++ Ditto +/
+auto redBlackTree(Stuff)(Stuff range)
+if (isInputRange!Stuff && !isArray!(Stuff))
+{
+ return new RedBlackTree!(ElementType!Stuff)(range);
+}
+
+/++ Ditto +/
+auto redBlackTree(bool allowDuplicates, Stuff)(Stuff range)
+if (isInputRange!Stuff && !isArray!(Stuff))
+{
+ return new RedBlackTree!(ElementType!Stuff, "a < b", allowDuplicates)(range);
+}
+
+/++ Ditto +/
+auto redBlackTree(alias less, Stuff)(Stuff range)
+if ( is(typeof(binaryFun!less((ElementType!Stuff).init, (ElementType!Stuff).init)))
+ && isInputRange!Stuff && !isArray!(Stuff))
+{
+ return new RedBlackTree!(ElementType!Stuff, less)(range);
+}
+
+/++ Ditto +/
+auto redBlackTree(alias less, bool allowDuplicates, Stuff)(Stuff range)
+if ( is(typeof(binaryFun!less((ElementType!Stuff).init, (ElementType!Stuff).init)))
+ && isInputRange!Stuff && !isArray!(Stuff))
+{
+ //We shouldn't need to instantiate less here, but for some reason,
+ //dmd can't handle it if we don't (even though the template which
+ //takes less but not allowDuplicates works just fine).
+ return new RedBlackTree!(ElementType!Stuff, binaryFun!less, allowDuplicates)(range);
+}
+
+///
+@safe pure unittest
+{
+ 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));
+}
+
+//Combinations not in examples.
+@safe pure unittest
+{
+ auto rbt1 = redBlackTree!(true, string)("hello", "hello");
+ auto rbt2 = redBlackTree!((a, b){return a < b;}, double)(5.1, 2.3);
+ auto rbt3 = redBlackTree!("a > b", true, string)("hello", "world");
+}
+
+//Range construction.
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : iota;
+ auto rbt = new RedBlackTree!(int, "a > b")(iota(5));
+ assert(equal(rbt[], [4, 3, 2, 1, 0]));
+}
+
+
+// construction with arrays
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto rbt = redBlackTree!"a > b"([0, 1, 2, 3, 4]);
+ assert(equal(rbt[], [4, 3, 2, 1, 0]));
+
+ auto rbt2 = redBlackTree!"a > b"(["a", "b"]);
+ assert(equal(rbt2[], ["b", "a"]));
+
+ auto rbt3 = redBlackTree!"a > b"([1, 2]);
+ assert(equal(rbt3[], [2, 1]));
+
+ auto rbt4 = redBlackTree([0, 1, 7, 5]);
+ assert(equal(rbt4[], [0, 1, 5, 7]));
+
+ auto rbt5 = redBlackTree(["hello", "world"]);
+ assert(equal(rbt5[], ["hello", "world"]));
+
+ auto rbt6 = redBlackTree!true([0, 1, 5, 7, 5]);
+ assert(equal(rbt6[], [0, 1, 5, 5, 7]));
+
+ auto rbt7 = redBlackTree!"a > b"([0, 1, 5, 7]);
+ assert(equal(rbt7[], [7, 5, 1, 0]));
+
+ auto rbt8 = redBlackTree!("a > b", true)([0.1, 1.3, 5.9, 7.2, 5.9]);
+ assert(equal(rbt8[], [7.2, 5.9, 5.9, 1.3, 0.1]));
+}
+
+// convenience wrapper range construction
+@safe pure unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : chain, iota;
+
+ auto rbt = redBlackTree(iota(3));
+ assert(equal(rbt[], [0, 1, 2]));
+
+ auto rbt2 = redBlackTree!"a > b"(iota(2));
+ assert(equal(rbt2[], [1, 0]));
+
+ auto rbt3 = redBlackTree(chain([0, 1], [7, 5]));
+ assert(equal(rbt3[], [0, 1, 5, 7]));
+
+ auto rbt4 = redBlackTree(chain(["hello"], ["world"]));
+ assert(equal(rbt4[], ["hello", "world"]));
+
+ auto rbt5 = redBlackTree!true(chain([0, 1], [5, 7, 5]));
+ assert(equal(rbt5[], [0, 1, 5, 5, 7]));
+
+ auto rbt6 = redBlackTree!("a > b", true)(chain([0.1, 1.3], [5.9, 7.2, 5.9]));
+ assert(equal(rbt6[], [7.2, 5.9, 5.9, 1.3, 0.1]));
+}
+
+@safe pure unittest
+{
+ import std.array : array;
+
+ auto rt1 = redBlackTree(5, 4, 3, 2, 1);
+ assert(rt1.length == 5);
+ assert(array(rt1[]) == [1, 2, 3, 4, 5]);
+
+ auto rt2 = redBlackTree!"a > b"(1.1, 2.1);
+ assert(rt2.length == 2);
+ assert(array(rt2[]) == [2.1, 1.1]);
+
+ auto rt3 = redBlackTree!true(5, 5, 4);
+ assert(rt3.length == 3);
+ assert(array(rt3[]) == [4, 5, 5]);
+
+ auto rt4 = redBlackTree!string("hello", "hello");
+ assert(rt4.length == 1);
+ assert(array(rt4[]) == ["hello"]);
+}
+
+@system unittest
+{
+ import std.conv : to;
+
+ auto rt1 = redBlackTree!string();
+ assert(rt1.to!string == "RedBlackTree([])");
+
+ auto rt2 = redBlackTree!string("hello");
+ assert(rt2.to!string == "RedBlackTree([\"hello\"])");
+
+ auto rt3 = redBlackTree!string("hello", "world", "!");
+ assert(rt3.to!string == "RedBlackTree([\"!\", \"hello\", \"world\"])");
+
+ // type deduction can be done automatically
+ auto rt4 = redBlackTree(["hello"]);
+ assert(rt4.to!string == "RedBlackTree([\"hello\"])");
+}
+
+//constness checks
+@safe pure unittest
+{
+ const rt1 = redBlackTree(5,4,3,2,1);
+ static assert(is(typeof(rt1.length)));
+ static assert(is(typeof(5 in rt1)));
+
+ static assert(is(typeof(rt1.upperBound(3).front) == const(int)));
+ import std.algorithm.comparison : equal;
+ assert(rt1.upperBound(3).equal([4, 5]));
+ assert(rt1.lowerBound(3).equal([1, 2]));
+ assert(rt1.equalRange(3).equal([3]));
+ assert(rt1[].equal([1, 2, 3, 4, 5]));
+}
+
+//immutable checks
+@safe pure unittest
+{
+ immutable rt1 = redBlackTree(5,4,3,2,1);
+ static assert(is(typeof(rt1.length)));
+
+ static assert(is(typeof(rt1.upperBound(3).front) == immutable(int)));
+ import std.algorithm.comparison : equal;
+ assert(rt1.upperBound(2).equal([3, 4, 5]));
+}
+
+// issue 15941
+@safe pure unittest
+{
+ class C {}
+ RedBlackTree!(C, "cast(void*)a < cast(void*) b") tree;
+}
+
+@safe pure unittest // const/immutable elements (issue 17519)
+{
+ RedBlackTree!(immutable int) t1;
+ RedBlackTree!(const int) t2;
+
+ import std.algorithm.iteration : map;
+ static struct S { int* p; }
+ auto t3 = new RedBlackTree!(immutable S, (a, b) => *a.p < *b.p);
+ t3.insert([1, 2, 3].map!(x => immutable S(new int(x))));
+ static assert(!__traits(compiles, *t3.front.p = 4));
+ assert(*t3.front.p == 1);
+}
diff --git a/libphobos/src/std/container/slist.d b/libphobos/src/std/container/slist.d
new file mode 100644
index 0000000..820b0bb
--- /dev/null
+++ b/libphobos/src/std/container/slist.d
@@ -0,0 +1,846 @@
+/**
+This module implements a singly-linked list container.
+It can be used as a stack.
+
+This module is a submodule of $(MREF std, container).
+
+Source: $(PHOBOSSRC std/container/_slist.d)
+
+Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
+
+License: Distributed under the Boost Software License, Version 1.0.
+(See accompanying file LICENSE_1_0.txt or copy at $(HTTP
+boost.org/LICENSE_1_0.txt)).
+
+Authors: $(HTTP erdani.com, Andrei Alexandrescu)
+
+$(SCRIPT inhibitQuickIndex = 1;)
+*/
+module std.container.slist;
+
+///
+@safe unittest
+{
+ 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);
+}
+
+public import std.container.util;
+
+/**
+ Implements a simple and fast singly-linked list.
+ It can be used as a stack.
+
+ $(D SList) uses reference semantics.
+ */
+struct SList(T)
+{
+ import std.exception : enforce;
+ import std.range : Take;
+ import std.range.primitives : isInputRange, isForwardRange, ElementType;
+ import std.traits : isImplicitlyConvertible;
+
+ private struct Node
+ {
+ Node * _next;
+ T _payload;
+ }
+ private struct NodeWithoutPayload
+ {
+ Node* _next;
+ }
+ static assert(NodeWithoutPayload._next.offsetof == Node._next.offsetof);
+
+ private Node * _root;
+
+ private void initialize() @trusted nothrow pure
+ {
+ if (_root) return;
+ _root = cast (Node*) new NodeWithoutPayload();
+ }
+
+ private ref inout(Node*) _first() @property @safe nothrow pure inout
+ {
+ assert(_root);
+ return _root._next;
+ }
+
+ private static Node * findLastNode(Node * n)
+ {
+ assert(n);
+ auto ahead = n._next;
+ while (ahead)
+ {
+ n = ahead;
+ ahead = n._next;
+ }
+ return n;
+ }
+
+ private static Node * findLastNode(Node * n, size_t limit)
+ {
+ assert(n && limit);
+ auto ahead = n._next;
+ while (ahead)
+ {
+ if (!--limit) break;
+ n = ahead;
+ ahead = n._next;
+ }
+ return n;
+ }
+
+ private static Node * findNode(Node * n, Node * findMe)
+ {
+ assert(n);
+ auto ahead = n._next;
+ while (ahead != findMe)
+ {
+ n = ahead;
+ enforce(n);
+ ahead = n._next;
+ }
+ return n;
+ }
+
+/**
+Constructor taking a number of nodes
+ */
+ this(U)(U[] values...) if (isImplicitlyConvertible!(U, T))
+ {
+ insertFront(values);
+ }
+
+/**
+Constructor taking an input range
+ */
+ this(Stuff)(Stuff stuff)
+ if (isInputRange!Stuff
+ && isImplicitlyConvertible!(ElementType!Stuff, T)
+ && !is(Stuff == T[]))
+ {
+ insertFront(stuff);
+ }
+
+/**
+Comparison for equality.
+
+Complexity: $(BIGOH min(n, n1)) where $(D n1) is the number of
+elements in $(D rhs).
+ */
+ bool opEquals(const SList rhs) const
+ {
+ return opEquals(rhs);
+ }
+
+ /// ditto
+ bool opEquals(ref const SList rhs) const
+ {
+ if (_root is rhs._root) return true;
+ if (_root is null) return rhs._root is null || rhs._first is null;
+ if (rhs._root is null) return _root is null || _first is null;
+
+ const(Node) * n1 = _first, n2 = rhs._first;
+
+ for (;; n1 = n1._next, n2 = n2._next)
+ {
+ if (!n1) return !n2;
+ if (!n2 || n1._payload != n2._payload) return false;
+ }
+ }
+
+/**
+Defines the container's primary range, which embodies a forward range.
+ */
+ struct Range
+ {
+ private Node * _head;
+ private this(Node * p) { _head = p; }
+
+ /// Input range primitives.
+ @property bool empty() const { return !_head; }
+
+ /// ditto
+ @property ref T front()
+ {
+ assert(!empty, "SList.Range.front: Range is empty");
+ return _head._payload;
+ }
+
+ /// ditto
+ void popFront()
+ {
+ assert(!empty, "SList.Range.popFront: Range is empty");
+ _head = _head._next;
+ }
+
+ /// Forward range primitive.
+ @property Range save() { return this; }
+
+ T moveFront()
+ {
+ import std.algorithm.mutation : move;
+
+ assert(!empty, "SList.Range.moveFront: Range is empty");
+ return move(_head._payload);
+ }
+
+ bool sameHead(Range rhs)
+ {
+ return _head && _head == rhs._head;
+ }
+ }
+
+ @safe unittest
+ {
+ static assert(isForwardRange!Range);
+ }
+
+/**
+Property returning $(D true) if and only if the container has no
+elements.
+
+Complexity: $(BIGOH 1)
+ */
+ @property bool empty() const
+ {
+ return _root is null || _first is null;
+ }
+
+/**
+Duplicates the container. The elements themselves are not transitively
+duplicated.
+
+Complexity: $(BIGOH n).
+ */
+ @property SList dup()
+ {
+ return SList(this[]);
+ }
+
+/**
+Returns a range that iterates over all elements of the container, in
+forward order.
+
+Complexity: $(BIGOH 1)
+ */
+ Range opSlice()
+ {
+ if (empty)
+ return Range(null);
+ else
+ return Range(_first);
+ }
+
+/**
+Forward to $(D opSlice().front).
+
+Complexity: $(BIGOH 1)
+ */
+ @property ref T front()
+ {
+ assert(!empty, "SList.front: List is empty");
+ return _first._payload;
+ }
+
+ @safe unittest
+ {
+ auto s = SList!int(1, 2, 3);
+ s.front = 42;
+ assert(s == SList!int(42, 2, 3));
+ }
+
+/**
+Returns a new $(D SList) that's the concatenation of $(D this) and its
+argument. $(D opBinaryRight) is only defined if $(D Stuff) does not
+define $(D opBinary).
+ */
+ SList opBinary(string op, Stuff)(Stuff rhs)
+ if (op == "~" && is(typeof(SList(rhs))))
+ {
+ auto toAdd = SList(rhs);
+ if (empty) return toAdd;
+ // TODO: optimize
+ auto result = dup;
+ auto n = findLastNode(result._first);
+ n._next = toAdd._first;
+ return result;
+ }
+
+ /// ditto
+ SList opBinaryRight(string op, Stuff)(Stuff lhs)
+ if (op == "~" && !is(typeof(lhs.opBinary!"~"(this))) && is(typeof(SList(lhs))))
+ {
+ auto toAdd = SList(lhs);
+ if (empty) return toAdd;
+ auto result = dup;
+ result.insertFront(toAdd[]);
+ return result;
+ }
+
+/**
+Removes all contents from the $(D SList).
+
+Postcondition: $(D empty)
+
+Complexity: $(BIGOH 1)
+ */
+ void clear()
+ {
+ if (_root)
+ _first = null;
+ }
+
+/**
+Reverses SList in-place. Performs no memory allocation.
+
+Complexity: $(BIGOH n)
+ */
+ void reverse()
+ {
+ if (!empty)
+ {
+ Node* prev;
+ while (_first)
+ {
+ auto next = _first._next;
+ _first._next = prev;
+ prev = _first;
+ _first = next;
+ }
+ _first = prev;
+ }
+ }
+
+/**
+Inserts $(D stuff) to the front of the container. $(D stuff) can be a
+value convertible to $(D T) or a range of objects convertible to $(D
+T). The stable version behaves the same, but guarantees that ranges
+iterating over the container are never invalidated.
+
+Returns: The number of elements inserted
+
+Complexity: $(BIGOH m), where $(D m) is the length of $(D stuff)
+ */
+ size_t insertFront(Stuff)(Stuff stuff)
+ if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
+ {
+ initialize();
+ size_t result;
+ Node * n, newRoot;
+ foreach (item; stuff)
+ {
+ auto newNode = new Node(null, item);
+ (newRoot ? n._next : newRoot) = newNode;
+ n = newNode;
+ ++result;
+ }
+ if (!n) return 0;
+ // Last node points to the old root
+ n._next = _first;
+ _first = newRoot;
+ return result;
+ }
+
+ /// ditto
+ size_t insertFront(Stuff)(Stuff stuff)
+ if (isImplicitlyConvertible!(Stuff, T))
+ {
+ initialize();
+ auto newRoot = new Node(_first, stuff);
+ _first = newRoot;
+ return 1;
+ }
+
+/// ditto
+ alias insert = insertFront;
+
+/// ditto
+ alias stableInsert = insert;
+
+ /// ditto
+ alias stableInsertFront = insertFront;
+
+/**
+Picks one value in an unspecified position in the container, removes
+it from the container, and returns it. The stable version behaves the same,
+but guarantees that ranges iterating over the container are never invalidated.
+
+Precondition: $(D !empty)
+
+Returns: The element removed.
+
+Complexity: $(BIGOH 1).
+ */
+ T removeAny()
+ {
+ import std.algorithm.mutation : move;
+
+ assert(!empty, "SList.removeAny: List is empty");
+ auto result = move(_first._payload);
+ _first = _first._next;
+ return result;
+ }
+ /// ditto
+ alias stableRemoveAny = removeAny;
+
+/**
+Removes the value at the front of the container. The stable version
+behaves the same, but guarantees that ranges iterating over the
+container are never invalidated.
+
+Precondition: $(D !empty)
+
+Complexity: $(BIGOH 1).
+ */
+ void removeFront()
+ {
+ assert(!empty, "SList.removeFront: List is empty");
+ _first = _first._next;
+ }
+
+ /// ditto
+ alias stableRemoveFront = removeFront;
+
+/**
+Removes $(D howMany) values at the front or back of the
+container. Unlike the unparameterized versions above, these functions
+do not throw if they could not remove $(D howMany) elements. Instead,
+if $(D howMany > n), all elements are removed. The returned value is
+the effective number of elements removed. The stable version behaves
+the same, but guarantees that ranges iterating over the container are
+never invalidated.
+
+Returns: The number of elements removed
+
+Complexity: $(BIGOH howMany * log(n)).
+ */
+ size_t removeFront(size_t howMany)
+ {
+ size_t result;
+ while (_first && result < howMany)
+ {
+ _first = _first._next;
+ ++result;
+ }
+ return result;
+ }
+
+ /// ditto
+ alias stableRemoveFront = removeFront;
+
+/**
+Inserts $(D stuff) after range $(D r), which must be a range
+previously extracted from this container. Given that all ranges for a
+list end at the end of the list, this function essentially appends to
+the list and uses $(D r) as a potentially fast way to reach the last
+node in the list. Ideally $(D r) is positioned near or at the last
+element of the list.
+
+$(D stuff) can be a value convertible to $(D T) or a range of objects
+convertible to $(D T). The stable version behaves the same, but
+guarantees that ranges iterating over the container are never
+invalidated.
+
+Returns: The number of values inserted.
+
+Complexity: $(BIGOH k + m), where $(D k) is the number of elements in
+$(D r) and $(D m) is the length of $(D stuff).
+
+Example:
+--------------------
+auto sl = SList!string(["a", "b", "d"]);
+sl.insertAfter(sl[], "e"); // insert at the end (slowest)
+assert(std.algorithm.equal(sl[], ["a", "b", "d", "e"]));
+sl.insertAfter(std.range.take(sl[], 2), "c"); // insert after "b"
+assert(std.algorithm.equal(sl[], ["a", "b", "c", "d", "e"]));
+--------------------
+ */
+
+ size_t insertAfter(Stuff)(Range r, Stuff stuff)
+ {
+ initialize();
+ if (!_first)
+ {
+ enforce(!r._head);
+ return insertFront(stuff);
+ }
+ enforce(r._head);
+ auto n = findLastNode(r._head);
+ SList tmp;
+ auto result = tmp.insertFront(stuff);
+ n._next = tmp._first;
+ return result;
+ }
+
+/**
+Similar to $(D insertAfter) above, but accepts a range bounded in
+count. This is important for ensuring fast insertions in the middle of
+the list. For fast insertions after a specified position $(D r), use
+$(D insertAfter(take(r, 1), stuff)). The complexity of that operation
+only depends on the number of elements in $(D stuff).
+
+Precondition: $(D r.original.empty || r.maxLength > 0)
+
+Returns: The number of values inserted.
+
+Complexity: $(BIGOH k + m), where $(D k) is the number of elements in
+$(D r) and $(D m) is the length of $(D stuff).
+ */
+ size_t insertAfter(Stuff)(Take!Range r, Stuff stuff)
+ {
+ auto orig = r.source;
+ if (!orig._head)
+ {
+ // Inserting after a null range counts as insertion to the
+ // front
+ return insertFront(stuff);
+ }
+ enforce(!r.empty);
+ // Find the last valid element in the range
+ foreach (i; 1 .. r.maxLength)
+ {
+ if (!orig._head._next) break;
+ orig.popFront();
+ }
+ // insert here
+ SList tmp;
+ tmp.initialize();
+ tmp._first = orig._head._next;
+ auto result = tmp.insertFront(stuff);
+ orig._head._next = tmp._first;
+ return result;
+ }
+
+/// ditto
+ alias stableInsertAfter = insertAfter;
+
+/**
+Removes a range from the list in linear time.
+
+Returns: An empty range.
+
+Complexity: $(BIGOH n)
+ */
+ Range linearRemove(Range r)
+ {
+ if (!_first)
+ {
+ enforce(!r._head);
+ return this[];
+ }
+ auto n = findNode(_root, r._head);
+ n._next = null;
+ return Range(null);
+ }
+
+/**
+Removes a $(D Take!Range) from the list in linear time.
+
+Returns: A range comprehending the elements after the removed range.
+
+Complexity: $(BIGOH n)
+ */
+ Range linearRemove(Take!Range r)
+ {
+ auto orig = r.source;
+ // We have something to remove here
+ if (orig._head == _first)
+ {
+ // remove straight from the head of the list
+ for (; !r.empty; r.popFront())
+ {
+ removeFront();
+ }
+ return this[];
+ }
+ if (!r.maxLength)
+ {
+ // Nothing to remove, return the range itself
+ return orig;
+ }
+ // Remove from somewhere in the middle of the list
+ enforce(_first);
+ auto n1 = findNode(_root, orig._head);
+ auto n2 = findLastNode(orig._head, r.maxLength);
+ n1._next = n2._next;
+ return Range(n1._next);
+ }
+
+/// ditto
+ alias stableLinearRemove = linearRemove;
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto a = SList!int(5);
+ auto b = a;
+ auto r = a[];
+ a.insertFront(1);
+ b.insertFront(2);
+ assert(equal(a[], [2, 1, 5]));
+ assert(equal(b[], [2, 1, 5]));
+ r.front = 9;
+ assert(equal(a[], [2, 1, 9]));
+ assert(equal(b[], [2, 1, 9]));
+}
+
+@safe unittest
+{
+ auto s = SList!int(1, 2, 3);
+ auto n = s.findLastNode(s._root);
+ assert(n && n._payload == 3);
+}
+
+@safe unittest
+{
+ import std.range.primitives;
+ auto s = SList!int(1, 2, 5, 10);
+ assert(walkLength(s[]) == 4);
+}
+
+@safe unittest
+{
+ import std.range : take;
+ auto src = take([0, 1, 2, 3], 3);
+ auto s = SList!int(src);
+ assert(s == SList!int(0, 1, 2));
+}
+
+@safe unittest
+{
+ auto a = SList!int();
+ auto b = SList!int();
+ auto c = a ~ b[];
+ assert(c.empty);
+}
+
+@safe unittest
+{
+ auto a = SList!int(1, 2, 3);
+ auto b = SList!int(4, 5, 6);
+ auto c = a ~ b[];
+ assert(c == SList!int(1, 2, 3, 4, 5, 6));
+}
+
+@safe unittest
+{
+ auto a = SList!int(1, 2, 3);
+ auto b = [4, 5, 6];
+ auto c = a ~ b;
+ assert(c == SList!int(1, 2, 3, 4, 5, 6));
+}
+
+@safe unittest
+{
+ auto a = SList!int(1, 2, 3);
+ auto c = a ~ 4;
+ assert(c == SList!int(1, 2, 3, 4));
+}
+
+@safe unittest
+{
+ auto a = SList!int(2, 3, 4);
+ auto b = 1 ~ a;
+ assert(b == SList!int(1, 2, 3, 4));
+}
+
+@safe unittest
+{
+ auto a = [1, 2, 3];
+ auto b = SList!int(4, 5, 6);
+ auto c = a ~ b;
+ assert(c == SList!int(1, 2, 3, 4, 5, 6));
+}
+
+@safe unittest
+{
+ auto s = SList!int(1, 2, 3, 4);
+ s.insertFront([ 42, 43 ]);
+ assert(s == SList!int(42, 43, 1, 2, 3, 4));
+}
+
+@safe unittest
+{
+ auto s = SList!int(1, 2, 3);
+ assert(s.removeAny() == 1);
+ assert(s == SList!int(2, 3));
+ assert(s.stableRemoveAny() == 2);
+ assert(s == SList!int(3));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto s = SList!int(1, 2, 3);
+ s.removeFront();
+ assert(equal(s[], [2, 3]));
+ s.stableRemoveFront();
+ assert(equal(s[], [3]));
+}
+
+@safe unittest
+{
+ auto s = SList!int(1, 2, 3, 4, 5, 6, 7);
+ assert(s.removeFront(3) == 3);
+ assert(s == SList!int(4, 5, 6, 7));
+}
+
+@safe unittest
+{
+ auto a = SList!int(1, 2, 3);
+ auto b = SList!int(1, 2, 3);
+ assert(a.insertAfter(a[], b[]) == 3);
+}
+
+@safe unittest
+{
+ import std.range : take;
+ auto s = SList!int(1, 2, 3, 4);
+ auto r = take(s[], 2);
+ assert(s.insertAfter(r, 5) == 1);
+ assert(s == SList!int(1, 2, 5, 3, 4));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : take;
+
+ // insertAfter documentation example
+ auto sl = SList!string(["a", "b", "d"]);
+ sl.insertAfter(sl[], "e"); // insert at the end (slowest)
+ assert(equal(sl[], ["a", "b", "d", "e"]));
+ sl.insertAfter(take(sl[], 2), "c"); // insert after "b"
+ assert(equal(sl[], ["a", "b", "c", "d", "e"]));
+}
+
+@safe unittest
+{
+ import std.range.primitives;
+ auto s = SList!int(1, 2, 3, 4, 5);
+ auto r = s[];
+ popFrontN(r, 3);
+ auto r1 = s.linearRemove(r);
+ assert(s == SList!int(1, 2, 3));
+ assert(r1.empty);
+}
+
+@safe unittest
+{
+ auto s = SList!int(1, 2, 3, 4, 5);
+ auto r = s[];
+ auto r1 = s.linearRemove(r);
+ assert(s == SList!int());
+ assert(r1.empty);
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range;
+
+ auto s = SList!int(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
+ auto r = s[];
+ popFrontN(r, 3);
+ auto r1 = take(r, 4);
+ assert(equal(r1, [4, 5, 6, 7]));
+ auto r2 = s.linearRemove(r1);
+ assert(s == SList!int(1, 2, 3, 8, 9, 10));
+ assert(equal(r2, [8, 9, 10]));
+}
+
+@safe unittest
+{
+ import std.range.primitives;
+ auto lst = SList!int(1, 5, 42, 9);
+ assert(!lst.empty);
+ assert(lst.front == 1);
+ assert(walkLength(lst[]) == 4);
+
+ auto lst2 = lst ~ [ 1, 2, 3 ];
+ assert(walkLength(lst2[]) == 7);
+
+ auto lst3 = lst ~ [ 7 ];
+ assert(walkLength(lst3[]) == 5);
+}
+
+@safe unittest
+{
+ auto s = make!(SList!int)(1, 2, 3);
+}
+
+@safe unittest
+{
+ // 5193
+ static struct Data
+ {
+ const int val;
+ }
+ SList!Data list;
+}
+
+@safe unittest
+{
+ auto s = SList!int([1, 2, 3]);
+ s.front = 5; //test frontAssign
+ assert(s.front == 5);
+ auto r = s[];
+ r.front = 1; //test frontAssign
+ assert(r.front == 1);
+}
+
+@safe unittest
+{
+ // issue 14920
+ SList!int s;
+ s.insertAfter(s[], 1);
+ assert(s.front == 1);
+}
+
+@safe unittest
+{
+ // issue 15659
+ SList!int s;
+ s.clear();
+}
+
+@safe unittest
+{
+ SList!int s;
+ s.reverse();
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+
+ auto s = SList!int([1, 2, 3]);
+ assert(s[].equal([1, 2, 3]));
+
+ s.reverse();
+ assert(s[].equal([3, 2, 1]));
+}
diff --git a/libphobos/src/std/container/util.d b/libphobos/src/std/container/util.d
new file mode 100644
index 0000000..5be9e7d
--- /dev/null
+++ b/libphobos/src/std/container/util.d
@@ -0,0 +1,189 @@
+/**
+This module contains some common utilities used by containers.
+
+This module is a submodule of $(MREF std, container).
+
+Source: $(PHOBOSSRC std/container/_util.d)
+
+Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
+
+License: Distributed under the Boost Software License, Version 1.0.
+(See accompanying file LICENSE_1_0.txt or copy at $(HTTP
+boost.org/LICENSE_1_0.txt)).
+
+Authors: $(HTTP erdani.com, Andrei Alexandrescu)
+
+$(SCRIPT inhibitQuickIndex = 1;)
+*/
+module std.container.util;
+
+/**
+Returns an initialized object. This function is mainly for eliminating
+construction differences between structs and classes. It allows code to not
+worry about whether the type it's constructing is a struct or a class.
+ */
+template make(T)
+if (is(T == struct) || is(T == class))
+{
+ T make(Args...)(Args arguments)
+ if (is(T == struct) && __traits(compiles, T(arguments)))
+ {
+ // constructing an std.container.Array without arguments,
+ // does not initialize its payload and is equivalent
+ // to a null reference. We therefore construct an empty container
+ // by passing an empty array to its constructor.
+ // Issue #13872.
+ static if (arguments.length == 0)
+ {
+ import std.range.primitives : ElementType;
+ alias ET = ElementType!(T.Range);
+ return T(ET[].init);
+ }
+ else
+ return T(arguments);
+ }
+
+ T make(Args...)(Args arguments)
+ if (is(T == class) && __traits(compiles, new T(arguments)))
+ {
+ return new T(arguments);
+ }
+}
+
+
+///
+@system unittest
+{
+ 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]));
+}
+
+@system unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.container;
+
+ auto arr1 = make!(Array!dchar)();
+ assert(arr1.empty);
+ auto arr2 = make!(Array!dchar)("hello"d);
+ assert(equal(arr2[], "hello"d));
+
+ auto rtb1 = make!(RedBlackTree!dchar)();
+ assert(rtb1.empty);
+ auto rtb2 = make!(RedBlackTree!dchar)('h', 'e', 'l', 'l', 'o');
+ assert(equal(rtb2[], "ehlo"d));
+}
+
+// Issue 8895
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.container;
+
+ auto a = make!(DList!int)(1,2,3,4);
+ auto b = make!(DList!int)(1,2,3,4);
+ auto c = make!(DList!int)(1,2,3,5);
+ auto d = make!(DList!int)(1,2,3,4,5);
+ assert(a == b); // this better terminate!
+ assert(a != c);
+ assert(a != d);
+}
+
+/**
+ * Convenience function for constructing a generic container.
+ */
+template make(alias Container, Args...)
+if (!is(Container))
+{
+ import std.range : isInputRange, isInfinite;
+ import std.traits : isDynamicArray;
+
+ auto make(Range)(Range range)
+ if (!isDynamicArray!Range && isInputRange!Range && !isInfinite!Range)
+ {
+ import std.range : ElementType;
+ return .make!(Container!(ElementType!Range, Args))(range);
+ }
+
+ auto make(T)(T[] items...)
+ if (!isInfinite!T)
+ {
+ return .make!(Container!(T, Args))(items);
+ }
+}
+
+/// forbid construction from infinite range
+@safe unittest
+{
+ 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.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]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.container.rbtree;
+
+ auto rbtmin = make!(RedBlackTree, "a < b", false)(3, 2, 2, 1);
+ assert(equal(rbtmin[], [1, 2, 3]));
+}
+
+// Issue 13872
+@system unittest
+{
+ import std.container;
+
+ auto tree1 = make!(RedBlackTree!int)();
+ auto refToTree1 = tree1;
+ refToTree1.insert(1);
+ assert(1 in tree1);
+
+ auto array1 = make!(Array!int)();
+ auto refToArray1 = array1;
+ refToArray1.insertBack(1);
+ assert(!array1.empty);
+
+ auto slist = make!(SList!int)();
+ auto refToSlist = slist;
+ refToSlist.insert(1);
+ assert(!slist.empty);
+
+ auto dlist = make!(DList!int)();
+ auto refToDList = dlist;
+ refToDList.insert(1);
+ assert(!dlist.empty);
+}