xref: /netbsd-src/external/gpl3/gcc/dist/libphobos/src/std/container/dlist.d (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1 /**
2 This module implements a generic doubly-linked list container.
3 It can be used as a queue, dequeue or stack.
4 
5 This module is a submodule of $(MREF std, container).
6 
7 Source: $(PHOBOSSRC std/container/dlist.d)
8 
9 Copyright: 2010- Andrei Alexandrescu. All rights reserved by the respective holders.
10 
11 License: Distributed under the Boost Software License, Version 1.0.
12 (See accompanying file LICENSE_1_0.txt or copy at $(HTTP
13 boost.org/LICENSE_1_0.txt)).
14 
15 Authors: $(HTTP erdani.com, Andrei Alexandrescu)
16 
17 $(SCRIPT inhibitQuickIndex = 1;)
18 */
19 module std.container.dlist;
20 
21 ///
22 @safe unittest
23 {
24     import std.algorithm.comparison : equal;
25     import std.container : DList;
26 
27     auto s = DList!int(1, 2, 3);
28     assert(equal(s[], [1, 2, 3]));
29 
30     s.removeFront();
31     assert(equal(s[], [2, 3]));
32     s.removeBack();
33     assert(equal(s[], [2]));
34 
35     s.insertFront([4, 5]);
36     assert(equal(s[], [4, 5, 2]));
37     s.insertBack([6, 7]);
38     assert(equal(s[], [4, 5, 2, 6, 7]));
39 
40     // If you want to apply range operations, simply slice it.
41     import std.algorithm.searching : countUntil;
42     import std.range : popFrontN, popBackN, walkLength;
43 
44     auto sl = DList!int([1, 2, 3, 4, 5]);
45     assert(countUntil(sl[], 2) == 1);
46 
47     auto r = sl[];
48     popFrontN(r, 2);
49     popBackN(r, 2);
50     assert(r.equal([3]));
51     assert(walkLength(r) == 1);
52 
53     // DList.Range can be used to remove elements from the list it spans
54     auto nl = DList!int([1, 2, 3, 4, 5]);
55     for (auto rn = nl[]; !rn.empty;)
56         if (rn.front % 2 == 0)
57             nl.popFirstOf(rn);
58         else
59             rn.popFront();
60     assert(equal(nl[], [1, 3, 5]));
61     auto rs = nl[];
62     rs.popFront();
63     nl.remove(rs);
64     assert(equal(nl[], [1]));
65 }
66 
67 import std.range.primitives;
68 import std.traits;
69 
70 public import std.container.util;
71 
72 /+
73 A DList Node without payload. Used to handle the sentinel node (henceforth "sentinode").
74 
75 Also used for parts of the code that don't depend on the payload type.
76  +/
77 private struct BaseNode
78 {
79     private BaseNode* _prev = null;
80     private BaseNode* _next = null;
81 
82     /+
83     Gets the payload associated with this node.
84     This is trusted because all nodes are associated with a payload, even
85     the sentinel node.
86     It is also not possible to mix Nodes in DLists of different types.
87     This function is implemented as a member function here, as UFCS does not
88     work with pointers.
89     +/
inoutBaseNode90     ref inout(T) getPayload(T)() inout @trusted
91     {
92         return (cast(inout(DList!T.PayNode)*)&this)._payload;
93     }
94 
95     // Helper: Given nodes p and n, connects them.
96     static void connect(BaseNode* p, BaseNode* n) @safe nothrow pure
97     {
98         p._next = n;
99         n._prev = p;
100     }
101 }
102 
103 /+
104 The base DList Range. Contains Range primitives that don't depend on payload type.
105  +/
106 private struct DRange
107 {
108     @safe unittest
109     {
110         static assert(isBidirectionalRange!DRange);
111         static assert(is(ElementType!DRange == BaseNode*));
112     }
113 
114 nothrow @safe pure:
115     private BaseNode* _first;
116     private BaseNode* _last;
117 
118     private this(BaseNode* first, BaseNode* last)
119     {
120         assert((first is null) == (last is null), "Dlist.Range.this: Invalid arguments");
121         _first = first;
122         _last = last;
123     }
124     private this(BaseNode* n)
125     {
126         this(n, n);
127     }
128 
129     @property
130     bool empty() const scope
131     {
132         assert((_first is null) == (_last is null), "DList.Range: Invalidated state");
133         return !_first;
134     }
135 
136     @property BaseNode* front() return scope
137     {
138         assert(!empty, "DList.Range.front: Range is empty");
139         return _first;
140     }
141 
142     void popFront() scope
143     {
144         assert(!empty, "DList.Range.popFront: Range is empty");
145         if (_first is _last)
146         {
147             _first = _last = null;
148         }
149         else
150         {
151             assert(_first._next && _first is _first._next._prev, "DList.Range: Invalidated state");
152             _first = _first._next;
153         }
154     }
155 
156     @property BaseNode* back() return scope
157     {
158         assert(!empty, "DList.Range.front: Range is empty");
159         return _last;
160     }
161 
162     void popBack() scope
163     {
164         assert(!empty, "DList.Range.popBack: Range is empty");
165         if (_first is _last)
166         {
167             _first = _last = null;
168         }
169         else
170         {
171             assert(_last._prev && _last is _last._prev._next, "DList.Range: Invalidated state");
172             _last = _last._prev;
173         }
174     }
175 
176     /// Forward range primitive.
177     @property DRange save() return scope { return this; }
178 }
179 
180 /**
181 Implements a doubly-linked list.
182 
183 `DList` uses reference semantics.
184  */
185 struct DList(T)
186 {
187     import std.range : Take;
188 
189     /*
190     A Node with a Payload. A PayNode.
191      */
192     struct PayNode
193     {
194         BaseNode _base;
195         alias _base this;
196 
197         T _payload = T.init;
198 
199         this (BaseNode _base, T _payload)
200         {
201             this._base = _base;
202             this._payload = _payload;
203         }
204 
205         inout(BaseNode)* asBaseNode() inout @trusted
206         {
207             return &_base;
208         }
209     }
210 
211     //The sentinel node
212     private BaseNode* _root;
213 
214   private
215   {
216     //Construct as new PayNode, and returns it as a BaseNode.
217     static BaseNode* createNode(Stuff)(auto ref Stuff arg, BaseNode* prev = null, BaseNode* next = null)
218     {
219         return (new PayNode(BaseNode(prev, next), arg)).asBaseNode();
220     }
221 
222     void initialize() nothrow @safe pure
223     {
224         if (_root) return;
225         //Note: We allocate a PayNode for safety reasons.
226         _root = (new PayNode()).asBaseNode();
227         _root._next = _root._prev = _root;
228     }
229     ref inout(BaseNode*) _first() @property @safe nothrow pure inout
230     {
231         assert(_root, "Root pointer must not be null");
232         return _root._next;
233     }
234     ref inout(BaseNode*) _last() @property @safe nothrow pure inout
235     {
236         assert(_root, "Root pointer must not be null");
237         return _root._prev;
238     }
239   } //end private
240 
241 /**
242 Constructor taking a number of nodes
243      */
244     this(U)(U[] values...) if (isImplicitlyConvertible!(U, T))
245     {
246         insertBack(values);
247     }
248 
249 /**
250 Constructor taking an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
251      */
252     this(Stuff)(Stuff stuff)
253     if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
254     {
255         insertBack(stuff);
256     }
257 
258 /**
259 Comparison for equality.
260 
261 Complexity: $(BIGOH min(n, n1)) where `n1` is the number of
262 elements in `rhs`.
263      */
264     bool opEquals()(ref const DList rhs) const
265     if (is(typeof(front == front)))
266     {
267         const lhs = this;
268         const lroot = lhs._root;
269         const rroot = rhs._root;
270 
271         if (lroot is rroot) return true;
272         if (lroot is null) return rroot is rroot._next;
273         if (rroot is null) return lroot is lroot._next;
274 
275         const(BaseNode)* pl = lhs._first;
276         const(BaseNode)* pr = rhs._first;
277         while (true)
278         {
279             if (pl is lroot) return pr is rroot;
280             if (pr is rroot) return false;
281 
282             // !== because of NaN
283             if (!(pl.getPayload!T() == pr.getPayload!T())) return false;
284 
285             pl = pl._next;
286             pr = pr._next;
287         }
288     }
289 
290     /**
291     Defines the container's primary range, which embodies a bidirectional range.
292      */
293     struct Range
294     {
295         static assert(isBidirectionalRange!Range);
296 
297         DRange _base;
298         alias _base this;
299 
300         private this(BaseNode* first, BaseNode* last)
301         {
302             _base = DRange(first, last);
303         }
304         private this(BaseNode* n)
305         {
306             this(n, n);
307         }
308 
309         @property ref T front()
310         {
311             return _base.front.getPayload!T();
312         }
313 
314         @property ref T back()
315         {
316             return _base.back.getPayload!T();
317         }
318 
319         //Note: shadows base DRange.save.
320         //Necessary for static covariance.
321         @property Range save() { return this; }
322     }
323 
324 /**
325 Property returning `true` if and only if the container has no
326 elements.
327 
328 Complexity: $(BIGOH 1)
329      */
330     bool empty() @property const nothrow
331     {
332         return _root is null || _root is _first;
333     }
334 
335 /**
336 Removes all contents from the `DList`.
337 
338 Postcondition: `empty`
339 
340 Complexity: $(BIGOH 1)
341      */
342     void clear()
343     {
344         //remove actual elements.
345         remove(this[]);
346     }
347 
348 /**
349 Duplicates the container. The elements themselves are not transitively
350 duplicated.
351 
352 Complexity: $(BIGOH n).
353      */
354     @property DList dup()
355     {
356         return DList(this[]);
357     }
358 
359 /**
360 Returns a range that iterates over all elements of the container, in
361 forward order.
362 
363 Complexity: $(BIGOH 1)
364      */
365     Range opSlice()
366     {
367         if (empty)
368             return Range(null, null);
369         else
370             return Range(_first, _last);
371     }
372 
373 /**
374 Forward to `opSlice().front`.
375 
376 Complexity: $(BIGOH 1)
377      */
378     @property ref inout(T) front() inout
379     {
380         assert(!empty, "DList.front: List is empty");
381         return _first.getPayload!T();
382     }
383 
384 /**
385 Forward to `opSlice().back`.
386 
387 Complexity: $(BIGOH 1)
388      */
389     @property ref inout(T) back() inout
390     {
391         assert(!empty, "DList.back: List is empty");
392         return _last.getPayload!T();
393     }
394 
395 /+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
396 /+                        BEGIN CONCAT FUNCTIONS HERE                         +/
397 /+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
398 
399 /**
400 Returns a new `DList` that's the concatenation of `this` and its
401 argument `rhs`.
402      */
403     DList opBinary(string op, Stuff)(Stuff rhs)
404     if (op == "~" && is(typeof(insertBack(rhs))))
405     {
406         auto ret = this.dup;
407         ret.insertBack(rhs);
408         return ret;
409     }
410 
411 /**
412 Returns a new `DList` that's the concatenation of the argument `lhs`
413 and `this`.
414      */
415     DList opBinaryRight(string op, Stuff)(Stuff lhs)
416     if (op == "~" && is(typeof(insertFront(lhs))))
417     {
418         auto ret = this.dup;
419         ret.insertFront(lhs);
420         return ret;
421     }
422 
423 /**
424 Appends the contents of the argument `rhs` into `this`.
425      */
426     DList opOpAssign(string op, Stuff)(Stuff rhs)
427     if (op == "~" && is(typeof(insertBack(rhs))))
428     {
429         insertBack(rhs);
430         return this;
431     }
432 
433 /+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
434 /+                        BEGIN INSERT FUNCTIONS HERE                         +/
435 /+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
436 
437 /**
438 Inserts `stuff` to the front/back of the container. `stuff` can be a
439 value convertible to `T` or a range of objects convertible to $(D
440 T). The stable version behaves the same, but guarantees that ranges
441 iterating over the container are never invalidated.
442 
443 Returns: The number of elements inserted
444 
445 Complexity: $(BIGOH log(n))
446      */
447     size_t insertFront(Stuff)(Stuff stuff)
448     {
449         initialize();
450         return insertAfterNode(_root, stuff);
451     }
452 
453     /// ditto
454     size_t insertBack(Stuff)(Stuff stuff)
455     {
456         initialize();
457         return insertBeforeNode(_root, stuff);
458     }
459 
460     /// ditto
461     alias insert = insertBack;
462 
463     /// ditto
464     alias stableInsert = insert;
465 
466     /// ditto
467     alias stableInsertFront = insertFront;
468 
469     /// ditto
470     alias stableInsertBack = insertBack;
471 
472 /**
473 Inserts `stuff` after range `r`, which must be a non-empty range
474 previously extracted from this container.
475 
476 `stuff` can be a value convertible to `T` or a range of objects
477 convertible to `T`. The stable version behaves the same, but
478 guarantees that ranges iterating over the container are never
479 invalidated.
480 
481 Returns: The number of values inserted.
482 
483 Complexity: $(BIGOH k + m), where `k` is the number of elements in
484 `r` and `m` is the length of `stuff`.
485      */
486     size_t insertBefore(Stuff)(Range r, Stuff stuff)
487     {
488         if (r._first)
489             return insertBeforeNode(r._first, stuff);
490         else
491         {
492             initialize();
493             return insertAfterNode(_root, stuff);
494         }
495     }
496 
497     /// ditto
498     alias stableInsertBefore = insertBefore;
499 
500     /// ditto
501     size_t insertAfter(Stuff)(Range r, Stuff stuff)
502     {
503         if (r._last)
504             return insertAfterNode(r._last, stuff);
505         else
506         {
507             initialize();
508             return insertBeforeNode(_root, stuff);
509         }
510     }
511 
512     /// ditto
513     alias stableInsertAfter = insertAfter;
514 
515 /+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
516 /+                        BEGIN REMOVE FUNCTIONS HERE                         +/
517 /+ ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +/
518 
519 /**
520 Picks one value in an unspecified position in the container, removes
521 it from the container, and returns it. The stable version behaves the same,
522 but guarantees that ranges iterating over the container are never invalidated.
523 
524 Precondition: `!empty`
525 
526 Returns: The element removed.
527 
528 Complexity: $(BIGOH 1).
529      */
530     T removeAny()
531     {
532         import std.algorithm.mutation : move;
533 
534         assert(!empty, "DList.removeAny: List is empty");
535         auto result = move(back);
536         removeBack();
537         return result;
538     }
539     /// ditto
540     alias stableRemoveAny = removeAny;
541 
542 /**
543 Removes the value at the front/back of the container. The stable version
544 behaves the same, but guarantees that ranges iterating over the
545 container are never invalidated.
546 
547 Precondition: `!empty`
548 
549 Complexity: $(BIGOH 1).
550      */
551     void removeFront()
552     {
553         assert(!empty, "DList.removeFront: List is empty");
554         assert(_root is _first._prev, "DList: Inconsistent state");
555         BaseNode.connect(_root, _first._next);
556     }
557 
558     /// ditto
559     alias stableRemoveFront = removeFront;
560 
561     /// ditto
562     void removeBack()
563     {
564         assert(!empty, "DList.removeBack: List is empty");
565         assert(_last._next is _root, "DList: Inconsistent state");
566         BaseNode.connect(_last._prev, _root);
567     }
568 
569     /// ditto
570     alias stableRemoveBack = removeBack;
571 
572 /**
573 Removes `howMany` values at the front or back of the
574 container. Unlike the unparameterized versions above, these functions
575 do not throw if they could not remove `howMany` elements. Instead,
576 if $(D howMany > n), all elements are removed. The returned value is
577 the effective number of elements removed. The stable version behaves
578 the same, but guarantees that ranges iterating over the container are
579 never invalidated.
580 
581 Returns: The number of elements removed
582 
583 Complexity: $(BIGOH howMany).
584      */
585     size_t removeFront(size_t howMany)
586     {
587         if (!_root) return 0;
588         size_t result;
589         auto p = _first;
590         while (p !is _root && result < howMany)
591         {
592             p = p._next;
593             ++result;
594         }
595         BaseNode.connect(_root, p);
596         return result;
597     }
598 
599     /// ditto
600     alias stableRemoveFront = removeFront;
601 
602     /// ditto
603     size_t removeBack(size_t howMany)
604     {
605         if (!_root) return 0;
606         size_t result;
607         auto p = _last;
608         while (p !is _root && result < howMany)
609         {
610             p = p._prev;
611             ++result;
612         }
613         BaseNode.connect(p, _root);
614         return result;
615     }
616 
617     /// ditto
618     alias stableRemoveBack = removeBack;
619 
620 /**
621 Removes all elements belonging to `r`, which must be a range
622 obtained originally from this container.
623 
624 Returns: A range spanning the remaining elements in the container that
625 initially were right after `r`.
626 
627 Complexity: $(BIGOH 1)
628      */
629     Range remove(Range r)
630     {
631         if (r.empty)
632             return r;
633 
634         assert(_root !is null, "Cannot remove from an un-initialized List");
635         assert(r._first, "Remove: Range is empty");
636 
637         BaseNode.connect(r._first._prev, r._last._next);
638         auto after = r._last._next;
639         if (after is _root)
640             return Range(null, null);
641         else
642             return Range(after, _last);
643     }
644 
645     /// ditto
646     Range linearRemove(Range r)
647     {
648         return remove(r);
649     }
650 
651     /// ditto
652     alias stableRemove = remove;
653 
654 /**
655 Removes first element of `r`, wich must be a range obtained originally
656 from this container, from both DList instance and range `r`.
657 
658 Compexity: $(BIGOH 1)
659      */
660     void popFirstOf(ref Range r)
661     {
662         assert(_root !is null, "Cannot remove from an un-initialized List");
663         assert(r._first, "popFirstOf: Range is empty");
664         auto prev = r._first._prev;
665         auto next = r._first._next;
666         r.popFront();
667         BaseNode.connect(prev, next);
668     }
669 
670 /**
671 Removes last element of `r`, wich must be a range obtained originally
672 from this container, from both DList instance and range `r`.
673 
674 Compexity: $(BIGOH 1)
675      */
676     void popLastOf(ref Range r)
677     {
678         assert(_root !is null, "Cannot remove from an un-initialized List");
679         assert(r._first, "popLastOf: Range is empty");
680         auto prev = r._last._prev;
681         auto next = r._last._next;
682         r.popBack();
683         BaseNode.connect(prev, next);
684     }
685 
686 /**
687 `linearRemove` functions as `remove`, but also accepts ranges that are
688 result the of a `take` operation. This is a convenient way to remove a
689 fixed amount of elements from the range.
690 
691 Complexity: $(BIGOH r.walkLength)
692      */
693     Range linearRemove(Take!Range r)
694     {
695         assert(_root !is null, "Cannot remove from an un-initialized List");
696         assert(r.source._first, "Remove: Range is empty");
697 
698         BaseNode* first = r.source._first;
699         BaseNode* last = null;
700         do
701         {
702             last = r.source._first;
703             r.popFront();
704         } while ( !r.empty );
705 
706         return remove(Range(first, last));
707     }
708 
709     /// ditto
710     alias stableLinearRemove = linearRemove;
711 
712 /**
713 Removes the first occurence of an element from the list in linear time.
714 
715 Returns: True if the element existed and was successfully removed, false otherwise.
716 
717 Params:
718     value = value of the node to be removed
719 
720 Complexity: $(BIGOH n)
721      */
722     bool linearRemoveElement(T value)
723     {
724         auto n1 = findNodeByValue(_root, value);
725         if (n1)
726         {
727             auto n2 = n1._next._next;
728             BaseNode.connect(n1, n2);
729             return true;
730         }
731 
732         return false;
733     }
734 
735 
736 private:
737 
738     BaseNode* findNodeByValue(BaseNode* n, T value)
739     {
740         if (!n) return null;
741         auto ahead = n._next;
742         while (ahead && ahead.getPayload!T() != value)
743         {
744             n = ahead;
745             ahead = n._next;
746             if (ahead == _last._next) return null;
747         }
748         return n;
749     }
750 
751     // Helper: Inserts stuff before the node n.
752     size_t insertBeforeNode(Stuff)(BaseNode* n, ref Stuff stuff)
753     if (isImplicitlyConvertible!(Stuff, T))
754     {
755         auto p = createNode(stuff, n._prev, n);
756         n._prev._next = p;
757         n._prev = p;
758         return 1;
759     }
760     // ditto
761     size_t insertBeforeNode(Stuff)(BaseNode* n, ref Stuff stuff)
762     if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
763     {
764         if (stuff.empty) return 0;
765         size_t result;
766         Range r = createRange(stuff, result);
767         BaseNode.connect(n._prev, r._first);
768         BaseNode.connect(r._last, n);
769         return result;
770     }
771 
772     // Helper: Inserts stuff after the node n.
773     size_t insertAfterNode(Stuff)(BaseNode* n, ref Stuff stuff)
774     if (isImplicitlyConvertible!(Stuff, T))
775     {
776         auto p = createNode(stuff, n, n._next);
777         n._next._prev = p;
778         n._next = p;
779         return 1;
780     }
781     // ditto
782     size_t insertAfterNode(Stuff)(BaseNode* n, ref Stuff stuff)
783     if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
784     {
785         if (stuff.empty) return 0;
786         size_t result;
787         Range r = createRange(stuff, result);
788         BaseNode.connect(r._last, n._next);
789         BaseNode.connect(n, r._first);
790         return result;
791     }
792 
793     // Helper: Creates a chain of nodes from the range stuff.
794     Range createRange(Stuff)(ref Stuff stuff, ref size_t result)
795     {
796         BaseNode* first = createNode(stuff.front);
797         BaseNode* last = first;
798         ++result;
799         for ( stuff.popFront() ; !stuff.empty ; stuff.popFront() )
800         {
801             auto p = createNode(stuff.front, last);
802             last = last._next = p;
803             ++result;
804         }
805         return Range(first, last);
806     }
807 }
808 
809 @safe unittest
810 {
811     import std.algorithm.comparison : equal;
812 
813     auto e = DList!int();
814     auto b = e.linearRemoveElement(1);
815     assert(b == false);
816     assert(e.empty());
817     auto a = DList!int(-1, 1, 2, 1, 3, 4);
818     b = a.linearRemoveElement(1);
819     assert(equal(a[], [-1, 2, 1, 3, 4]));
820     assert(b == true);
821     b = a.linearRemoveElement(-1);
822     assert(b == true);
823     assert(equal(a[], [2, 1, 3, 4]));
824     b = a.linearRemoveElement(1);
825     assert(b == true);
826     assert(equal(a[], [2, 3, 4]));
827     b = a.linearRemoveElement(2);
828     assert(b == true);
829     b = a.linearRemoveElement(20);
830     assert(b == false);
831     assert(equal(a[], [3, 4]));
832     b = a.linearRemoveElement(4);
833     assert(b == true);
834     assert(equal(a[], [3]));
835     b = a.linearRemoveElement(3);
836     assert(b == true);
837     assert(a.empty());
838     a.linearRemoveElement(3);
839 }
840 
841 @safe unittest
842 {
843     import std.algorithm.comparison : equal;
844 
845     //Tests construction signatures
846     alias IntList = DList!int;
847     auto a0 = IntList();
848     auto a1 = IntList(0);
849     auto a2 = IntList(0, 1);
850     auto a3 = IntList([0]);
851     auto a4 = IntList([0, 1]);
852 
853     assert(a0[].empty);
854     assert(equal(a1[], [0]));
855     assert(equal(a2[], [0, 1]));
856     assert(equal(a3[], [0]));
857     assert(equal(a4[], [0, 1]));
858 }
859 
860 @safe unittest
861 {
862     import std.algorithm.comparison : equal;
863 
864     alias IntList = DList!int;
865     IntList list = IntList([0,1,2,3]);
866     assert(equal(list[],[0,1,2,3]));
867     list.insertBack([4,5,6,7]);
868     assert(equal(list[],[0,1,2,3,4,5,6,7]));
869 
870     list = IntList();
871     list.insertFront([0,1,2,3]);
872     assert(equal(list[],[0,1,2,3]));
873     list.insertFront([4,5,6,7]);
874     assert(equal(list[],[4,5,6,7,0,1,2,3]));
875 }
876 
877 @safe unittest
878 {
879     import std.algorithm.comparison : equal;
880     import std.range : take;
881 
882     alias IntList = DList!int;
883     IntList list = IntList([0,1,2,3]);
884     auto range = list[];
885     for ( ; !range.empty; range.popFront())
886     {
887         int item = range.front;
888         if (item == 2)
889         {
890             list.stableLinearRemove(take(range, 1));
891             break;
892         }
893     }
894     assert(equal(list[],[0,1,3]));
895 
896     list = IntList([0,1,2,3]);
897     range = list[];
898     for ( ; !range.empty; range.popFront())
899     {
900         int item = range.front;
901         if (item == 2)
902         {
903             list.stableLinearRemove(take(range,2));
904             break;
905         }
906     }
907     assert(equal(list[],[0,1]));
908 
909     list = IntList([0,1,2,3]);
910     range = list[];
911     for ( ; !range.empty; range.popFront())
912     {
913         int item = range.front;
914         if (item == 0)
915         {
916             list.stableLinearRemove(take(range,2));
917             break;
918         }
919     }
920     assert(equal(list[],[2,3]));
921 
922     list = IntList([0,1,2,3]);
923     range = list[];
924     for ( ; !range.empty; range.popFront())
925     {
926         int item = range.front;
927         if (item == 1)
928         {
929             list.stableLinearRemove(take(range,2));
930             break;
931         }
932     }
933     assert(equal(list[],[0,3]));
934 }
935 
936 @safe unittest
937 {
938     import std.algorithm.comparison : equal;
939 
940     auto dl = DList!int([1, 2, 3, 4, 5]);
941     auto r = dl[];
942     r.popFront();
943     dl.popFirstOf(r);
944     assert(equal(dl[], [1, 3, 4, 5]));
945     assert(equal(r, [3, 4, 5]));
946     r.popBack();
947     dl.popLastOf(r);
948     assert(equal(dl[], [1, 3, 5]));
949     assert(equal(r, [3]));
950     dl = DList!int([0]);
951     r = dl[];
952     dl.popFirstOf(r);
953     assert(dl.empty);
954     dl = DList!int([0]);
955     r = dl[];
956     dl.popLastOf(r);
957     assert(dl.empty);
958 }
959 
960 @safe unittest
961 {
962     import std.algorithm.comparison : equal;
963 
964     auto dl = DList!string(["a", "b", "d"]);
965     dl.insertAfter(dl[], "e"); // insert at the end
966     assert(equal(dl[], ["a", "b", "d", "e"]));
967     auto dlr = dl[];
968     dlr.popBack(); dlr.popBack();
969     dl.insertAfter(dlr, "c"); // insert after "b"
970     assert(equal(dl[], ["a", "b", "c", "d", "e"]));
971 }
972 
973 @safe unittest
974 {
975     import std.algorithm.comparison : equal;
976 
977     auto dl = DList!string(["a", "b", "d"]);
978     dl.insertBefore(dl[], "e"); // insert at the front
979     assert(equal(dl[], ["e", "a", "b", "d"]));
980     auto dlr = dl[];
981     dlr.popFront(); dlr.popFront();
982     dl.insertBefore(dlr, "c"); // insert before "b"
983     assert(equal(dl[], ["e", "a", "c", "b", "d"]));
984 }
985 
986 @safe unittest
987 {
988     auto d = DList!int([1, 2, 3]);
989     d.front = 5; //test frontAssign
990     assert(d.front == 5);
991     auto r = d[];
992     r.back = 1;
993     assert(r.back == 1);
994 }
995 
996 // https://issues.dlang.org/show_bug.cgi?id=8895
997 @safe unittest
998 {
999     auto a = make!(DList!int)(1,2,3,4);
1000     auto b = make!(DList!int)(1,2,3,4);
1001     auto c = make!(DList!int)(1,2,3,5);
1002     auto d = make!(DList!int)(1,2,3,4,5);
1003     assert(a == b); // this better terminate!
1004     assert(!(a == c));
1005     assert(!(a == d));
1006 }
1007 
1008 @safe unittest
1009 {
1010     auto d = DList!int([1, 2, 3]);
1011     d.front = 5; //test frontAssign
1012     assert(d.front == 5);
1013     auto r = d[];
1014     r.back = 1;
1015     assert(r.back == 1);
1016 }
1017 
1018 @safe unittest
1019 {
1020     auto a = DList!int();
1021     assert(a.removeFront(10) == 0);
1022     a.insert([1, 2, 3]);
1023     assert(a.removeFront(10) == 3);
1024     assert(a[].empty);
1025 }
1026 
1027 @safe unittest
1028 {
1029     import std.algorithm.comparison : equal;
1030 
1031     //Verify all flavors of ~
1032     auto a = DList!int();
1033     auto b = DList!int();
1034     auto c = DList!int([1, 2, 3]);
1035     auto d = DList!int([4, 5, 6]);
1036 
1037     assert((a ~ b[])[].empty);
1038     assert((c ~ d[])[].equal([1, 2, 3, 4, 5, 6]));
1039     assert(c[].equal([1, 2, 3]));
1040     assert(d[].equal([4, 5, 6]));
1041 
1042     assert((c[] ~ d)[].equal([1, 2, 3, 4, 5, 6]));
1043     assert(c[].equal([1, 2, 3]));
1044     assert(d[].equal([4, 5, 6]));
1045 
1046     a~=c[];
1047     assert(a[].equal([1, 2, 3]));
1048     assert(c[].equal([1, 2, 3]));
1049 
1050     a~=d[];
1051     assert(a[].equal([1, 2, 3, 4, 5, 6]));
1052     assert(d[].equal([4, 5, 6]));
1053 
1054     a~=[7, 8, 9];
1055     assert(a[].equal([1, 2, 3, 4, 5, 6, 7, 8, 9]));
1056 
1057     //trick test:
1058     auto r = c[];
1059     c.removeFront();
1060     c.removeBack();
1061 }
1062 
1063 @safe unittest
1064 {
1065     import std.algorithm.comparison : equal;
1066 
1067     // https://issues.dlang.org/show_bug.cgi?id=8905
1068     auto a = DList!int([1, 2, 3, 4]);
1069     auto r = a[];
1070     a.stableRemoveBack();
1071     a.stableInsertBack(7);
1072     assert(a[].equal([1, 2, 3, 7]));
1073 }
1074 
1075 // https://issues.dlang.org/show_bug.cgi?id=12566
1076 @safe unittest
1077 {
1078     auto dl2 = DList!int([2,7]);
1079     dl2.removeFront();
1080     assert(dl2[].walkLength == 1);
1081     dl2.removeBack();
1082     assert(dl2.empty, "not empty?!");
1083 }
1084 
1085 // https://issues.dlang.org/show_bug.cgi?id=13076
1086 @safe unittest
1087 {
1088     DList!int list;
1089     assert(list.empty);
1090     list.clear();
1091 }
1092 
1093 // https://issues.dlang.org/show_bug.cgi?id=13425
1094 @safe unittest
1095 {
1096     import std.range : drop, take;
1097     auto list = DList!int([1,2,3,4,5]);
1098     auto r = list[].drop(4); // r is a view of the last element of list
1099     assert(r.front == 5 && r.walkLength == 1);
1100     r = list.linearRemove(r.take(1));
1101     assert(r.empty); // fails
1102 }
1103 
1104 // https://issues.dlang.org/show_bug.cgi?id=14300
1105 @safe unittest
1106 {
1107     interface ITest {}
1108     static class Test : ITest {}
1109 
1110     DList!ITest().insertBack(new Test());
1111 }
1112 
1113 // https://issues.dlang.org/show_bug.cgi?id=15263
1114 @safe unittest
1115 {
1116     import std.range : iota;
1117     auto a = DList!int();
1118     a.insertFront(iota(0, 5)); // can insert range with non-ref front
1119     assert(a.front == 0 && a.back == 4);
1120 }
1121