1 /**
2 This module implements a singly-linked list container.
3 It can be used as a stack.
4 
5 This module is a submodule of $(MREF std, container).
6 
7 Source: $(PHOBOSSRC std/container/slist.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.slist;
20 
21 ///
22 @safe unittest
23 {
24     import std.algorithm.comparison : equal;
25     import std.container : SList;
26 
27     auto s = SList!int(1, 2, 3);
28     assert(equal(s[], [1, 2, 3]));
29 
30     s.removeFront();
31     assert(equal(s[], [2, 3]));
32 
33     s.insertFront([5, 6]);
34     assert(equal(s[], [5, 6, 2, 3]));
35 
36     // If you want to apply range operations, simply slice it.
37     import std.algorithm.searching : countUntil;
38     import std.range : popFrontN, walkLength;
39 
40     auto sl = SList!int(1, 2, 3, 4, 5);
41     assert(countUntil(sl[], 2) == 1);
42 
43     auto r = sl[];
44     popFrontN(r, 2);
45     assert(walkLength(r) == 3);
46 }
47 
48 public import std.container.util;
49 
50 /**
51    Implements a simple and fast singly-linked list.
52    It can be used as a stack.
53 
54    `SList` uses reference semantics.
55  */
56 struct SList(T)
57 if (!is(T == shared))
58 {
59     import std.exception : enforce;
60     import std.range : Take;
61     import std.range.primitives : isInputRange, isForwardRange, ElementType;
62     import std.traits : isImplicitlyConvertible;
63 
64     private struct Node
65     {
66         Node * _next;
67         T _payload;
68     }
69     private struct NodeWithoutPayload
70     {
71         Node* _next;
72     }
73     static assert(NodeWithoutPayload._next.offsetof == Node._next.offsetof);
74 
75     private Node * _root;
76 
77     private void initialize() @trusted nothrow pure
78     {
79         if (_root) return;
80         _root = cast (Node*) new NodeWithoutPayload();
81     }
82 
83     private ref inout(Node*) _first() @property @safe nothrow pure inout
84     {
85         assert(_root, "root pointer must not be null");
86         return _root._next;
87     }
88 
89     private static Node * findLastNode(Node * n)
90     {
91         assert(n, "Node n pointer must not be null");
92         auto ahead = n._next;
93         while (ahead)
94         {
95             n = ahead;
96             ahead = n._next;
97         }
98         return n;
99     }
100 
101     private static Node * findLastNode(Node * n, size_t limit)
102     {
103         assert(n, "Node n pointer must not be null");
104         assert(limit, "limit must be greater than 0");
105         auto ahead = n._next;
106         while (ahead)
107         {
108             if (!--limit) break;
109             n = ahead;
110             ahead = n._next;
111         }
112         return n;
113     }
114 
115     private static Node * findNode(Node * n, Node * findMe)
116     {
117         assert(n, "Node n pointer must not be null");
118         auto ahead = n._next;
119         while (ahead != findMe)
120         {
121             n = ahead;
122             enforce(n);
123             ahead = n._next;
124         }
125         return n;
126     }
127 
128     private static Node* findNodeByValue(Node* n, T value)
129     {
130         if (!n) return null;
131         auto ahead = n._next;
132         while (ahead && ahead._payload != value)
133         {
134             n = ahead;
135             ahead = n._next;
136         }
137         return n;
138     }
139 
140     private static auto createNodeChain(Stuff)(Stuff stuff)
141     if (isImplicitlyConvertible!(Stuff, T))
142     {
143         import std.range : only;
144         return createNodeChain(only(stuff));
145     }
146 
147     private static auto createNodeChain(Stuff)(Stuff stuff)
148     if (isInputRange!Stuff && isImplicitlyConvertible!(ElementType!Stuff, T))
149     {
150         static struct Chain
151         {
152             Node* first;
153             Node* last;
154             size_t length;
155         }
156 
157         Chain ch;
158 
159         foreach (item; stuff)
160         {
161             auto newNode = new Node(null, item);
162             (ch.first ? ch.last._next : ch.first) = newNode;
163             ch.last = newNode;
164             ++ch.length;
165         }
166 
167         return ch;
168     }
169 
170     private static size_t insertAfterNode(Stuff)(Node* n, Stuff stuff)
171     {
172         auto ch = createNodeChain(stuff);
173 
174         if (!ch.length) return 0;
175 
176         ch.last._next = n._next;
177         n._next = ch.first;
178 
179         return ch.length;
180     }
181 
182 /**
183 Constructor taking a number of nodes
184      */
185     this(U)(U[] values...)
186     if (isImplicitlyConvertible!(U, T))
187     {
188         insertFront(values);
189     }
190 
191 /**
192 Constructor taking an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
193      */
194     this(Stuff)(Stuff stuff)
195     if (isInputRange!Stuff
196             && isImplicitlyConvertible!(ElementType!Stuff, T)
197             && !is(Stuff == T[]))
198     {
199         insertFront(stuff);
200     }
201 
202 /**
203 Comparison for equality.
204 
205 Complexity: $(BIGOH min(n, n1)) where `n1` is the number of
206 elements in `rhs`.
207      */
208     bool opEquals(const SList rhs) const
209     {
210         return opEquals(rhs);
211     }
212 
213     /// ditto
214     bool opEquals(ref const SList rhs) const
215     {
216         if (_root is rhs._root) return true;
217         if (_root is null) return rhs._root is null || rhs._first is null;
218         if (rhs._root is null) return _root is null || _first is null;
219 
220         const(Node) * n1 = _first, n2 = rhs._first;
221 
222         for (;; n1 = n1._next, n2 = n2._next)
223         {
224             if (!n1) return !n2;
225             if (!n2 || n1._payload != n2._payload) return false;
226         }
227     }
228 
229 /**
230 Defines the container's primary range, which embodies a forward range.
231      */
232     struct Range
233     {
234         private Node * _head;
235         private this(Node * p) { _head = p; }
236 
237         /// Input range primitives.
238         @property bool empty() const { return !_head; }
239 
240         /// ditto
241         @property ref T front()
242         {
243             assert(!empty, "SList.Range.front: Range is empty");
244             return _head._payload;
245         }
246 
247         /// ditto
248         void popFront()
249         {
250             assert(!empty, "SList.Range.popFront: Range is empty");
251             _head = _head._next;
252         }
253 
254         /// Forward range primitive.
255         @property Range save() { return this; }
256 
257         T moveFront()
258         {
259             import std.algorithm.mutation : move;
260 
261             assert(!empty, "SList.Range.moveFront: Range is empty");
262             return move(_head._payload);
263         }
264 
265         bool sameHead(Range rhs)
266         {
267             return _head && _head == rhs._head;
268         }
269     }
270 
271     @safe unittest
272     {
273         static assert(isForwardRange!Range);
274     }
275 
276 /**
277 Property returning `true` if and only if the container has no
278 elements.
279 
280 Complexity: $(BIGOH 1)
281      */
282     @property bool empty() const
283     {
284         return _root is null || _first is null;
285     }
286 
287 /**
288 Duplicates the container. The elements themselves are not transitively
289 duplicated.
290 
291 Complexity: $(BIGOH n).
292      */
293     @property SList dup()
294     {
295         return SList(this[]);
296     }
297 
298 /**
299 Returns a range that iterates over all elements of the container, in
300 forward order.
301 
302 Complexity: $(BIGOH 1)
303      */
304     Range opSlice()
305     {
306         if (empty)
307             return Range(null);
308         else
309             return Range(_first);
310     }
311 
312 /**
313 Forward to `opSlice().front`.
314 
315 Complexity: $(BIGOH 1)
316      */
317     @property ref T front()
318     {
319         assert(!empty, "SList.front: List is empty");
320         return _first._payload;
321     }
322 
323     @safe unittest
324     {
325         auto s = SList!int(1, 2, 3);
326         s.front = 42;
327         assert(s == SList!int(42, 2, 3));
328     }
329 
330 /**
331 Returns a new `SList` that's the concatenation of `this` and its
332 argument. `opBinaryRight` is only defined if `Stuff` does not
333 define `opBinary`.
334      */
335     SList opBinary(string op, Stuff)(Stuff rhs)
336     if (op == "~" && is(typeof(SList(rhs))))
337     {
338         import std.range : chain, only;
339 
340         static if (isInputRange!Stuff)
341             alias r = rhs;
342         else
343             auto r = only(rhs);
344 
345         return SList(this[].chain(r));
346     }
347 
348     /// ditto
349     SList opBinaryRight(string op, Stuff)(Stuff lhs)
350     if (op == "~" && !is(typeof(lhs.opBinary!"~"(this))) && is(typeof(SList(lhs))))
351     {
352         import std.range : chain, only;
353 
354         static if (isInputRange!Stuff)
355             alias r = lhs;
356         else
357             auto r = only(lhs);
358 
359         return SList(r.chain(this[]));
360     }
361 
362 /**
363 Removes all contents from the `SList`.
364 
365 Postcondition: `empty`
366 
367 Complexity: $(BIGOH 1)
368      */
369     void clear()
370     {
371         if (_root)
372             _first = null;
373     }
374 
375 /**
376 Reverses SList in-place. Performs no memory allocation.
377 
378 Complexity: $(BIGOH n)
379      */
380     void reverse()
381     {
382         if (!empty)
383         {
384             Node* prev;
385             while (_first)
386             {
387                 auto next = _first._next;
388                 _first._next = prev;
389                 prev = _first;
390                 _first = next;
391             }
392             _first = prev;
393         }
394     }
395 
396 /**
397 Inserts `stuff` to the front of the container. `stuff` can be a
398 value convertible to `T` or a range of objects convertible to $(D
399 T). The stable version behaves the same, but guarantees that ranges
400 iterating over the container are never invalidated.
401 
402 Returns: The number of elements inserted
403 
404 Complexity: $(BIGOH m), where `m` is the length of `stuff`
405      */
406     size_t insertFront(Stuff)(Stuff stuff)
407     if (isInputRange!Stuff || isImplicitlyConvertible!(Stuff, T))
408     {
409         initialize();
410         return insertAfterNode(_root, stuff);
411     }
412 
413     /// ditto
414     alias insert = insertFront;
415 
416     /// ditto
417     alias stableInsert = insert;
418 
419     /// ditto
420     alias stableInsertFront = insertFront;
421 
422 /**
423 Picks one value in an unspecified position in the container, removes
424 it from the container, and returns it. The stable version behaves the same,
425 but guarantees that ranges iterating over the container are never invalidated.
426 
427 Precondition: `!empty`
428 
429 Returns: The element removed.
430 
431 Complexity: $(BIGOH 1).
432      */
433     T removeAny()
434     {
435         import std.algorithm.mutation : move;
436 
437         assert(!empty, "SList.removeAny: List is empty");
438         auto result = move(_first._payload);
439         _first = _first._next;
440         return result;
441     }
442     /// ditto
443     alias stableRemoveAny = removeAny;
444 
445 /**
446 Removes the value at the front of the container. The stable version
447 behaves the same, but guarantees that ranges iterating over the
448 container are never invalidated.
449 
450 Precondition: `!empty`
451 
452 Complexity: $(BIGOH 1).
453      */
454     void removeFront()
455     {
456         assert(!empty, "SList.removeFront: List is empty");
457         _first = _first._next;
458     }
459 
460     /// ditto
461     alias stableRemoveFront = removeFront;
462 
463 /**
464 Removes `howMany` values at the front or back of the
465 container. Unlike the unparameterized versions above, these functions
466 do not throw if they could not remove `howMany` elements. Instead,
467 if $(D howMany > n), all elements are removed. The returned value is
468 the effective number of elements removed. The stable version behaves
469 the same, but guarantees that ranges iterating over the container are
470 never invalidated.
471 
472 Returns: The number of elements removed
473 
474 Complexity: $(BIGOH howMany * log(n)).
475      */
476     size_t removeFront(size_t howMany)
477     {
478         size_t result;
479         while (_first && result < howMany)
480         {
481             _first = _first._next;
482             ++result;
483         }
484         return result;
485     }
486 
487     /// ditto
488     alias stableRemoveFront = removeFront;
489 
490 /**
491 Inserts `stuff` after range `r`, which must be a range
492 previously extracted from this container. Given that all ranges for a
493 list end at the end of the list, this function essentially appends to
494 the list and uses `r` as a potentially fast way to reach the last
495 node in the list. Ideally `r` is positioned near or at the last
496 element of the list.
497 
498 `stuff` can be a value convertible to `T` or a range of objects
499 convertible to `T`. The stable version behaves the same, but
500 guarantees that ranges iterating over the container are never
501 invalidated.
502 
503 Returns: The number of values inserted.
504 
505 Complexity: $(BIGOH k + m), where `k` is the number of elements in
506 `r` and `m` is the length of `stuff`.
507 
508 Example:
509 $(RUNNABLE_EXAMPLE
510 --------------------
511 import std.algorithm, std.container, std.range;
512 
513 auto sl = SList!string(["a", "b", "d"]);
514 sl.insertAfter(sl[], "e"); // insert at the end (slowest)
515 assert(equal(sl[], ["a", "b", "d", "e"]));
516 
517 sl.insertAfter(take(sl[], 2), "c"); // insert after "b"
518 assert(equal(sl[], ["a", "b", "c", "d", "e"]));
519 --------------------
520 )
521      */
522 
523     size_t insertAfter(Stuff)(Range r, Stuff stuff)
524     if (isInputRange!Stuff || isImplicitlyConvertible!(Stuff, T))
525     {
526         initialize();
527         if (!_first)
528         {
529             enforce(!r._head);
530             return insertFront(stuff);
531         }
532         enforce(r._head);
533         auto n = findLastNode(r._head);
534         return insertAfterNode(n, stuff);
535     }
536 
537 /**
538 Similar to `insertAfter` above, but accepts a range bounded in
539 count. This is important for ensuring fast insertions in the middle of
540 the list.  For fast insertions after a specified position `r`, use
541 $(D insertAfter(take(r, 1), stuff)). The complexity of that operation
542 only depends on the number of elements in `stuff`.
543 
544 Precondition: $(D r.original.empty || r.maxLength > 0)
545 
546 Returns: The number of values inserted.
547 
548 Complexity: $(BIGOH k + m), where `k` is the number of elements in
549 `r` and `m` is the length of `stuff`.
550      */
551     size_t insertAfter(Stuff)(Take!Range r, Stuff stuff)
552     if (isInputRange!Stuff || isImplicitlyConvertible!(Stuff, T))
553     {
554         auto orig = r.source;
555         if (!orig._head)
556         {
557             // Inserting after a null range counts as insertion to the
558             // front
559             return insertFront(stuff);
560         }
561         enforce(!r.empty);
562         // Find the last valid element in the range
563         foreach (i; 1 .. r.maxLength)
564         {
565             if (!orig._head._next) break;
566             orig.popFront();
567         }
568         // insert here
569         return insertAfterNode(orig._head, stuff);
570     }
571 
572 /// ditto
573     alias stableInsertAfter = insertAfter;
574 
575 /**
576 Removes a range from the list in linear time.
577 
578 Returns: An empty range.
579 
580 Complexity: $(BIGOH n)
581      */
582     Range linearRemove(Range r)
583     {
584         if (!_first)
585         {
586             enforce(!r._head);
587             return this[];
588         }
589         auto n = findNode(_root, r._head);
590         n._next = null;
591         return Range(null);
592     }
593 
594 /**
595 Removes a `Take!Range` from the list in linear time.
596 
597 Returns: A range comprehending the elements after the removed range.
598 
599 Complexity: $(BIGOH n)
600      */
601     Range linearRemove(Take!Range r)
602     {
603         auto orig = r.source;
604         // We have something to remove here
605         if (orig._head == _first)
606         {
607             // remove straight from the head of the list
608             for (; !r.empty; r.popFront())
609             {
610                 removeFront();
611             }
612             return this[];
613         }
614         if (!r.maxLength)
615         {
616             // Nothing to remove, return the range itself
617             return orig;
618         }
619         // Remove from somewhere in the middle of the list
620         enforce(_first);
621         auto n1 = findNode(_root, orig._head);
622         auto n2 = findLastNode(orig._head, r.maxLength);
623         n1._next = n2._next;
624         return Range(n1._next);
625     }
626 
627 /// ditto
628     alias stableLinearRemove = linearRemove;
629 
630 /**
631 Removes the first occurence of an element from the list in linear time.
632 
633 Returns: True if the element existed and was successfully removed, false otherwise.
634 
635 Params:
636     value = value of the node to be removed
637 
638 Complexity: $(BIGOH n)
639      */
640     bool linearRemoveElement(T value)
641     {
642         auto n1 = findNodeByValue(_root, value);
643 
644         if (n1 && n1._next)
645         {
646             n1._next = n1._next._next;
647             return true;
648         }
649 
650         return false;
651     }
652 }
653 
654 @safe unittest
655 {
656     import std.algorithm.comparison : equal;
657 
658     auto e = SList!int();
659     auto b = e.linearRemoveElement(2);
660     assert(b == false);
661     assert(e.empty());
662     auto a = SList!int(-1, 1, 2, 1, 3, 4);
663     b = a.linearRemoveElement(1);
664     assert(equal(a[], [-1, 2, 1, 3, 4]));
665     assert(b == true);
666     b = a.linearRemoveElement(-1);
667     assert(b == true);
668     assert(equal(a[], [2, 1, 3, 4]));
669     b = a.linearRemoveElement(1);
670     assert(b == true);
671     assert(equal(a[], [2, 3, 4]));
672     b = a.linearRemoveElement(2);
673     assert(b == true);
674     b = a.linearRemoveElement(20);
675     assert(b == false);
676     assert(equal(a[], [3, 4]));
677     b = a.linearRemoveElement(4);
678     assert(b == true);
679     assert(equal(a[], [3]));
680     b = a.linearRemoveElement(3);
681     assert(b == true);
682     assert(a.empty());
683     a.linearRemoveElement(3);
684 }
685 
686 @safe unittest
687 {
688     import std.algorithm.comparison : equal;
689 
690     auto a = SList!int(5);
691     auto b = a;
692     auto r = a[];
693     a.insertFront(1);
694     b.insertFront(2);
695     assert(equal(a[], [2, 1, 5]));
696     assert(equal(b[], [2, 1, 5]));
697     r.front = 9;
698     assert(equal(a[], [2, 1, 9]));
699     assert(equal(b[], [2, 1, 9]));
700 }
701 
702 @safe unittest
703 {
704     auto s = SList!int(1, 2, 3);
705     auto n = s.findLastNode(s._root);
706     assert(n && n._payload == 3);
707 }
708 
709 @safe unittest
710 {
711     import std.range.primitives;
712     auto s = SList!int(1, 2, 5, 10);
713     assert(walkLength(s[]) == 4);
714 }
715 
716 @safe unittest
717 {
718     import std.range : take;
719     auto src = take([0, 1, 2, 3], 3);
720     auto s = SList!int(src);
721     assert(s == SList!int(0, 1, 2));
722 }
723 
724 @safe unittest
725 {
726     auto a = SList!int();
727     auto b = SList!int();
728     auto c = a ~ b[];
729     assert(c.empty);
730 }
731 
732 @safe unittest
733 {
734     auto a = SList!int(1, 2, 3);
735     auto b = SList!int(4, 5, 6);
736     auto c = a ~ b[];
737     assert(c == SList!int(1, 2, 3, 4, 5, 6));
738 }
739 
740 @safe unittest
741 {
742     auto a = SList!int(1, 2, 3);
743     auto b = [4, 5, 6];
744     auto c = a ~ b;
745     assert(c == SList!int(1, 2, 3, 4, 5, 6));
746 }
747 
748 @safe unittest
749 {
750     auto a = SList!int(1, 2, 3);
751     auto c = a ~ 4;
752     assert(c == SList!int(1, 2, 3, 4));
753 }
754 
755 @safe unittest
756 {
757     auto a = SList!int(2, 3, 4);
758     auto b = 1 ~ a;
759     assert(b == SList!int(1, 2, 3, 4));
760 }
761 
762 @safe unittest
763 {
764     auto a = [1, 2, 3];
765     auto b = SList!int(4, 5, 6);
766     auto c = a ~ b;
767     assert(c == SList!int(1, 2, 3, 4, 5, 6));
768 }
769 
770 @safe unittest
771 {
772     auto s = SList!int(1, 2, 3, 4);
773     s.insertFront([ 42, 43 ]);
774     assert(s == SList!int(42, 43, 1, 2, 3, 4));
775 }
776 
777 @safe unittest
778 {
779     auto s = SList!int(1, 2, 3);
780     assert(s.removeAny() == 1);
781     assert(s == SList!int(2, 3));
782     assert(s.stableRemoveAny() == 2);
783     assert(s == SList!int(3));
784 }
785 
786 @safe unittest
787 {
788     import std.algorithm.comparison : equal;
789 
790     auto s = SList!int(1, 2, 3);
791     s.removeFront();
792     assert(equal(s[], [2, 3]));
793     s.stableRemoveFront();
794     assert(equal(s[], [3]));
795 }
796 
797 @safe unittest
798 {
799     auto s = SList!int(1, 2, 3, 4, 5, 6, 7);
800     assert(s.removeFront(3) == 3);
801     assert(s == SList!int(4, 5, 6, 7));
802 }
803 
804 @safe unittest
805 {
806     auto a = SList!int(1, 2, 3);
807     auto b = SList!int(1, 2, 3);
808     assert(a.insertAfter(a[], b[]) == 3);
809 }
810 
811 @safe unittest
812 {
813     import std.range : take;
814     auto s = SList!int(1, 2, 3, 4);
815     auto r = take(s[], 2);
816     assert(s.insertAfter(r, 5) == 1);
817     assert(s == SList!int(1, 2, 5, 3, 4));
818 }
819 
820 @safe unittest
821 {
822     import std.algorithm.comparison : equal;
823     import std.range : take;
824 
825     // insertAfter documentation example
826     auto sl = SList!string(["a", "b", "d"]);
827     sl.insertAfter(sl[], "e"); // insert at the end (slowest)
828     assert(equal(sl[], ["a", "b", "d", "e"]));
829     sl.insertAfter(take(sl[], 2), "c"); // insert after "b"
830     assert(equal(sl[], ["a", "b", "c", "d", "e"]));
831 }
832 
833 @safe unittest
834 {
835     import std.range.primitives;
836     auto s = SList!int(1, 2, 3, 4, 5);
837     auto r = s[];
838     popFrontN(r, 3);
839     auto r1 = s.linearRemove(r);
840     assert(s == SList!int(1, 2, 3));
841     assert(r1.empty);
842 }
843 
844 @safe unittest
845 {
846     auto s = SList!int(1, 2, 3, 4, 5);
847     auto r = s[];
848     auto r1 = s.linearRemove(r);
849     assert(s == SList!int());
850     assert(r1.empty);
851 }
852 
853 @safe unittest
854 {
855     import std.algorithm.comparison : equal;
856     import std.range;
857 
858     auto s = SList!int(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
859     auto r = s[];
860     popFrontN(r, 3);
861     auto r1 = take(r, 4);
862     assert(equal(r1, [4, 5, 6, 7]));
863     auto r2 = s.linearRemove(r1);
864     assert(s == SList!int(1, 2, 3, 8, 9, 10));
865     assert(equal(r2, [8, 9, 10]));
866 }
867 
868 @safe unittest
869 {
870     import std.range.primitives;
871     auto lst = SList!int(1, 5, 42, 9);
872     assert(!lst.empty);
873     assert(lst.front == 1);
874     assert(walkLength(lst[]) == 4);
875 
876     auto lst2 = lst ~ [ 1, 2, 3 ];
877     assert(walkLength(lst2[]) == 7);
878 
879     auto lst3 = lst ~ [ 7 ];
880     assert(walkLength(lst3[]) == 5);
881 }
882 
883 @safe unittest
884 {
885     auto s = make!(SList!int)(1, 2, 3);
886 }
887 
888 // https://issues.dlang.org/show_bug.cgi?id=5193
889 @safe unittest
890 {
891     static struct Data
892     {
893         const int val;
894     }
895     SList!Data list;
896 }
897 
898 @safe unittest
899 {
900     auto s = SList!int([1, 2, 3]);
901     s.front = 5; //test frontAssign
902     assert(s.front == 5);
903     auto r = s[];
904     r.front = 1; //test frontAssign
905     assert(r.front == 1);
906 }
907 
908 // https://issues.dlang.org/show_bug.cgi?id=14920
909 @safe unittest
910 {
911     SList!int s;
912     s.insertAfter(s[], 1);
913     assert(s.front == 1);
914 }
915 
916 // https://issues.dlang.org/show_bug.cgi?id=15659
917 @safe unittest
918 {
919     SList!int s;
920     s.clear();
921 }
922 
923 @safe unittest
924 {
925     SList!int s;
926     s.reverse();
927 }
928 
929 @safe unittest
930 {
931     import std.algorithm.comparison : equal;
932 
933     auto s = SList!int([1, 2, 3]);
934     assert(s[].equal([1, 2, 3]));
935 
936     s.reverse();
937     assert(s[].equal([3, 2, 1]));
938 }
939 
940 @safe unittest
941 {
942     auto s = SList!int([4, 6, 8, 12, 16]);
943     auto d = s.dup;
944     assert(d !is s);
945     assert(d == s);
946 }