1 module cachetools.containers.lists;
2 
3 private import std.experimental.allocator;
4 private import std.experimental.allocator.mallocator : Mallocator;
5 private import std.experimental.logger;
6 private import std.format;
7 
8 ///
9 /// N-way multilist
10 struct MultiDList(T, int N, Allocator = Mallocator)
11 {
12     alias allocator = Allocator.instance;
13     struct Node {
14         T payload;
15         private:
16         Link[N] links;
17         Node* next(size_t i) @safe @nogc
18         {
19             return links[i].next;
20         }
21         Node* prev(size_t i) @safe @nogc
22         {
23             return links[i].prev;
24         }
25         alias payload this;
26     }
27     private 
28     {
29         struct Link
30         {
31             Node* prev;
32             Node* next;
33         }
34         Node*[N]    _heads;
35         Node*[N]    _tails;
36         size_t      _length;
37         
38     }
39     size_t length() const pure nothrow @safe @nogc {
40         return _length;
41     }
42 
43     Node* insert_last(T v) @safe nothrow
44     out
45     {
46         assert(_length>0);
47     }
48     do
49     {
50         auto n = make!(Node)(allocator, v);
51         static foreach(index;0..N) {
52             if ( _heads[index] is null ) {
53                 _heads[index] = n;
54             }
55             n.links[index].prev = _tails[index];
56             if ( _tails[index] !is null )
57             {
58                 _tails[index].links[index].next = n;
59             }
60             _tails[index] = n;
61         }
62         _length++;
63         return n;
64     }
65 
66     void move_to_tail(Node* n, size_t i) @safe @nogc
67     in
68     {
69         assert(i < N);
70         assert(_length>0);
71     }
72     out
73     {
74         assert(_heads[i] !is null && _tails[i] !is null);
75     }
76     do
77     {
78         if ( n == _tails[i] ) {
79             return;
80         }
81         // unlink
82         if ( n.links[i].prev is null )
83         {
84             _heads[i] = n.links[i].next;
85         }
86         else
87         {
88             n.links[i].prev.links[i].next = n.links[i].next;
89         }
90         if ( n.links[i].next is null )
91         {
92             _tails[i] = n.links[i].prev;
93         }
94         else
95         {
96             n.links[i].next.links[i].prev = n.links[i].prev;
97         }
98         // insert back
99         if ( _heads[i] is null ) {
100             _heads[i] = n;
101         }
102         n.links[i].prev = _tails[i];
103         if ( _tails[i] !is null )
104         {
105             _tails[i].links[i].next = n;
106         }
107         n.links[i].next = null;
108         _tails[i] = n;
109     }
110 
111     void remove(Node* n) nothrow @safe @nogc
112     {
113         if ( n is null || _length == 0 )
114         {
115             return;
116         }
117         static foreach(i;0..N) {
118             if ( n.links[i].prev !is null ) {
119                 n.links[i].prev.links[i].next = n.links[i].next;
120             }
121             if ( n.links[i].next !is null ) {
122                 n.links[i].next.links[i].prev = n.links[i].prev;
123             }
124             if ( n == _tails[i] ) {
125                 _tails[i] = n.links[i].prev;
126             }
127             if ( n == _heads[i] ) {
128                 _heads[i] = n.links[i].next;
129             }
130         }
131         (() @trusted {dispose(allocator, n);})();
132         _length--;
133     }
134     Node* tail(size_t i) pure nothrow @safe @nogc
135     {
136         return _tails[i];
137     }
138     Node* head(size_t i) pure nothrow @safe @nogc
139     {
140         return _heads[i];
141     }
142     void clear() nothrow @safe @nogc
143     {
144         while(_length>0)
145         {
146             auto n = _heads[0];
147             remove(n);
148         }
149     }
150 }
151 
152 @safe unittest {
153     import std.algorithm;
154     import std.stdio;
155     import std.range;
156     struct Person
157     {
158         string name;
159         int    age;
160     }
161     MultiDList!(Person*, 2) mdlist;
162     Person[3] persons = [{"Alice", 11}, {"Bob", 9}, {"Carl", 10}];
163     foreach(i; 0..persons.length)
164     {
165         mdlist.insert_last(&persons[i]);
166     }
167     enum NameIndex = 0;
168     enum AgeIndex  = 1;
169     assert(mdlist.head(NameIndex).payload.name == "Alice");
170     assert(mdlist.head(AgeIndex).payload.age == 11);
171     assert(mdlist.tail(NameIndex).payload.name == "Carl");
172     assert(mdlist.tail(AgeIndex).payload.age == 10);
173     auto alice = mdlist.head(NameIndex);
174     auto bob = alice.next(NameIndex);
175     auto carl = bob.next(NameIndex);
176     mdlist.move_to_tail(alice, AgeIndex);
177     assert(mdlist.tail(AgeIndex).payload.age == 11);
178     mdlist.remove(alice);
179     assert(mdlist.head(NameIndex).payload.name == "Bob");
180     assert(mdlist.tail(NameIndex).payload.name == "Carl");
181     assert(mdlist.head(AgeIndex).payload.age == 9);
182     assert(mdlist.tail(AgeIndex).payload.age == 10);
183 }
184 
185 struct DList(T, Allocator = Mallocator) {
186     this(this) @disable;
187     struct Node(T) {
188         T payload;
189         private Node!T* prev;
190         private Node!T* next;
191         alias payload this;
192     }
193     private {
194         alias allocator = Allocator.instance;
195         Node!T* _head;
196         Node!T* _tail;
197         ulong   _length;
198     }
199 
200     invariant {
201         assert
202         (
203             ( _length > 0 && _head !is null && _tail !is null) ||
204             ( _length == 0 && _tail is null && _tail is null) ||
205             ( _length == 1 && _tail == _head && _head !is null ),
206             "length: %s, head: %s, tail: %s".format(_length, _head, _tail)
207         );
208     }
209 
210     ulong length() const pure nothrow @safe @nogc {
211         return _length;
212     }
213 
214     Node!T* insert_last(T v) @safe nothrow
215     out
216     {
217         assert(_length>0);
218         assert(_head !is null && _tail !is null);
219     }
220     do
221     {
222         auto n = make!(Node!T)(allocator);
223         n.payload = v;
224         if ( _head is null ) {
225             _head = n;
226         }
227         n.prev = _tail;
228         if ( _tail !is null )
229         {
230             _tail.next = n;
231         }
232         _tail = n;
233         _length++;
234         return n;
235     }
236 
237     alias insertFront = insert_first;
238     Node!T* insert_first(T v) @safe nothrow
239     out
240     {
241         assert(_length>0);
242         assert(_head !is null && _tail !is null);
243     }
244     do 
245     {
246         auto n = make!(Node!T)(allocator);
247         n.payload = v;
248         if ( _tail is null ) {
249             _tail = n;
250         }
251         n.next = _head;
252         if ( _head !is null )
253         {
254             _head.prev = n;
255         }
256         _head = n;
257         _length++;
258         return n;
259     }
260 
261     bool remove(Node!T* n) @safe @nogc
262     in {assert(_length>0);}
263     do {
264         if ( n.prev ) {
265             n.prev.next = n.next;
266         }
267         if ( n.next ) {
268             n.next.prev = n.prev;
269         }
270         if ( n == _tail ) {
271             _tail = n.prev;
272         }
273         if ( n == _head ) {
274             _head = n.next;
275         }
276         (() @trusted {dispose(allocator, n);})();
277         _length--;
278         return true;
279     }
280 
281     void move_to_tail(Node!T* n) @safe @nogc
282     in
283     {
284         assert(_length > 0);
285         assert(_head !is null && _tail !is null);
286     }
287     out {
288         assert(_tail == n && n.next is null);
289     }
290     do
291     {
292         if ( n == _tail ) {
293             return;
294         }
295         // unlink
296         if ( n.prev is null )
297         {
298             _head = n.next;
299         }
300         else
301         {
302             n.prev.next = n.next;
303         }
304         if ( n.next is null )
305         {
306             _tail = n.prev;
307         }
308         else
309         {
310             n.next.prev = n.prev;
311         }
312         // insert back
313         if ( _head is null ) {
314             _head = n;
315         }
316         n.prev = _tail;
317         if ( _tail !is null )
318         {
319             _tail.next = n;
320         }
321         n.next = null;
322         _tail = n;
323 
324         ////debug(cachetools) tracef("n: %s".format(*n));
325         //assert(n.next !is null);
326         ////debug tracef("m-t-t: %s, tail: %s", *n, *_tail);
327         //assert(n.next, "non-tail entry have no 'next' pointer?");
328         //if ( _head == n ) {
329         //    assert(n.prev is null);
330         //    _head = n.next;
331         //} else {
332         //    n.prev.next = n.next;
333         //}
334         //// move this node to end
335         //n.next.prev = n.prev;
336         //n.next = null;
337         //tail.next = n;
338         //_tail = n;
339     }
340 
341     Node!T* head() @safe @nogc nothrow {
342         return _head;
343     }
344     Node!T* tail() @safe @nogc nothrow {
345         return _tail;
346     }
347 }
348 
349 struct SList(T, Allocator = Mallocator) {
350     this(this) @safe
351     {
352         // copy items
353         _Node!T* __newFirst, __newLast;
354         auto f = _first;
355         while(f)
356         {
357             auto v = f.v;
358             auto n = make!(_Node!T)(allocator, v);
359             if ( __newLast !is null ) {
360                 __newLast._next = n;
361             } else {
362                 __newFirst = n;
363             }
364             __newLast = n;
365             f = f._next;
366         }
367         _first = __newFirst;
368         _last = __newLast;
369     }
370 
371     package {
372         struct _Node(T) {
373             T v;
374             _Node!T *_next;
375         }
376         alias allocator = Allocator.instance;
377 
378         ulong _length;
379         _Node!T *_first;
380         _Node!T *_last;
381     }
382 
383     invariant {
384         assert
385         ( 
386             ( _length > 0 && _first !is null && _last !is null) ||
387             ( _length == 0 && _first is null && _last is null)
388         );
389     }
390     ~this()
391     {
392         clear();
393     }
394     ulong length() const pure @nogc @safe nothrow {
395         return _length;
396     }
397 
398     bool empty() @nogc @safe const {
399         return _length == 0;
400     }
401 
402     T front() pure @nogc @safe {
403         return _first.v;
404     }
405 
406     T back() pure @nogc @safe {
407         return _last.v;
408     }
409 
410     T popFront() @nogc @safe nothrow
411     in { assert(_first !is null); }
412     do {
413         T v = _first.v;
414         auto next = _first._next;
415         (() @trusted {dispose(allocator, _first);})();
416         _first = next;
417         if ( _first is null ) {
418             _last = null;
419         }
420         _length--;
421         return v;
422     }
423     void clear() @nogc @safe {
424         _Node!T* n = _first;
425         while( n !is null ) {
426             auto next = n._next;
427             (() @trusted {dispose(allocator, n);})();
428             n = next;
429         }
430     }
431     private struct Range(T) {
432         private {
433             _Node!T *current;
434         }
435         auto front() pure nothrow @safe @nogc @property {
436             return &current.v;
437         }
438         void popFront() @safe @nogc nothrow {
439             current = current._next;
440         }
441         bool empty() pure const nothrow @safe @nogc @property {
442             return current is null;
443         }
444     }
445     alias opSlice = range;
446     auto range() {
447         return Range!T(_first);
448     }
449 
450     void insertFront(T v) @safe nothrow
451     out{ assert(_first !is null && _last !is null);}
452     do {
453         auto n = make!(_Node!T)(allocator, v);
454         if ( _first !is null ) {
455             n._next = _first;
456         }
457         _first = n;
458         if ( _last is null ) {
459             _last = n;
460         }
461         _length++;
462     }
463 
464     void insertBack(T v) @safe nothrow
465     out{ assert(_first !is null && _last !is null);}
466     do {
467         auto n = make!(_Node!T)(allocator, v);
468         if ( _last !is null ) {
469             _last._next = n;
470         } else {
471             _first = n;
472         }
473         _last = n;
474         _length++;
475     }
476     bool remove_by_predicate(scope bool delegate(T) @safe @nogc nothrow f) @nogc @trusted nothrow {
477         bool removed;
478         _Node!T *current = _first;
479         _Node!T *prev = null;
480         while (current !is null) {
481             auto next = current._next;
482             if ( !f(current.v) ) {
483                 prev = current;
484                 current = next;
485                 continue;
486             }
487             // do remove
488             _length--;
489             removed = true;
490             dispose(allocator, current);
491             if ( prev is null ) {
492                 _first = next;                    
493             } else {
494                 prev._next = next;
495             }
496             if ( next is null ) {
497                 _last = prev;
498             }
499         }
500         return removed;
501     }
502 }
503 
504 @safe @nogc unittest {
505     SList!int l;
506     assert(l.length() == 0);
507     l.insertBack(1);
508     assert(l.front() == 1);
509     assert(l.length() == 1);
510     l.insertBack(2);
511     assert(l.front() == 1);
512     assert(l.back() == 2);
513     assert(l.length() == 2);
514     //log(l.range());
515     l.popFront();
516     assert(l.front() == 2);
517     assert(l.back() == 2);
518     assert(l.length() == 1);
519     l.insertBack(3);
520     l.insertBack(4);
521     //foreach(v; l[]){
522     //    log("v=%d\n", *v);
523     //}
524     //log("---\n");
525     bool removed;
526     removed = l.remove_by_predicate((n){return n==2;});
527     foreach(v; l[]){
528         //log("v=%d\n", *v);
529     }
530     assert(removed);
531     assert(l.length()==2);
532     //log("---\n");
533     removed = l.remove_by_predicate((n){return n==4;});
534     foreach(v; l[]){
535         //log("v=%d\n", *v);
536     }
537     assert(removed);
538     assert(l.length()==1);
539     //log("---\n");
540     removed = l.remove_by_predicate((n){return n==3;});
541     foreach(v; l[]){
542         //log("v=%d\n", *v);
543     }
544     assert(removed);
545     assert(l.length()==0);
546     //log("---\n");
547     removed = l.remove_by_predicate((n){return n==3;});
548     foreach(v; l[]){
549         //log("v=%d\n", *v);
550     }
551     assert(!removed);
552     assert(l.length()==0);
553     auto l1 = SList!int();
554     foreach(i;0..100) {
555         l1.insertBack(i);
556     }
557     while(l.length) {
558         l1.popFront();
559     }
560     foreach(i;0..100) {
561         l1.insertFront(i);
562     }
563     while(l.length) {
564         l1.popFront();
565     }
566 }
567 
568 @safe unittest {
569     DList!int dlist;
570     auto n1 = dlist.insert_last(1);
571     assert(dlist.length == 1);
572     dlist.remove(n1);
573     assert(dlist.length == 0);
574 
575     n1 = dlist.insert_first(1);
576     assert(dlist.length == 1);
577     dlist.remove(n1);
578     assert(dlist.length == 0);
579 
580     n1 = dlist.insert_first(1);
581     auto n2 = dlist.insert_last(2);
582     assert(dlist.length == 2);
583     dlist.move_to_tail(n1);
584     assert(dlist.head.payload == 2);
585     assert(dlist.tail.payload == 1);
586 }