1 /*
2 Copyright (c) 2011-2021 Timur Gafarov
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 
6 Permission is hereby granted, free of charge, to any person or organization
7 obtaining a copy of the software and accompanying documentation covered by
8 this license (the "Software") to use, reproduce, display, distribute,
9 execute, and transmit the Software, and to prepare derivative works of the
10 Software, and to permit third-parties to whom the Software is furnished to
11 do so, all subject to the following:
12 
13 The copyright notices in the Software and this entire statement, including
14 the above license grant, this restriction and the following disclaimer,
15 must be included in all copies of the Software, in whole or in part, and
16 all derivative works of the Software, unless such copies or derivative
17 works are solely in the form of machine-executable object code generated by
18 a source language processor.
19 
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 DEALINGS IN THE SOFTWARE.
27 */
28 
29 /**
30  * Singly linked list
31  *
32  * Copyright: Timur Gafarov 2011-2021.
33  * License: $(LINK2 boost.org/LICENSE_1_0.txt, Boost License 1.0).
34  * Authors: Timur Gafarov, Andrey Penechko, Roman Chistokhodov, ijet
35  */
36 module dlib.container.linkedlist;
37 
38 import dlib.core.memory;
39 
40 /**
41  * Element of single linked list.
42  */
43 struct LinkedListElement(T)
44 {
45     LinkedListElement!(T)* next = null;
46     T datum;
47 
48     this(LinkedListElement!(T)* n)
49     {
50         next = n;
51         datum = T.init;
52     }
53 }
54 
55 /**
56  * GC-free single linked list implementation.
57  */
58 struct LinkedList(T, bool ordered = true)
59 {
60     ///Head of the list.
61     LinkedListElement!(T)* head = null;
62     
63     ///Tail of the list.
64     LinkedListElement!(T)* tail = null;
65     
66     ///Number of elements in the list.
67     size_t length = 0;
68 
69     /**
70      * Check if list has no elements.
71      */
72     @property bool empty()
73     {
74         return length == 0;
75     }
76 
77     ///
78     unittest
79     {
80         LinkedList!int list;
81         assert(list.empty);
82     }
83 
84     /**
85      * Remove all elements and free used memory.
86      */
87     void free()
88     {
89         LinkedListElement!(T)* element = head;
90         while (element !is null)
91         {
92             auto e = element;
93             element = element.next;
94             Delete(e);
95         }
96         head = null;
97         tail = null;
98         length = 0;
99     }
100 
101     /**
102      * Iterating over list via foreach.
103      */
104     int opApply(scope int delegate(size_t, ref T) dg)
105     {
106         int result = 0;
107         uint index = 0;
108 
109         LinkedListElement!(T)* element = head;
110         while (element !is null)
111         {
112             result = dg(index, element.datum);
113             if (result)
114                 break;
115             element = element.next;
116             index++;
117         }
118 
119         return result;
120     }
121 
122     ///
123     unittest
124     {
125         LinkedList!int list;
126         scope(exit) list.free();
127 
128         list.append(1);
129         list.append(2);
130         list.append(3);
131         list.append(4);
132 
133         int[4] values;
134 
135         foreach(size_t i, ref int val; list) {
136             values[i] = val;
137         }
138 
139         assert(values[] == [1,2,3,4]);
140     }
141 
142     /**
143      * Iterating over list via foreach.
144      */
145     int opApply(scope int delegate(ref T) dg)
146     {
147         int result = 0;
148 
149         LinkedListElement!(T)* element = head;
150         while (element !is null)
151         {
152             result = dg(element.datum);
153             if (result)
154                 break;
155             element = element.next;
156         }
157 
158         return result;
159     }
160 
161     ///
162     unittest
163     {
164         LinkedList!int list;
165         scope(exit) list.free();
166 
167         list.append(1);
168         list.append(2);
169         list.append(3);
170         list.append(4);
171 
172         int[] values;
173 
174         foreach(ref int val; list) {
175             values ~= val;
176         }
177 
178         assert(values[] == [1,2,3,4]);
179     }
180 
181     /**
182      * Appen value v to the end.
183      * Returns: Pointer to added list element.
184      */
185     LinkedListElement!(T)* insertBack(T v)
186     {
187         length++;
188 
189         if (tail is null)
190         {
191             tail = New!(LinkedListElement!(T))(null);
192             tail.datum = v;
193         }
194         else
195         {
196             tail.next = New!(LinkedListElement!(T))(null);
197             tail.next.datum = v;
198             tail = tail.next;
199         }
200 
201         if (head is null) head = tail;
202 
203         return tail;
204     }
205 
206     // Insert operator
207     auto opCatAssign(T v)
208     {
209         insertBack(v);
210         return this;
211     }
212 
213     ///
214     unittest
215     {
216         LinkedList!int list;
217         scope(exit) list.free();
218 
219         auto element = list.append(13);
220         assert(element.datum == 13);
221         assert(list.length == 1);
222         element = list.append(42);
223         assert(element.datum == 42);
224         assert(list.length == 2);
225     }
226 
227     /**
228      * Insert value v after element.
229      * Returns: Pointer to inserted element.
230      * Note: element must be not null.
231      */
232     LinkedListElement!(T)* insertAfter(LinkedListElement!(T)* element, T v)
233     {
234         length++;
235         auto newElement = New!(LinkedListElement!(T))(null);
236         newElement.datum = v;
237         newElement.next = element.next;
238         element.next = newElement;
239         if (element is tail) tail = newElement;
240         return newElement;
241     }
242 
243     ///
244     unittest
245     {
246         LinkedList!int list;
247         scope(exit) list.free();
248 
249         auto first = list.append(1);
250         auto last = list.append(2);
251         list.insertAfter(first, 3);
252 
253         assert(list.length == 3);
254         auto arr = list.toArray();
255         assert(arr == [1,3,2]);
256         Delete(arr);
257     }
258 
259     /**
260      * Insert value v at the beginning.
261      */
262     LinkedListElement!(T)* insertFront(T v)
263     {
264         length++;
265         auto newElement = New!(LinkedListElement!(T))(null);
266         newElement.datum = v;
267         newElement.next = head;
268         head = newElement;
269         if (tail is null) {
270             tail = head;
271         }
272         return newElement;
273     }
274 
275     ///
276     unittest
277     {
278         LinkedList!int list;
279         scope(exit) list.free();
280 
281         list.insertBeginning(1);
282         list.insertBack(2);
283         list.insertBeginning(0);
284 
285         import std.algorithm : equal;
286         assert(equal(list.byElement(), [0,1,2]));
287     }
288 
289     /**
290      * Remove value after element.
291      * Note: element must be not null.
292      */
293     void removeAfter(LinkedListElement!(T)* element)
294     {
295         length--;
296         auto obsolete = element.next;
297         if (obsolete !is null)
298         {
299             if (obsolete is tail) tail = element;
300             element.next = obsolete.next;
301             Delete(obsolete);
302         }
303     }
304 
305     ///
306     unittest
307     {
308         LinkedList!int list;
309         scope(exit) list.free();
310 
311         auto first = list.insertBack(1);
312         auto second = list.insertBack(2);
313         auto third = list.insertBack(3);
314         list.removeAfter(first);
315 
316         import std.algorithm : equal;
317         assert(equal(list.byElement(), [1,3]));
318     }
319 
320     /**
321      * Remove the first element.
322      * Note: list must be non-empty.
323      */
324     void removeFront()
325     {
326         length--;
327         auto obsolete = head;
328         if (obsolete !is null)
329         {
330             head = obsolete.next;
331             Delete(obsolete);
332         }
333     }
334 
335     ///
336     unittest
337     {
338         LinkedList!int list;
339         scope(exit) list.free();
340 
341         list.insertBack(0);
342         list.removeFront();
343         assert(list.length == 0);
344 
345         list.insertBack(1);
346         list.insertBack(2);
347         list.insertBack(3);
348         list.removeFront();
349         assert(list.length == 2);
350         import std.algorithm : equal;
351         assert(equal(list.byElement(), [2,3]));
352     }
353 
354     /**
355      * Append other list.
356      * Note: Appended list should not be freed. It becomes part of this list.
357      */
358     void appendList(LinkedList!(T) list)
359     {
360         length += list.length;
361         if (tail !is null) {
362             tail.next = list.head;
363         }
364         if (head is null) {
365             head = list.head;
366         }
367         tail = list.tail;
368     }
369 
370     ///
371     unittest
372     {
373         LinkedList!int list1;
374         scope(exit) list1.free();
375         LinkedList!int list2;
376         LinkedList!int list3;
377 
378         list2.insertBack(1);
379         list2.insertBack(2);
380 
381         list1.appendList(list2);
382 
383         import std.algorithm : equal;
384         assert(equal(list1.byElement(), [1,2]));
385 
386         list3.insertBack(3);
387         list3.insertBack(4);
388         list1.appendList(list3);
389 
390         assert(equal(list1.byElement(), [1,2,3,4]));
391     }
392 
393     /**
394      * Search for element with value v.
395      * Returns: Found element or null if could not find.
396      */
397     LinkedListElement!(T)* find(T v)
398     {
399         LinkedListElement!(T)* element = head;
400         LinkedListElement!(T)* prevElement = head;
401         while (element !is null)
402         {
403             if (element.datum == v)
404             {
405                 static if (!ordered)
406                 {
407                    /*
408                     * Move-to-front heuristic:
409                     * Move an element to the beginning of the list once it is found.
410                     * This scheme ensures that the most recently used items are also
411                     * the quickest to find again.
412                     */
413                     prevElement.next = element.next;
414                     element.next = head;
415                     head = element;
416                 }
417 
418                 return element;
419             }
420 
421             prevElement = element;
422             element = element.next;
423         }
424 
425         return null;
426     }
427 
428     ///
429     unittest
430     {
431         LinkedList!int list;
432         scope(exit) list.free();
433 
434         assert(list.find(42) is null);
435 
436         list.insertBack(13);
437         list.insertBack(42);
438 
439         auto first = list.find(13);
440         assert(first && first.datum == 13);
441 
442         auto second = list.find(42);
443         assert(second && second.datum == 42);
444 
445         assert(list.find(0) is null);
446     }
447 
448     /**
449      * Convert to array.
450      */
451     T[] toArray()
452     {
453         T[] arr = New!(T[])(length);
454         foreach(i, v; this)
455             arr[i] = v;
456         return arr;
457     }
458 
459     ///
460     unittest
461     {
462         LinkedList!int list;
463         scope(exit) list.free();
464 
465         list.insertBack(1);
466         list.insertBack(2);
467         list.insertBack(3);
468 
469         auto arr = list.toArray();
470         assert(arr == [1,2,3]);
471         Delete(arr);
472     }
473 
474     auto byElement()
475     {
476         struct ByElement
477         {
478         private:
479             LinkedListElement!(T)* _first;
480 
481         public:
482             @property bool empty() {
483                 return _first is null;
484             }
485 
486             @property T front() {
487                 return _first.datum;
488             }
489 
490             void popFront() {
491                 _first = _first.next;
492             }
493 
494             auto save() {
495                 return this;
496             }
497         }
498 
499         return ByElement(head);
500     }
501 
502     ///
503     unittest
504     {
505         LinkedList!int list;
506         scope(exit) list.free();
507 
508         assert(list.byElement().empty);
509 
510         list.insertBack(1);
511         list.insertBack(2);
512         list.insertBack(3);
513 
514         auto range = list.byElement();
515         import std.range : isInputRange;
516         import std.algorithm : equal;
517         static assert(isInputRange!(typeof(range)));
518 
519         assert(equal(range, [1,2,3]));
520 
521         range = list.byElement();
522         auto saved = range.save();
523         range.popFront();
524         assert(equal(range,[2,3]));
525         assert(equal(saved,[1,2,3]));
526     }
527 
528     // For backward compatibility
529     alias append = insertBack;
530     alias insertBeginning = insertFront;
531     alias removeBeginning = removeFront;
532     alias search = find;
533 }