1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 /**
6  * This module contains singly-linked ($(D_PSYMBOL SList)) and doubly-linked
7  * ($(D_PSYMBOL DList)) lists.
8  *
9  * Copyright: Eugene Wissner 2016-2021.
10  * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
11  *                  Mozilla Public License, v. 2.0).
12  * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
13  * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/container/list.d,
14  *                 tanya/container/list.d)
15  */
16 module tanya.container.list;
17 
18 import std.algorithm.comparison;
19 import std.algorithm.iteration;
20 import tanya.container.entry;
21 import tanya.memory.allocator;
22 import tanya.memory.lifetime;
23 import tanya.meta.trait;
24 import tanya.meta.transform;
25 import tanya.range.array;
26 import tanya.range.primitive;
27 
28 /**
29  * Forward range for the $(D_PSYMBOL SList).
30  *
31  * Params:
32  *  L = List type.
33  */
34 struct SRange(L)
35 {
36     private alias EntryPointer = typeof(L.head);
37     private alias E = typeof(EntryPointer.content);
38 
39     private EntryPointer* head;
40 
41     invariant
42     {
43         assert(this.head !is null);
44     }
45 
46     private this(return ref EntryPointer head) @trusted
47     {
48         this.head = &head;
49     }
50 
51     @disable this();
52 
53     @property SRange save()
54     {
55         return this;
56     }
57 
58     @property bool empty() const
59     {
60         return *this.head is null;
61     }
62 
63     @property ref inout(E) front() inout
64     in
65     {
66         assert(!empty);
67     }
68     do
69     {
70         return (*this.head).content;
71     }
72 
73     void popFront() @trusted
74     in
75     {
76         assert(!empty);
77     }
78     do
79     {
80         this.head = &(*this.head).next;
81     }
82 
83     SRange opIndex()
84     {
85         return typeof(return)(*this.head);
86     }
87 
88     L.ConstRange opIndex() const
89     {
90         return typeof(return)(*this.head);
91     }
92 }
93 
94 /**
95  * Singly-linked list.
96  *
97  * Params:
98  *  T = Content type.
99  */
100 struct SList(T)
101 {
102     /// The range types for $(D_PSYMBOL SList).
103     alias Range = SRange!SList;
104 
105     /// ditto
106     alias ConstRange = SRange!(const SList);
107 
108     private alias Entry = SEntry!T;
109 
110     // 0th element of the list.
111     private Entry* head;
112 
113     /**
114      * Creates a new $(D_PSYMBOL SList) with the elements from a static array.
115      *
116      * Params:
117      *  R         = Static array size.
118      *  init      = Values to initialize the list with.
119      *  allocator = Allocator.
120      */
121     this(size_t R)(T[R] init, shared Allocator allocator = defaultAllocator)
122     {
123         this(allocator);
124         insertFront(init[]);
125     }
126 
127     ///
128     @nogc nothrow pure @safe unittest
129     {
130         auto l = SList!int([5, 8, 15]);
131         assert(l.front == 5);
132     }
133 
134     /**
135      * Creates a new $(D_PSYMBOL SList) with the elements from an input range.
136      *
137      * Params:
138      *  R         = Type of the initial range.
139      *  init      = Values to initialize the list with.
140      *  allocator = Allocator.
141      */
142     this(R)(scope R init, shared Allocator allocator = defaultAllocator)
143     if (!isInfinite!R
144      && isInputRange!R
145      && isImplicitlyConvertible!(ElementType!R, T))
146     {
147         this(allocator);
148         insertFront(init);
149     }
150 
151     /**
152      * Creates a new $(D_PSYMBOL SList).
153      *
154      * Params:
155      *  len       = Initial length of the list.
156      *  init      = Initial value to fill the list with.
157      *  allocator = Allocator.
158      */
159     this()(size_t len,
160            auto ref T init,
161            shared Allocator allocator = defaultAllocator)
162     {
163         this(allocator);
164         if (len == 0)
165         {
166             return;
167         }
168 
169         Entry* next = this.head = allocator.make!Entry(init);
170         foreach (i; 1 .. len)
171         {
172             next.next = allocator.make!Entry(init);
173             next = next.next;
174         }
175     }
176 
177     ///
178     @nogc nothrow pure @safe unittest
179     {
180         auto l = SList!int(2, 3);
181         assert(l.front == 3);
182     }
183 
184     /// ditto
185     this(size_t len, shared Allocator allocator = defaultAllocator)
186     {
187         this(allocator);
188         if (len == 0)
189         {
190             return;
191         }
192 
193         Entry* next = this.head = allocator.make!Entry();
194         foreach (i; 1 .. len)
195         {
196             next.next = allocator.make!Entry();
197             next = next.next;
198         }
199     }
200 
201     ///
202     @nogc nothrow pure @safe unittest
203     {
204         auto l = SList!int(2);
205         assert(l.front == 0);
206     }
207 
208     /// ditto
209     this(shared Allocator allocator)
210     in
211     {
212         assert(allocator !is null);
213     }
214     do
215     {
216         this.allocator_ = allocator;
217     }
218 
219     /**
220      * Initializes this list from another one.
221      *
222      * If $(D_PARAM init) is passed by value, it won't be copied, but moved.
223      * If the allocator of ($D_PARAM init) matches $(D_PARAM allocator),
224      * $(D_KEYWORD this) will just take the ownership over $(D_PARAM init)'s
225      * storage, otherwise, the storage will be allocated with
226      * $(D_PARAM allocator) and all elements will be moved;
227      * $(D_PARAM init) will be destroyed at the end.
228      *
229      * If $(D_PARAM init) is passed by reference, it will be copied.
230      *
231      * Params:
232      *  R         = Source list type.
233      *  init      = Source list.
234      *  allocator = Allocator.
235      */
236     this(R)(ref R init, shared Allocator allocator = defaultAllocator)
237     if (is(Unqual!R == SList))
238     {
239         this(init[], allocator);
240     }
241 
242     /// ditto
243     this(R)(R init, shared Allocator allocator = defaultAllocator) @trusted
244     if (is(R == SList))
245     {
246         this(allocator);
247         if (allocator is init.allocator)
248         {
249             this.head = init.head;
250             init.head = null;
251         }
252         else
253         {
254             Entry* next;
255             for (auto current = init.head; current !is null; current = current.next)
256             {
257                 if (this.head is null)
258                 {
259                     this.head = allocator.make!Entry(move(current.content));
260                     next = this.head;
261                 }
262                 else
263                 {
264                     next.next = allocator.make!Entry(move(current.content));
265                     next = next.next;
266                 }
267             }
268         }
269     }
270 
271     ///
272     @nogc nothrow pure @safe unittest
273     {
274         auto l1 = SList!int([5, 1, 234]);
275         auto l2 = SList!int(l1);
276         assert(l1 == l2);
277     }
278 
279     /**
280      * Removes all elements from the list.
281      */
282     ~this()
283     {
284         clear();
285     }
286 
287     static if (isCopyable!T)
288     {
289         this(this)
290         {
291             auto list = typeof(this)(this[], this.allocator);
292             this.head = list.head;
293             list.head = null;
294         }
295     }
296     else
297     {
298         @disable this(this);
299     }
300 
301     ///
302     @nogc nothrow pure @safe unittest
303     {
304         auto l1 = SList!int([5, 1, 234]);
305         auto l2 = l1;
306         assert(l1 == l2);
307     }
308 
309     /**
310      * Removes all contents from the list.
311      */
312     void clear()
313     {
314         while (!empty)
315         {
316             removeFront();
317         }
318     }
319 
320     ///
321     @nogc nothrow pure @safe unittest
322     {
323         SList!int l = SList!int([8, 5]);
324 
325         assert(!l.empty);
326         l.clear();
327         assert(l.empty);
328     }
329 
330     /**
331      * Returns: First element.
332      *
333      * Precondition: $(D_INLINECODE !empty).
334      */
335     @property ref inout(T) front() inout
336     in
337     {
338         assert(!empty);
339     }
340     do
341     {
342         return this.head.content;
343     }
344 
345     private size_t moveEntry(R)(ref Entry* head, ref R el) @trusted
346     if (isImplicitlyConvertible!(R, T))
347     {
348         auto temp = cast(Entry*) allocator.allocate(Entry.sizeof);
349 
350         el.moveEmplace(temp.content);
351         temp.next = head;
352 
353         head = temp;
354         return 1;
355     }
356 
357     /**
358      * Inserts a new element at the beginning.
359      *
360      * Params:
361      *  R  = Type of the inserted value(s).
362      *  el = New element(s).
363      *
364      * Returns: The number of elements inserted.
365      */
366     size_t insertFront(R)(R el)
367     if (isImplicitlyConvertible!(R, T))
368     {
369         return moveEntry(this.head, el);
370     }
371 
372     /// ditto
373     size_t insertFront(R)(ref R el) @trusted
374     if (isImplicitlyConvertible!(R, T))
375     {
376         this.head = allocator.make!Entry(el, this.head);
377         return 1;
378     }
379 
380     ///
381     @nogc nothrow pure @safe unittest
382     {
383         SList!int l;
384         int value = 5;
385 
386         l.insertFront(value);
387         assert(l.front == value);
388 
389         value = 8;
390         l.insertFront(value);
391         assert(l.front == 8);
392     }
393 
394     /// ditto
395     size_t insertFront(R)(scope R el) @trusted
396     if (!isInfinite!R
397      && isInputRange!R
398      && isImplicitlyConvertible!(ElementType!R, T))
399     {
400         size_t retLength;
401         Entry* next, newHead;
402 
403         if (!el.empty)
404         {
405             next = allocator.make!Entry(el.front);
406             newHead = next;
407             el.popFront();
408             retLength = 1;
409         }
410         foreach (ref e; el)
411         {
412             next.next = allocator.make!Entry(e);
413             next = next.next;
414             ++retLength;
415         }
416         if (newHead !is null)
417         {
418             next.next = this.head;
419             this.head = newHead;
420         }
421         return retLength;
422     }
423 
424     /// ditto
425     size_t insertFront(size_t R)(T[R] el)
426     {
427         return insertFront!(T[])(el[]);
428     }
429 
430     /// ditto
431     alias insert = insertFront;
432 
433     ///
434     @nogc nothrow pure @safe unittest
435     {
436         SList!int l1;
437 
438         assert(l1.insertFront(8) == 1);
439         assert(l1.front == 8);
440         assert(l1.insertFront(9) == 1);
441         assert(l1.front == 9);
442 
443         SList!int l2;
444         assert(l2.insertFront([25, 30, 15]) == 3);
445         assert(l2.front == 25);
446 
447         l2.insertFront(l1[]);
448         assert(l2.front == 9);
449     }
450 
451     private bool checkRangeBelonging(ref const Range r) const
452     {
453         version (assert)
454         {
455             const(Entry)* pos = this.head;
456             for (; pos !is *r.head && pos !is null; pos = pos.next)
457             {
458             }
459             return pos is *r.head;
460         }
461         else
462         {
463             return true;
464         }
465     }
466 
467     /**
468      * Inserts new elements before $(D_PARAM r).
469      *
470      * Params:
471      *  R  = Type of the inserted value(s).
472      *  r  = Range extracted from this list.
473      *  el = New element(s).
474      *
475      * Returns: The number of elements inserted.
476      *
477      * Precondition: $(D_PARAM r) is extracted from this list.
478      */
479     size_t insertBefore(R)(Range r, R el)
480     if (isImplicitlyConvertible!(R, T))
481     in
482     {
483         assert(checkRangeBelonging(r));
484     }
485     do
486     {
487         return moveEntry(*r.head, el);
488     }
489 
490     ///
491     @nogc nothrow pure @safe unittest
492     {
493         auto l1 = SList!int([234, 5, 1]);
494         auto l2 = SList!int([5, 1]);
495         l2.insertBefore(l2[], 234);
496         assert(l1 == l2);
497     }
498 
499     /// ditto
500     size_t insertBefore(R)(Range r, scope R el)
501     if (!isInfinite!R
502         && isInputRange!R
503         && isImplicitlyConvertible!(ElementType!R, T))
504     in
505     {
506         assert(checkRangeBelonging(r));
507     }
508     do
509     {
510         size_t inserted;
511         foreach (e; el)
512         {
513             inserted += insertBefore(r, e);
514             r.popFront();
515         }
516         return inserted;
517     }
518 
519     ///
520     @nogc nothrow pure @safe unittest
521     {
522         auto l1 = SList!int([5, 234, 30, 1]);
523         auto l2 = SList!int([5, 1]);
524         auto l3 = SList!int([234, 30]);
525         auto r = l2[];
526         r.popFront();
527         l2.insertBefore(r, l3[]);
528         assert(l1 == l2);
529     }
530 
531     /// ditto
532     size_t insertBefore()(Range r, ref T el) @trusted
533     in
534     {
535         assert(checkRangeBelonging(r));
536     }
537     do
538     {
539         *r.head = allocator.make!Entry(el, *r.head);
540         return 1;
541     }
542 
543     ///
544     @nogc nothrow pure @safe unittest
545     {
546         auto l1 = SList!int([234, 5, 1]);
547         auto l2 = SList!int([5, 1]);
548         int var = 234;
549         l2.insertBefore(l2[], var);
550         assert(l1 == l2);
551     }
552 
553     /**
554      * Inserts elements from a static array before $(D_PARAM r).
555      *
556      * Params:
557      *  R  = Static array size.
558      *  r  = Range extracted from this list.
559      *  el = New elements.
560      *
561      * Returns: The number of elements inserted.
562      *
563      * Precondition: $(D_PARAM r) is extracted from this list.
564      */
565     size_t insertBefore(size_t R)(Range r, T[R] el)
566     {
567         return insertBefore!(T[])(r, el[]);
568     }
569 
570     ///
571     @nogc nothrow pure @safe unittest
572     {
573         auto l1 = SList!int([5, 234, 30, 1]);
574         auto l2 = SList!int([5, 1]);
575         auto r = l2[];
576         r.popFront();
577         l2.insertBefore(r, [234, 30]);
578         assert(l1 == l2);
579     }
580 
581     /**
582      * Comparison for equality.
583      *
584      * Params:
585      *  that = The list to compare with.
586      *
587      * Returns: $(D_KEYWORD true) if the lists are equal, $(D_KEYWORD false)
588      *          otherwise.
589      */
590     bool opEquals()(auto ref typeof(this) that) inout
591     {
592         return equal(opIndex(), that[]);
593     }
594 
595     ///
596     @nogc nothrow pure @safe unittest
597     {
598         SList!int l1, l2;
599 
600         l1.insertFront(8);
601         l1.insertFront(9);
602         l2.insertFront(8);
603         l2.insertFront(10);
604         assert(l1 != l2);
605 
606         l1.removeFront();
607         assert(l1 != l2);
608 
609         l2.removeFront();
610         assert(l1 == l2);
611 
612         l1.removeFront();
613         assert(l1 != l2);
614 
615         l2.removeFront();
616         assert(l1 == l2);
617     }
618 
619     /**
620      * Returns: $(D_KEYWORD true) if the list is empty.
621      */
622     @property bool empty() const
623     {
624         return this.head is null;
625     }
626 
627     /**
628      * Removes the front element.
629      *
630      * Precondition: $(D_INLINECODE !empty)
631      */
632     void removeFront()
633     in
634     {
635         assert(!empty);
636     }
637     do
638     {
639         auto n = this.head.next;
640 
641         allocator.dispose(this.head);
642         this.head = n;
643     }
644 
645     ///
646     @nogc nothrow pure @safe unittest
647     {
648         SList!int l;
649 
650         l.insertFront(8);
651         l.insertFront(9);
652         assert(l.front == 9);
653         l.removeFront();
654         assert(l.front == 8);
655         l.removeFront();
656         assert(l.empty);
657     }
658 
659     /**
660      * Removes $(D_PARAM howMany) elements from the list.
661      *
662      * Unlike $(D_PSYMBOL removeFront()), this method doesn't fail, if it could not
663      * remove $(D_PARAM howMany) elements. Instead, if $(D_PARAM howMany) is
664      * greater than the list length, all elements are removed.
665      *
666      * Params:
667      *  howMany = How many elements should be removed.
668      *
669      * Returns: The number of elements removed.
670      */
671     size_t removeFront(size_t howMany)
672     out (removed)
673     {
674         assert(removed <= howMany);
675     }
676     do
677     {
678         size_t i;
679         for (; i < howMany && !empty; ++i)
680         {
681             removeFront();
682         }
683         return i;
684     }
685 
686     ///
687     @nogc nothrow pure @safe unittest
688     {
689         SList!int l = SList!int([8, 5, 4]);
690 
691         assert(l.removeFront(0) == 0);
692         assert(l.removeFront(2) == 2);
693         assert(l.removeFront(3) == 1);
694         assert(l.removeFront(3) == 0);
695     }
696 
697     /**
698      * Removes $(D_PARAM r) from the list.
699      *
700      * Params:
701      *  r = The range to remove.
702      *
703      * Returns: An empty range.
704      *
705      * Precondition: $(D_PARAM r) is extracted from this list.
706      */
707     Range remove(scope Range r)
708     in
709     {
710         assert(checkRangeBelonging(r));
711     }
712     do
713     {
714         auto outOfScopeList = typeof(this)(allocator);
715         outOfScopeList.head = *r.head;
716         *r.head = null;
717 
718         return r;
719     }
720 
721     ///
722     @nogc nothrow pure @safe unittest
723     {
724         auto l1 = SList!int([5, 234, 30, 1]);
725         auto l2 = SList!int([5]);
726         auto r = l1[];
727 
728         r.popFront();
729 
730         assert(l1.remove(r).empty);
731         assert(l1 == l2);
732     }
733 
734     /**
735      * Removes the front element of the $(D_PARAM range) from the list.
736      *
737      * Params:
738      *  range = Range whose front element should be removed.
739      *
740      * Returns: $(D_PSYMBOL range) with its front element removed.
741      *
742      * Precondition: $(D_INLINECODE !range.empty).
743      *               $(D_PARAM range) is extracted from this list.
744      */
745     Range popFirstOf(Range range)
746     in
747     {
748         assert(!range.empty);
749         assert(checkRangeBelonging(range));
750     }
751     do
752     {
753         auto next = (*range.head).next;
754 
755         allocator.dispose(*range.head);
756         *range.head = next;
757 
758         return range;
759     }
760 
761     ///
762     @nogc nothrow pure @safe unittest
763     {
764         auto list = SList!int([5, 234, 30]);
765         auto range = list[];
766 
767         range.popFront();
768         assert(list.popFirstOf(range).front == 30);
769 
770         range = list[];
771         assert(range.front == 5);
772         range.popFront;
773         assert(range.front == 30);
774         range.popFront;
775         assert(range.empty);
776     }
777 
778     /**
779      * Returns: Range that iterates over all elements of the container, in
780      *          forward order.
781      */
782     Range opIndex()
783     {
784         return typeof(return)(this.head);
785     }
786 
787     /// ditto
788     ConstRange opIndex() const
789     {
790         return typeof(return)(this.head);
791     }
792 
793     /**
794      * Assigns another list.
795      *
796      * If $(D_PARAM that) is passed by value, it won't be copied, but moved.
797      * This list will take the ownership over $(D_PARAM that)'s storage and
798      * the allocator.
799      *
800      * If $(D_PARAM that) is passed by reference, it will be copied.
801      *
802      * Params:
803      *  R    = Content type.
804      *  that = The value should be assigned.
805      *
806      * Returns: $(D_KEYWORD this).
807      */
808     ref typeof(this) opAssign(R)(ref R that)
809     if (is(Unqual!R == SList))
810     {
811         return this = that[];
812     }
813 
814     /// ditto
815     ref typeof(this) opAssign(R)(R that)
816     if (is(R == SList))
817     {
818         swap(this.head, that.head);
819         swap(this.allocator_, that.allocator_);
820         return this;
821     }
822 
823     ///
824     @nogc nothrow pure @safe unittest
825     {
826         {
827             auto l1 = SList!int([5, 4, 9]);
828             auto l2 = SList!int([9, 4]);
829             l1 = l2;
830             assert(l1 == l2);
831         }
832         {
833             auto l1 = SList!int([5, 4, 9]);
834             auto l2 = SList!int([9, 4]);
835             l1 = SList!int([9, 4]);
836             assert(l1 == l2);
837         }
838     }
839 
840     /**
841      * Assigns an input range.
842      *
843      * Params:
844      *  R    = Type of the initial range.
845      *  that = Values to initialize the list with.
846      *
847      * Returns: $(D_KEYWORD this).
848      */
849     ref typeof(this) opAssign(R)(scope R that) @trusted
850     if (!isInfinite!R
851      && isInputRange!R
852      && isImplicitlyConvertible!(ElementType!R, T))
853     {
854         Entry** next = &this.head;
855 
856         foreach (ref e; that)
857         {
858             if (*next is null)
859             {
860                 *next = allocator.make!Entry(e);
861             }
862             else
863             {
864                 (*next).content = e;
865             }
866             next = &(*next).next;
867         }
868         remove(Range(*next));
869 
870         return this;
871     }
872 
873     ///
874     @nogc nothrow pure @safe unittest
875     {
876         auto l1 = SList!int([5, 4, 9]);
877         auto l2 = SList!int([9, 4]);
878         l1 = l2[];
879         assert(l1 == l2);
880     }
881 
882     /**
883      * Assigns a static array.
884      *
885      * Params:
886      *  R    = Static array size.
887      *  that = Values to initialize the list with.
888      *
889      * Returns: $(D_KEYWORD this).
890      */
891     ref typeof(this) opAssign(size_t R)(T[R] that)
892     {
893         return opAssign!(T[])(that[]);
894     }
895 
896     ///
897     @nogc nothrow pure @safe unittest
898     {
899         auto l1 = SList!int([5, 4, 9]);
900         auto l2 = SList!int([9, 4]);
901         l1 = [9, 4];
902         assert(l1 == l2);
903     }
904 
905     mixin DefaultAllocator;
906 }
907 
908 ///
909 @nogc nothrow pure @safe unittest
910 {
911     SList!int l;
912     size_t i;
913 
914     l.insertFront(5);
915     l.insertFront(4);
916     l.insertFront(9);
917     foreach (e; l)
918     {
919         assert(i != 0 || e == 9);
920         assert(i != 1 || e == 4);
921         assert(i != 2 || e == 5);
922         ++i;
923     }
924     assert(i == 3);
925 }
926 
927 /**
928  * Bidirectional range for the $(D_PSYMBOL DList).
929  *
930  * Params:
931  *  L = List type.
932  */
933 struct DRange(L)
934 {
935     private alias E = typeof(L.head.content);
936     private alias EntryPointer = typeof(L.head);
937 
938     private EntryPointer* head;
939     private EntryPointer* tail;
940 
941     invariant
942     {
943         assert(this.head !is null);
944         assert(this.tail !is null);
945     }
946 
947     private this(return ref EntryPointer head, return ref EntryPointer tail)
948     @trusted
949     {
950         this.head = &head;
951         this.tail = &tail;
952     }
953 
954     @disable this();
955 
956     @property DRange save()
957     {
958         return this;
959     }
960 
961     @property bool empty() const
962     {
963         return *this.head is null || *this.head is (*this.tail).next;
964     }
965 
966     @property ref inout(E) front() inout
967     in
968     {
969         assert(!empty);
970     }
971     do
972     {
973         return (*this.head).content;
974     }
975 
976     @property ref inout(E) back() inout
977     in
978     {
979         assert(!empty);
980     }
981     do
982     {
983         return (*this.tail).content;
984     }
985 
986     void popFront() @trusted
987     in
988     {
989         assert(!empty);
990     }
991     do
992     {
993         this.head = &(*this.head).next;
994     }
995 
996     void popBack() @trusted
997     in
998     {
999         assert(!empty);
1000     }
1001     do
1002     {
1003         this.tail = &(*this.tail).prev;
1004     }
1005 
1006     DRange opIndex()
1007     {
1008         return typeof(return)(*this.head, *this.tail);
1009     }
1010 
1011     L.ConstRange opIndex() const
1012     {
1013         return typeof(return)(*this.head, *this.tail);
1014     }
1015 }
1016 
1017 /**
1018  * Doubly-linked list.
1019  *
1020  * $(D_PSYMBOL DList) can be also used as a queue. Elements can be enqueued
1021  * with $(D_PSYMBOL DList.insertBack). To process the queue a `for`-loop comes
1022  * in handy:
1023  *
1024  * ---
1025  * for (; !dlist.empty; dlist.removeFront())
1026  * {
1027  *  do_something_with(dlist.front);
1028  * }
1029  * ---
1030  *
1031  * Params:
1032  *  T = Content type.
1033  */
1034 struct DList(T)
1035 {
1036     /// The range types for $(D_PSYMBOL DList).
1037     alias Range = DRange!DList;
1038 
1039     /// ditto
1040     alias ConstRange = DRange!(const DList);
1041 
1042     private alias Entry = DEntry!T;
1043 
1044     // 0th and the last elements of the list.
1045     private Entry* head, tail;
1046 
1047     static if (__VERSION__ < 2086) // Bug #20171.
1048     {
1049         invariant
1050         {
1051             assert((this.tail is null) == (this.head is null));
1052             assert(this.tail is null || this.tail.next is null);
1053             assert(this.head is null || this.head.prev is null);
1054         }
1055     }
1056 
1057     /**
1058      * Creates a new $(D_PSYMBOL DList) with the elements from a static array.
1059      *
1060      * Params:
1061      *  R         = Static array size.
1062      *  init      = Values to initialize the list with.
1063      *  allocator = Allocator.
1064      */
1065     this(size_t R)(T[R] init, shared Allocator allocator = defaultAllocator)
1066     {
1067         this(allocator);
1068         insertFront(init[]);
1069     }
1070 
1071     ///
1072     @nogc nothrow pure @safe unittest
1073     {
1074         auto l = DList!int([5, 8, 15]);
1075         assert(l.front == 5);
1076     }
1077 
1078     /**
1079      * Creates a new $(D_PSYMBOL DList) with the elements from an input range.
1080      *
1081      * Params:
1082      *  R         = Type of the initial range.
1083      *  init      = Values to initialize the list with.
1084      *  allocator = Allocator.
1085      */
1086     this(R)(scope R init, shared Allocator allocator = defaultAllocator)
1087     if (!isInfinite!R
1088      && isInputRange!R
1089      && isImplicitlyConvertible!(ElementType!R, T))
1090     {
1091         this(allocator);
1092         insertFront(init);
1093     }
1094 
1095     /**
1096      * Creates a new $(D_PSYMBOL DList).
1097      *
1098      * Params:
1099      *  len       = Initial length of the list.
1100      *  init      = Initial value to fill the list with.
1101      *  allocator = Allocator.
1102      */
1103     this()(size_t len,
1104            auto ref T init,
1105            shared Allocator allocator = defaultAllocator)
1106     {
1107         this(allocator);
1108         if (len == 0)
1109         {
1110             return;
1111         }
1112 
1113         Entry* next = this.head = allocator.make!Entry(init);
1114         foreach (i; 1 .. len)
1115         {
1116             next.next = allocator.make!Entry(init);
1117             next.next.prev = next;
1118             next = next.next;
1119         }
1120         this.tail = next;
1121     }
1122 
1123     ///
1124     @nogc nothrow pure @safe unittest
1125     {
1126         auto l = DList!int(2, 3);
1127         assert(l.front == 3);
1128         assert(l.back == 3);
1129     }
1130 
1131     /// ditto
1132     this(size_t len, shared Allocator allocator = defaultAllocator)
1133     {
1134         this(allocator);
1135         if (len == 0)
1136         {
1137             return;
1138         }
1139 
1140         Entry* next = this.head = allocator.make!Entry();
1141         foreach (i; 1 .. len)
1142         {
1143             next.next = allocator.make!Entry();
1144             next.next.prev = next;
1145             next = next.next;
1146         }
1147         this.tail = next;
1148     }
1149 
1150     ///
1151     @nogc nothrow pure @safe unittest
1152     {
1153         auto l = DList!int(2);
1154         assert(l.front == 0);
1155     }
1156 
1157     /// ditto
1158     this(shared Allocator allocator)
1159     in
1160     {
1161         assert(allocator !is null);
1162     }
1163     do
1164     {
1165         this.allocator_ = allocator;
1166     }
1167 
1168     /**
1169      * Initializes this list from another one.
1170      *
1171      * If $(D_PARAM init) is passed by value, it won't be copied, but moved.
1172      * If the allocator of ($D_PARAM init) matches $(D_PARAM allocator),
1173      * $(D_KEYWORD this) will just take the ownership over $(D_PARAM init)'s
1174      * storage, otherwise, the storage will be allocated with
1175      * $(D_PARAM allocator) and all elements will be moved;
1176      * $(D_PARAM init) will be destroyed at the end.
1177      *
1178      * If $(D_PARAM init) is passed by reference, it will be copied.
1179      *
1180      * Params:
1181      *  R         = Source list type.
1182      *  init      = Source list.
1183      *  allocator = Allocator.
1184      */
1185     this(R)(ref R init, shared Allocator allocator = defaultAllocator)
1186     if (is(Unqual!R == DList))
1187     {
1188         this(init[], allocator);
1189     }
1190 
1191     /// ditto
1192     this(R)(R init, shared Allocator allocator = defaultAllocator) @trusted
1193     if (is(R == DList))
1194     {
1195         this(allocator);
1196         if (allocator is init.allocator)
1197         {
1198             this.head = init.head;
1199             this.tail = init.tail;
1200             init.head = this.tail = null;
1201         }
1202         else
1203         {
1204             Entry* next;
1205             for (auto current = init.head; current !is null; current = current.next)
1206             {
1207                 if (this.head is null)
1208                 {
1209                     this.head = allocator.make!Entry(move(current.content));
1210                     next = this.head;
1211                 }
1212                 else
1213                 {
1214                     next.next = allocator.make!Entry(move(current.content));
1215                     next.next.prev = next;
1216                     next = next.next;
1217                 }
1218             }
1219             this.tail = next;
1220         }
1221     }
1222 
1223     ///
1224     @nogc nothrow pure @safe unittest
1225     {
1226         auto l1 = DList!int([5, 1, 234]);
1227         auto l2 = DList!int(l1);
1228         assert(l1 == l2);
1229     }
1230 
1231     /**
1232      * Removes all elements from the list.
1233      */
1234     ~this()
1235     {
1236         clear();
1237     }
1238 
1239     static if (isCopyable!T)
1240     {
1241         this(this)
1242         {
1243             auto list = typeof(this)(this[], this.allocator);
1244             this.head = list.head;
1245             this.tail = list.tail;
1246             list.head = list .tail = null;
1247         }
1248     }
1249     else
1250     {
1251         @disable this(this);
1252     }
1253 
1254     ///
1255     @nogc nothrow pure @safe unittest
1256     {
1257         auto l1 = DList!int([5, 1, 234]);
1258         auto l2 = l1;
1259         assert(l1 == l2);
1260     }
1261 
1262     /**
1263      * Removes all contents from the list.
1264      */
1265     void clear()
1266     {
1267         while (!empty)
1268         {
1269             removeFront();
1270         }
1271     }
1272 
1273     ///
1274     @nogc nothrow pure @safe unittest
1275     {
1276         DList!int l = DList!int([8, 5]);
1277 
1278         assert(!l.empty);
1279         l.clear();
1280         assert(l.empty);
1281     }
1282 
1283     /**
1284      * Returns: First element.
1285      *
1286      * Precondition: $(D_INLINECODE !empty).
1287      */
1288     @property ref inout(T) front() inout
1289     in
1290     {
1291         assert(!empty);
1292     }
1293     do
1294     {
1295         return this.head.content;
1296     }
1297 
1298     /**
1299      * Returns: Last element.
1300      *
1301      * Precondition: $(D_INLINECODE !empty).
1302      */
1303     @property ref inout(T) back() inout
1304     in
1305     {
1306         assert(!empty);
1307     }
1308     do
1309     {
1310         return this.tail.content;
1311     }
1312 
1313     ///
1314     @nogc nothrow pure @safe unittest
1315     {
1316         auto l = DList!int([25]);
1317         assert(l.front == 25);
1318         assert(l.back == 25);
1319 
1320         l.insertFront(30);
1321         assert(l.front == 30);
1322         assert(l.back == 25);
1323     }
1324 
1325     private size_t moveFront(R)(ref Entry* head, ref R el) @trusted
1326     if (isImplicitlyConvertible!(R, T))
1327     {
1328         auto temp = cast(Entry*) allocator.allocate(Entry.sizeof);
1329 
1330         el.moveEmplace(temp.content);
1331         temp.next = head;
1332         if (this.tail is null)
1333         {
1334             temp.prev = null;
1335             this.tail = temp;
1336         }
1337         else
1338         {
1339             temp.prev = head.prev;
1340             head.prev = temp;
1341         }
1342 
1343         head = temp;
1344         return 1;
1345     }
1346 
1347     // Creates a lsit of linked entries from a range.
1348     // Returns count of the elements in the list.
1349     private size_t makeList(R)(scope ref R el, out Entry* head, out Entry* tail)
1350     @trusted
1351     out (retLength)
1352     {
1353         assert((retLength == 0 && head is null && tail is null)
1354                  || (retLength > 0 && head !is null && tail !is null));
1355     }
1356     do
1357     {
1358         size_t retLength;
1359 
1360         if (!el.empty)
1361         {
1362             head = tail = allocator.make!Entry(el.front);
1363             el.popFront();
1364             retLength = 1;
1365         }
1366         foreach (ref e; el)
1367         {
1368             tail.next = allocator.make!Entry(e);
1369             tail.next.prev = tail;
1370             tail = tail.next;
1371             ++retLength;
1372         }
1373         return retLength;
1374     }
1375 
1376     /**
1377      * Inserts a new element at the beginning.
1378      *
1379      * Params:
1380      *  R  = Type of the inserted value(s).
1381      *  el = New element(s).
1382      *
1383      * Returns: The number of elements inserted.
1384      */
1385     size_t insertFront(R)(R el)
1386     if (isImplicitlyConvertible!(R, T))
1387     {
1388         return moveFront(this.head, el);
1389     }
1390 
1391     /// ditto
1392     size_t insertFront(R)(ref R el) @trusted
1393     if (isImplicitlyConvertible!(R, T))
1394     {
1395         if (this.tail is null)
1396         {
1397             this.head = this.tail = allocator.make!Entry(el);
1398         }
1399         else
1400         {
1401             this.head.prev = allocator.make!Entry(el, this.head);
1402             this.head = this.head.prev;
1403         }
1404         return 1;
1405     }
1406 
1407     ///
1408     @nogc nothrow pure @safe unittest
1409     {
1410         DList!int l;
1411         int value = 5;
1412 
1413         l.insertFront(value);
1414         assert(l.front == value);
1415         assert(l.back == value);
1416 
1417         value = 8;
1418         l.insertFront(value);
1419         assert(l.front == 8);
1420         assert(l.back == 5);
1421     }
1422 
1423     /// ditto
1424     size_t insertFront(R)(scope R el)
1425     if (!isInfinite!R
1426      && isInputRange!R
1427      && isImplicitlyConvertible!(ElementType!R, T))
1428     {
1429         Entry* begin, end;
1430         const inserted = makeList(el, begin, end);
1431 
1432         if (this.head is null)
1433         {
1434             this.tail = end;
1435         }
1436         if (begin !is null)
1437         {
1438             end.next = this.head;
1439             this.head = begin;
1440         }
1441 
1442         return inserted;
1443     }
1444 
1445     /// ditto
1446     size_t insertFront(size_t R)(T[R] el)
1447     {
1448         return insertFront!(T[])(el[]);
1449     }
1450 
1451     ///
1452     @nogc nothrow pure @safe unittest
1453     {
1454         DList!int l1;
1455 
1456         assert(l1.insertFront(8) == 1);
1457         assert(l1.front == 8);
1458         assert(l1.back == 8);
1459         assert(l1.insertFront(9) == 1);
1460         assert(l1.front == 9);
1461         assert(l1.back == 8);
1462 
1463         DList!int l2;
1464         assert(l2.insertFront([25, 30, 15]) == 3);
1465         assert(l2.front == 25);
1466         assert(l2.back == 15);
1467 
1468         l2.insertFront(l1[]);
1469         assert(l2.front == 9);
1470         assert(l2.back == 15);
1471     }
1472 
1473     private size_t moveBack(R)(ref Entry* tail, ref R el) @trusted
1474     if (isImplicitlyConvertible!(R, T))
1475     {
1476         auto temp = cast(Entry*) allocator.allocate(Entry.sizeof);
1477 
1478         el.moveEmplace(temp.content);
1479         temp.prev = tail;
1480         if (this.head is null)
1481         {
1482             temp.next = null;
1483             this.head = this.tail = temp;
1484         }
1485         else
1486         {
1487             temp.next = tail.next;
1488             tail.next = temp;
1489         }
1490 
1491         tail = temp;
1492         return 1;
1493     }
1494 
1495     /**
1496      * Inserts a new element at the end.
1497      *
1498      * Params:
1499      *  R  = Type of the inserted value(s).
1500      *  el = New element(s).
1501      *
1502      * Returns: The number of elements inserted.
1503      */
1504     size_t insertBack(R)(R el) @trusted
1505     if (isImplicitlyConvertible!(R, T))
1506     {
1507         return moveBack(this.tail, el);
1508     }
1509 
1510     /// ditto
1511     size_t insertBack(R)(ref R el) @trusted
1512     if (isImplicitlyConvertible!(R, T))
1513     {
1514         if (this.tail is null)
1515         {
1516             this.head = this.tail = allocator.make!Entry(el);
1517         }
1518         else
1519         {
1520             this.tail.next = allocator.make!Entry(el, null, this.tail);
1521             this.tail = this.tail.next;
1522         }
1523         return 1;
1524     }
1525 
1526     ///
1527     @nogc nothrow pure @safe unittest
1528     {
1529         DList!int l;
1530         int value = 5;
1531 
1532         l.insertBack(value);
1533         assert(l.front == value);
1534         assert(l.back == value);
1535 
1536         value = 8;
1537         l.insertBack(value);
1538         assert(l.front == 5);
1539         assert(l.back == value);
1540     }
1541 
1542     /// ditto
1543     size_t insertBack(R)(scope R el) @trusted
1544     if (!isInfinite!R
1545      && isInputRange!R
1546      && isImplicitlyConvertible!(ElementType!R, T))
1547     {
1548         Entry* begin, end;
1549         const inserted = makeList(el, begin, end);
1550 
1551         if (this.tail is null)
1552         {
1553             this.head = begin;
1554         }
1555         else
1556         {
1557             this.tail.next = begin;
1558         }
1559         if (begin !is null)
1560         {
1561             this.tail = end;
1562         }
1563 
1564         return inserted;
1565     }
1566 
1567     /// ditto
1568     size_t insertBack(size_t R)(T[R] el)
1569     {
1570         return insertBack!(T[])(el[]);
1571     }
1572 
1573     ///
1574     @nogc nothrow pure @safe unittest
1575     {
1576         DList!int l1;
1577 
1578         assert(l1.insertBack(8) == 1);
1579         assert(l1.back == 8);
1580         assert(l1.insertBack(9) == 1);
1581         assert(l1.back == 9);
1582 
1583         DList!int l2;
1584         assert(l2.insertBack([25, 30, 15]) == 3);
1585         assert(l2.back == 15);
1586 
1587         l2.insertBack(l1[]);
1588         assert(l2.back == 9);
1589     }
1590 
1591     /// ditto
1592     alias insert = insertBack;
1593 
1594     private bool checkRangeBelonging(ref const Range r) const
1595     {
1596         version (assert)
1597         {
1598             const(Entry)* pos = this.head;
1599             for (; pos !is *r.head && pos !is null; pos = pos.next)
1600             {
1601             }
1602             return pos is *r.head;
1603         }
1604         else
1605         {
1606             return true;
1607         }
1608     }
1609 
1610     /**
1611      * Inserts new elements before $(D_PARAM r).
1612      *
1613      * Params:
1614      *  R  = Type of the inserted value(s).
1615      *  r  = Range extracted from this list.
1616      *  el = New element(s).
1617      *
1618      * Returns: The number of elements inserted.
1619      *
1620      * Precondition: $(D_PARAM r) is extracted from this list.
1621      */
1622     size_t insertBefore(R)(Range r, R el)
1623     if (isImplicitlyConvertible!(R, T))
1624     in
1625     {
1626         assert(checkRangeBelonging(r));
1627     }
1628     do
1629     {
1630         return moveFront(*r.head, el);
1631     }
1632 
1633     ///
1634     @nogc nothrow pure @safe unittest
1635     {
1636         auto l1 = DList!int([234, 5, 1]);
1637         auto l2 = DList!int([5, 1]);
1638         l2.insertBefore(l2[], 234);
1639         assert(l1 == l2);
1640     }
1641 
1642     /// ditto
1643     size_t insertBefore()(Range r, ref T el) @trusted
1644     in
1645     {
1646         assert(checkRangeBelonging(r));
1647     }
1648     do
1649     {
1650         auto temp = allocator.make!Entry(el, *r.head);
1651 
1652         if (this.tail is null)
1653         {
1654             this.tail = temp;
1655         }
1656         else
1657         {
1658             temp.prev = (*r.head).prev;
1659             (*r.head).prev = temp;
1660         }
1661 
1662         *r.head = temp;
1663         return 1;
1664     }
1665 
1666     ///
1667     @nogc nothrow pure @safe unittest
1668     {
1669         auto l1 = DList!int([234, 5, 1]);
1670         auto l2 = DList!int([5, 1]);
1671         int var = 234;
1672 
1673         l2.insertBefore(l2[], var);
1674         assert(l1 == l2);
1675     }
1676 
1677     /// ditto
1678     size_t insertBefore(R)(Range r, scope R el)
1679     if (!isInfinite!R
1680         && isInputRange!R
1681         && isImplicitlyConvertible!(ElementType!R, T))
1682     in
1683     {
1684         assert(checkRangeBelonging(r));
1685     }
1686     do
1687     {
1688         size_t inserted;
1689         foreach (e; el)
1690         {
1691             inserted += insertBefore(r, e);
1692             r.popFront();
1693         }
1694         return inserted;
1695     }
1696 
1697     ///
1698     @nogc nothrow pure @safe unittest
1699     {
1700         auto l1 = DList!int([5, 234, 30, 1]);
1701         auto l2 = DList!int([5, 1]);
1702         auto r = l2[];
1703         r.popFront();
1704         l2.insertBefore(r, [234, 30]);
1705         assert(l1 == l2);
1706     }
1707 
1708     /**
1709      * Inserts elements from a static array before $(D_PARAM r).
1710      *
1711      * Params:
1712      *  R  = Static array size.
1713      *  r  = Range extracted from this list.
1714      *  el = New elements.
1715      *
1716      * Returns: The number of elements inserted.
1717      *
1718      * Precondition: $(D_PARAM r) is extracted from this list.
1719      */
1720     size_t insertBefore(size_t R)(Range r, T[R] el)
1721     {
1722         return insertBefore!(T[])(r, el[]);
1723     }
1724 
1725     /**
1726      * Inserts new elements after $(D_PARAM r).
1727      *
1728      * Params:
1729      *  R  = Type of the inserted value(s).
1730      *  r  = Range extracted from this list.
1731      *  el = New element(s).
1732      *
1733      * Returns: The number of elements inserted.
1734      *
1735      * Precondition: $(D_PARAM r) is extracted from this list.
1736      */
1737     size_t insertAfter(R)(Range r, R el) @trusted
1738     if (isImplicitlyConvertible!(R, T))
1739     in
1740     {
1741         assert(checkRangeBelonging(r));
1742     }
1743     do
1744     {
1745         return moveBack(*r.tail, el);
1746     }
1747 
1748     ///
1749     @nogc nothrow pure @safe unittest
1750     {
1751         auto l1 = DList!int([5, 234, 1]);
1752         auto l2 = DList!int([5, 1]);
1753         auto r = l2[];
1754         r.popBack();
1755         l2.insertAfter(r, 234);
1756         assert(l1 == l2);
1757     }
1758 
1759     /// ditto
1760     size_t insertAfter()(Range r, ref T el) @trusted
1761     in
1762     {
1763         assert(checkRangeBelonging(r));
1764     }
1765     do
1766     {
1767         auto temp = allocator.make!Entry(el, null, *r.tail);
1768 
1769         if (this.head is null)
1770         {
1771             this.head = temp;
1772         }
1773         else
1774         {
1775             temp.next = (*r.tail).next;
1776             (*r.tail).next = temp;
1777         }
1778 
1779         *r.tail = temp;
1780         return 1;
1781     }
1782 
1783     ///
1784     @nogc nothrow pure @safe unittest
1785     {
1786         auto l1 = DList!int([5, 1, 234]);
1787         auto l2 = DList!int([5, 1]);
1788         int var = 234;
1789 
1790         l2.insertAfter(l2[], var);
1791         assert(l1 == l2);
1792     }
1793 
1794     /// ditto
1795     size_t insertAfter(R)(Range r, scope R el)
1796     if (!isInfinite!R
1797         && isInputRange!R
1798         && isImplicitlyConvertible!(ElementType!R, T))
1799     in
1800     {
1801         assert(checkRangeBelonging(r));
1802     }
1803     do
1804     {
1805         return fold!((acc, x) => acc + insertAfter(r, x))(el, size_t.init);
1806     }
1807 
1808     ///
1809     @nogc nothrow pure @safe unittest
1810     {
1811         auto l1 = DList!int([5, 234, 30, 1]);
1812         auto l2 = DList!int([5, 1]);
1813         auto r = l2[];
1814 
1815         r.popBack();
1816         l2.insertAfter(r, [234, 30]);
1817         assert(l1 == l2);
1818     }
1819 
1820     /**
1821      * Inserts elements from a static array after $(D_PARAM r).
1822      *
1823      * Params:
1824      *  R  = Static array size.
1825      *  r  = Range extracted from this list.
1826      *  el = New elements.
1827      *
1828      * Returns: The number of elements inserted.
1829      *
1830      * Precondition: $(D_PARAM r) is extracted from this list.
1831      */
1832     size_t insertAfter(size_t R)(Range r, T[R] el)
1833     {
1834         return insertAfter!(T[])(r, el[]);
1835     }
1836 
1837     /**
1838      * Comparison for equality.
1839      *
1840      * Params:
1841      *  that = The list to compare with.
1842      *
1843      * Returns: $(D_KEYWORD true) if the lists are equal, $(D_KEYWORD false)
1844      *          otherwise.
1845      */
1846     bool opEquals()(auto ref typeof(this) that) inout
1847     {
1848         return equal(this[], that[]);
1849     }
1850 
1851     ///
1852     @nogc nothrow pure @safe unittest
1853     {
1854         DList!int l1, l2;
1855 
1856         l1.insertFront(8);
1857         l1.insertFront(9);
1858         l2.insertFront(8);
1859         l2.insertFront(10);
1860         assert(l1 != l2);
1861 
1862         l1.removeFront();
1863         assert(l1 != l2);
1864 
1865         l2.removeFront();
1866         assert(l1 == l2);
1867 
1868         l1.removeFront();
1869         assert(l1 != l2);
1870 
1871         l2.removeFront();
1872         assert(l1 == l2);
1873     }
1874 
1875     /**
1876      * Returns: $(D_KEYWORD true) if the list is empty.
1877      */
1878     @property bool empty() const
1879     {
1880         return this.head is null;
1881     }
1882 
1883     /**
1884      * Removes the front or back element.
1885      *
1886      * Precondition: $(D_INLINECODE !empty)
1887      */
1888     void removeFront()
1889     in
1890     {
1891         assert(!empty);
1892     }
1893     do
1894     {
1895         auto n = this.head.next;
1896 
1897         allocator.dispose(this.head);
1898         this.head = n;
1899         if (this.head is null)
1900         {
1901             this.tail = null;
1902         }
1903         else
1904         {
1905             this.head.prev = null;
1906         }
1907     }
1908 
1909     ///
1910     @nogc nothrow pure @safe unittest
1911     {
1912         DList!int l;
1913 
1914         l.insertFront(8);
1915         l.insertFront(9);
1916         assert(l.front == 9);
1917         l.removeFront();
1918         assert(l.front == 8);
1919         l.removeFront();
1920         assert(l.empty);
1921     }
1922 
1923     /// ditto
1924     void removeBack()
1925     in
1926     {
1927         assert(!empty);
1928     }
1929     do
1930     {
1931         auto n = this.tail.prev;
1932 
1933         allocator.dispose(this.tail);
1934         this.tail = n;
1935         if (this.tail is null)
1936         {
1937             this.head = null;
1938         }
1939         else
1940         {
1941             this.tail.next = null;
1942         }
1943     }
1944 
1945     ///
1946     @nogc nothrow pure @safe unittest
1947     {
1948         auto l = DList!int([9, 8]);
1949 
1950         assert(l.back == 8);
1951         l.removeBack();
1952         assert(l.back == 9);
1953         l.removeFront();
1954         assert(l.empty);
1955     }
1956 
1957     /**
1958      * Removes $(D_PARAM howMany) elements from the list.
1959      *
1960      * Unlike $(D_PSYMBOL removeFront()) and $(D_PSYMBOL removeBack()), this
1961      * method doesn't fail, if it could not remove $(D_PARAM howMany) elements.
1962      * Instead, if $(D_PARAM howMany) is greater than the list length, all
1963      * elements are removed.
1964      *
1965      * Params:
1966      *  howMany = How many elements should be removed.
1967      *
1968      * Returns: The number of elements removed.
1969      */
1970     size_t removeFront(size_t howMany)
1971     out (removed)
1972     {
1973         assert(removed <= howMany);
1974     }
1975     do
1976     {
1977         size_t i;
1978         for (; i < howMany && !empty; ++i)
1979         {
1980             removeFront();
1981         }
1982         return i;
1983     }
1984 
1985     ///
1986     @nogc nothrow pure @safe unittest
1987     {
1988         DList!int l = DList!int([8, 5, 4]);
1989 
1990         assert(l.removeFront(0) == 0);
1991         assert(l.removeFront(2) == 2);
1992         assert(l.removeFront(3) == 1);
1993         assert(l.removeFront(3) == 0);
1994     }
1995 
1996     /// ditto
1997     size_t removeBack(size_t howMany)
1998     out (removed)
1999     {
2000         assert(removed <= howMany);
2001     }
2002     do
2003     {
2004         size_t i;
2005         for (; i < howMany && !empty; ++i)
2006         {
2007             removeBack();
2008         }
2009         return i;
2010     }
2011 
2012     ///
2013     @nogc nothrow pure @safe unittest
2014     {
2015         DList!int l = DList!int([8, 5, 4]);
2016 
2017         assert(l.removeBack(0) == 0);
2018         assert(l.removeBack(2) == 2);
2019         assert(l.removeBack(3) == 1);
2020         assert(l.removeBack(3) == 0);
2021     }
2022 
2023     /**
2024      * Removes $(D_PARAM r) from the list.
2025      *
2026      * Params:
2027      *  r = The range to remove.
2028      *
2029      * Returns: Range spanning the elements just after $(D_PARAM r).
2030      *
2031      * Precondition: $(D_PARAM r) is extracted from this list.
2032      */
2033     Range remove(scope Range r)
2034     in
2035     {
2036         assert(checkRangeBelonging(r));
2037     }
2038     do
2039     {
2040         // Save references to the elements before and after the range.
2041         Entry* headPrev;
2042         Entry** tailNext;
2043         if (*r.tail !is null)
2044         {
2045             tailNext = &(*r.tail).next;
2046         }
2047         if (*r.head !is null)
2048         {
2049             headPrev = (*r.head).prev;
2050         }
2051 
2052         // Remove the elements.
2053         Entry* e = *r.head;
2054         while (e !is *tailNext)
2055         {
2056             auto next = e.next;
2057             allocator.dispose(e);
2058             e = next;
2059         }
2060 
2061         // Connect the elements before and after the removed range.
2062         if (*tailNext !is null)
2063         {
2064             (*tailNext).prev = headPrev;
2065         }
2066         else
2067         {
2068             this.tail = headPrev;
2069         }
2070         if (headPrev !is null)
2071         {
2072             headPrev.next = *tailNext;
2073         }
2074         else
2075         {
2076             this.head = *tailNext;
2077         }
2078         r.head = tailNext;
2079         r.tail = &this.tail;
2080 
2081         return r;
2082     }
2083 
2084     ///
2085     @nogc nothrow pure @safe unittest
2086     {
2087         auto l1 = DList!int([5, 234, 30, 1]);
2088         auto l2 = DList!int([5]);
2089         auto r = l1[];
2090 
2091         r.popFront();
2092 
2093         assert(l1.remove(r).empty);
2094         assert(l1 == l2);
2095     }
2096 
2097     /**
2098      * Removes the front or back element of the $(D_PARAM range) from the list
2099      * respectively.
2100      *
2101      * Params:
2102      *  range = Range whose element should be removed.
2103      *
2104      * Returns: $(D_PSYMBOL range) with its front or back element removed.
2105      *
2106      * Precondition: $(D_INLINECODE !range.empty).
2107      *               $(D_PARAM range) is extracted from this list.
2108      */
2109     Range popFirstOf(Range range)
2110     in
2111     {
2112         assert(!range.empty);
2113     }
2114     do
2115     {
2116         remove(Range(*range.head, *range.head));
2117         return range;
2118     }
2119 
2120     /// ditto
2121     Range popLastOf(Range range)
2122     in
2123     {
2124         assert(!range.empty);
2125     }
2126     do
2127     {
2128         remove(Range(*range.tail, *range.tail));
2129         return range;
2130     }
2131 
2132     ///
2133     @nogc nothrow pure @safe unittest
2134     {
2135         auto list = DList!int([5, 234, 30]);
2136         auto range = list[];
2137 
2138         range.popFront();
2139         range = list.popFirstOf(range);
2140         assert(range.front == 30);
2141         assert(range.back == 30);
2142 
2143         assert(list.popLastOf(range).empty);
2144         assert(list[].front == 5);
2145         assert(list[].back == 5);
2146     }
2147 
2148     /**
2149      * Returns: Range that iterates over all elements of the container, in
2150      *          forward order.
2151      */
2152     Range opIndex()
2153     {
2154         return typeof(return)(this.head, this.tail);
2155     }
2156 
2157     /// ditto
2158     ConstRange opIndex() const
2159     {
2160         return typeof(return)(this.head, this.tail);
2161     }
2162 
2163     /**
2164      * Assigns another list.
2165      *
2166      * If $(D_PARAM that) is passed by value, it won't be copied, but moved.
2167      * This list will take the ownership over $(D_PARAM that)'s storage and
2168      * the allocator.
2169      *
2170      * If $(D_PARAM that) is passed by reference, it will be copied.
2171      *
2172      * Params:
2173      *  R    = Content type.
2174      *  that = The value should be assigned.
2175      *
2176      * Returns: $(D_KEYWORD this).
2177      */
2178     ref typeof(this) opAssign(R)(ref R that)
2179     if (is(Unqual!R == DList))
2180     {
2181         return this = that[];
2182     }
2183 
2184     /// ditto
2185     ref typeof(this) opAssign(R)(R that)
2186     if (is(R == DList))
2187     {
2188         swap(this.head, that.head);
2189         swap(this.tail, that.tail);
2190         swap(this.allocator_, that.allocator_);
2191         return this;
2192     }
2193 
2194     /**
2195      * Assigns an input range.
2196      *
2197      * Params:
2198      *  R    = Type of the initial range.
2199      *  that = Values to initialize the list with.
2200      *
2201      * Returns: $(D_KEYWORD this).
2202      */
2203     ref typeof(this) opAssign(R)(scope R that) @trusted
2204     if (!isInfinite!R
2205      && isInputRange!R
2206      && isImplicitlyConvertible!(ElementType!R, T))
2207     {
2208         Entry** next = &this.head;
2209 
2210         while (!that.empty && *next !is null)
2211         {
2212             (*next).content = that.front;
2213             next = &(*next).next;
2214             that.popFront();
2215         }
2216         if (that.empty)
2217         {
2218             remove(Range(*next, this.tail));
2219         }
2220         else
2221         {
2222             insertBack(that);
2223         }
2224         return this;
2225     }
2226 
2227     ///
2228     @nogc nothrow pure @safe unittest
2229     {
2230         auto l1 = DList!int([5, 4, 9]);
2231         auto l2 = DList!int([9, 4]);
2232         l1 = l2[];
2233         assert(l1 == l2);
2234     }
2235 
2236     /**
2237      * Assigns a static array.
2238      *
2239      * Params:
2240      *  R    = Static array size.
2241      *  that = Values to initialize the list with.
2242      *
2243      * Returns: $(D_KEYWORD this).
2244      */
2245     ref typeof(this) opAssign(size_t R)(T[R] that)
2246     {
2247         return opAssign!(T[])(that[]);
2248     }
2249 
2250     ///
2251     @nogc nothrow pure @safe unittest
2252     {
2253         auto l1 = DList!int([5, 4, 9]);
2254         auto l2 = DList!int([9, 4]);
2255         l1 = [9, 4];
2256         assert(l1 == l2);
2257     }
2258 
2259     mixin DefaultAllocator;
2260 }
2261 
2262 ///
2263 @nogc nothrow pure @safe unittest
2264 {
2265     DList!int l;
2266     size_t i;
2267 
2268     l.insertFront(5);
2269     l.insertFront(4);
2270     l.insertFront(9);
2271     foreach (e; l)
2272     {
2273         assert(i != 0 || e == 9);
2274         assert(i != 1 || e == 4);
2275         assert(i != 2 || e == 5);
2276         ++i;
2277     }
2278     assert(i == 3);
2279 }