| /** | |
| 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 @nogc 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 scope | |
| { | |
| assert((_first is null) == (_last is null), "DList.Range: Invalidated state"); | |
| return !_first; | |
| } | |
| @property BaseNode* front() return scope | |
| { | |
| assert(!empty, "DList.Range.front: Range is empty"); | |
| return _first; | |
| } | |
| void popFront() scope | |
| { | |
| 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() return scope | |
| { | |
| assert(!empty, "DList.Range.front: Range is empty"); | |
| return _last; | |
| } | |
| void popBack() scope | |
| { | |
| 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 scope { return this; } | |
| } | |
| /** | |
| Implements a doubly-linked list. | |
| `DList` uses reference semantics. | |
| */ | |
| struct DList(T) | |
| { | |
| import std.range : Take; | |
| import std.traits : isMutable; | |
| /* | |
| A Node with a Payload. A PayNode. | |
| */ | |
| struct PayNode | |
| { | |
| BaseNode _base; | |
| alias _base this; | |
| T _payload = T.init; | |
| this (BaseNode _base, T _payload) | |
| { | |
| import std.algorithm.mutation : move; | |
| this._base = _base; | |
| this._payload = move(_payload); | |
| } | |
| 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) | |
| { | |
| import std.algorithm.mutation : move; | |
| static if (isMutable!Stuff) | |
| return (new PayNode(BaseNode(prev, next), move(arg))).asBaseNode(); | |
| else | |
| 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, "Root pointer must not be null"); | |
| return _root._next; | |
| } | |
| ref inout(BaseNode*) _last() @property @safe nothrow pure inout | |
| { | |
| assert(_root, "Root pointer must not be null"); | |
| return _root._prev; | |
| } | |
| } //end private | |
| /** | |
| Constructor taking a number of nodes | |
| */ | |
| this(U)(U[] values...) | |
| if (isImplicitlyConvertible!(U, T)) | |
| { | |
| insertBack(values); | |
| } | |
| /** | |
| Constructor taking an $(REF_ALTTEXT input range, isInputRange, std,range,primitives) | |
| */ | |
| this(Stuff)(Stuff stuff) | |
| if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T)) | |
| { | |
| insertBack(stuff); | |
| } | |
| /** | |
| Comparison for equality. | |
| Complexity: $(BIGOH min(n, n1)) where `n1` is the number of | |
| elements in `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 `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 `DList`. | |
| Postcondition: `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 `opSlice().front`. | |
| Complexity: $(BIGOH 1) | |
| */ | |
| @property ref inout(T) front() inout | |
| { | |
| assert(!empty, "DList.front: List is empty"); | |
| return _first.getPayload!T(); | |
| } | |
| /** | |
| Forward to `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 `DList` that's the concatenation of `this` and its | |
| argument `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 `DList` that's the concatenation of the argument `lhs` | |
| and `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 `rhs` into `this`. | |
| */ | |
| DList opOpAssign(string op, Stuff)(Stuff rhs) | |
| if (op == "~" && is(typeof(insertBack(rhs)))) | |
| { | |
| insertBack(rhs); | |
| return this; | |
| } | |
| /+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/ | |
| /+ BEGIN INSERT FUNCTIONS HERE +/ | |
| /+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/ | |
| /** | |
| Inserts `stuff` to the front/back of the container. `stuff` can be a | |
| value convertible to `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 `m` is the length of `stuff` | |
| */ | |
| 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 `stuff` after range `r`, which must be a non-empty range | |
| previously extracted from this container. | |
| `stuff` can be a value convertible to `T` or a range of objects | |
| convertible to `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 `k` is the number of elements in | |
| `r` and `m` is the length of `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: `!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: `!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 `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 `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 `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 `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); | |
| } | |
| /// ditto | |
| alias stableRemove = remove; | |
| /** | |
| Removes first element of `r`, wich must be a range obtained originally | |
| from this container, from both DList instance and range `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 `r`, wich must be a range obtained originally | |
| from this container, from both DList instance and range `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); | |
| } | |
| /** | |
| `linearRemove` functions as `remove`, but also accepts ranges that are | |
| result the of a `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 stableLinearRemove = linearRemove; | |
| /** | |
| Removes the first occurence of an element from the list in linear time. | |
| Returns: True if the element existed and was successfully removed, false otherwise. | |
| Params: | |
| value = value of the node to be removed | |
| Complexity: $(BIGOH n) | |
| */ | |
| bool linearRemoveElement(T value) | |
| { | |
| import std.algorithm.mutation : move; | |
| auto n1 = findNodeByValue(_root, move(value)); | |
| if (n1) | |
| { | |
| auto n2 = n1._next._next; | |
| BaseNode.connect(n1, n2); | |
| return true; | |
| } | |
| return false; | |
| } | |
| private: | |
| BaseNode* findNodeByValue(BaseNode* n, T value) | |
| { | |
| if (!n) return null; | |
| auto ahead = n._next; | |
| while (ahead && ahead.getPayload!T() != value) | |
| { | |
| n = ahead; | |
| ahead = n._next; | |
| if (ahead == _last._next) return null; | |
| } | |
| return n; | |
| } | |
| // 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; | |
| auto e = DList!int(); | |
| auto b = e.linearRemoveElement(1); | |
| assert(b == false); | |
| assert(e.empty()); | |
| auto a = DList!int(-1, 1, 2, 1, 3, 4); | |
| b = a.linearRemoveElement(1); | |
| assert(equal(a[], [-1, 2, 1, 3, 4])); | |
| assert(b == true); | |
| b = a.linearRemoveElement(-1); | |
| assert(b == true); | |
| assert(equal(a[], [2, 1, 3, 4])); | |
| b = a.linearRemoveElement(1); | |
| assert(b == true); | |
| assert(equal(a[], [2, 3, 4])); | |
| b = a.linearRemoveElement(2); | |
| assert(b == true); | |
| b = a.linearRemoveElement(20); | |
| assert(b == false); | |
| assert(equal(a[], [3, 4])); | |
| b = a.linearRemoveElement(4); | |
| assert(b == true); | |
| assert(equal(a[], [3])); | |
| b = a.linearRemoveElement(3); | |
| assert(b == true); | |
| assert(a.empty()); | |
| a.linearRemoveElement(3); | |
| } | |
| @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); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=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; | |
| // https://issues.dlang.org/show_bug.cgi?id=8905 | |
| auto a = DList!int([1, 2, 3, 4]); | |
| auto r = a[]; | |
| a.stableRemoveBack(); | |
| a.stableInsertBack(7); | |
| assert(a[].equal([1, 2, 3, 7])); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=12566 | |
| @safe unittest | |
| { | |
| auto dl2 = DList!int([2,7]); | |
| dl2.removeFront(); | |
| assert(dl2[].walkLength == 1); | |
| dl2.removeBack(); | |
| assert(dl2.empty, "not empty?!"); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=13076 | |
| @safe unittest | |
| { | |
| DList!int list; | |
| assert(list.empty); | |
| list.clear(); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=13425 | |
| @safe unittest | |
| { | |
| 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 | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=14300 | |
| @safe unittest | |
| { | |
| interface ITest {} | |
| static class Test : ITest {} | |
| DList!ITest().insertBack(new Test()); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=15263 | |
| @safe unittest | |
| { | |
| 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); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=22147 | |
| @safe unittest | |
| { | |
| import std.algorithm.mutation : move; | |
| static struct Item | |
| { | |
| @disable this(this); | |
| int x; | |
| } | |
| auto list = DList!Item(); | |
| list.insertFront(Item(1)); | |
| assert(list[].walkLength == 1); | |
| assert(list.front.x == 1); | |
| auto item = list.moveFront; | |
| item.x = 2; | |
| list.front = move(item); | |
| assert(list.front.x == 2); | |
| list.removeFront(); | |
| assert(list[].walkLength == 0); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=24637 | |
| @safe unittest | |
| { | |
| import std.algorithm.comparison : equal; | |
| struct A | |
| { | |
| int c; | |
| } | |
| DList!A B; | |
| B.insert(A(1)); | |
| assert(B[].equal([A(1)])); | |
| const a = A(3); | |
| B.insert(a); | |
| assert(B[].equal([A(1), A(3)])); | |
| } |