1 module prova.util.linkedlist;
2 
3 import std.typecons;
4 import std.exception;
5 
6 /**
7  * Linked list node
8  */
9 class Node(T)
10 {
11   private LinkedList!T list;
12   private Node!T next;
13   private Node!T last;
14   private Nullable!T _value;
15 
16   private this(LinkedList!T list, T value) {
17     this.list = list;
18     this.value = value;
19   }
20 
21   /**
22    * Retrieves the stored value
23    */
24   @property T value()
25   {
26     return _value;
27   }
28 
29   /**
30    * Modifies the stored value
31    */
32   @property void value(T value)
33   {
34     _value = value;
35   }
36 
37   /**
38    * Gets the node previous in order
39    */
40   Node!T getLast()
41   {
42     return last;
43   }
44 
45   /**
46    * Gets the node next in order
47    */
48   Node!T getNext()
49   {
50     return next;
51   }
52 
53   /**
54    * Detaches the node from the list
55    */
56   void remove()
57   {
58     if(last)
59       last.next = next;
60 
61     if(next)
62       next.last = last;
63 
64     if(list.last == this)
65       list.last = null;
66 
67     if(list.first == this)
68       list.first = null;
69 
70     list.count--;
71   }
72 }
73 
74 /**
75  * LinkedList template
76  */
77 class LinkedList(T)
78 {
79   private Node!T first;
80   private Node!T last;
81   private int count = 0;
82 
83   /**
84    * Returns the node count
85    */
86   @property int length()
87   {
88     return count;
89   }
90 
91   ///
92   Node!T getFirstNode()
93   {
94     return first;
95   }
96 
97   ///
98   Node!T getLastNode()
99   {
100     return last;
101   }
102 
103   /**
104    * Creates a node and places it at the start of the list
105    */
106   Node!T insertFront(T value)
107   {
108     auto node = new Node!T(this, value);
109 
110     if(first) {
111       first.last = node;
112       node.next = first;
113       first = node;
114     } else {
115       first = last = node;
116     }
117 
118     return node;
119   }
120 
121   /**
122    * Creates a node and places it at the end of the list
123    */
124   Node!T insertBack(T value)
125   {
126     auto node = new Node!T(this, value);
127 
128     if(last) {
129       last.next = node;
130       node.last = last;
131       last = node;
132     } else {
133       first = last = node;
134     }
135 
136     return node;
137   }
138 
139   /**
140    * Creates a node in the list placed before the reference node
141    */
142   Node!T insertBefore(T value, Node!T referenceNode)
143   {
144     if(!referenceNode)
145       return insertFront(value);
146 
147     auto node = new Node!T(this, value);
148 
149     assert(referenceNode.list == this, "Reference node should belong to this list");
150 
151     node.next = referenceNode;
152     node.last = referenceNode.last;
153 
154     if(referenceNode.last)
155       referenceNode.last.next = node;
156 
157     referenceNode.last = node;
158 
159     count++;
160     return node;
161   }
162 
163   /**
164    * Creates a node in the list placed after the reference node
165    */
166   Node!T insertAfter(T value, Node!T referenceNode)
167   {
168     if(!referenceNode)
169       return insertBack(value);
170 
171     assert(referenceNode.list == this, "Reference node should belong to this list");
172 
173     auto node = new Node!T(this, value);
174 
175     node.last = referenceNode;
176     node.next = referenceNode.next;
177 
178     if(referenceNode.next)
179       referenceNode.next.last = node;
180 
181     referenceNode.next = node;
182 
183     count++;
184     return node;
185   }
186 
187   /**
188    * Tests if the list contains a node with the specified value
189    */
190   bool contains(T value)
191   {
192     foreach(Node!T node; this)
193       if(node.value == value)
194         return true;
195 
196     return false;
197   }
198 
199   /**
200    * Removes the first node with the specified value
201    */
202   void remove(T value)
203   {
204     foreach(Node!T node; this) {
205       if(node.value == value) {
206         node.remove();
207         break;
208       }
209     }
210   }
211 
212   /**
213    * Empties the LinkedList
214    */
215   void clear()
216   {
217     first = null;
218     last = null;
219     count = 0;
220   }
221 
222   ///
223   T[] toArray()
224   {
225     T[] array;
226     array.reserve(length);
227 
228     foreach(T value; this) {
229       array ~= value;
230     }
231 
232     return array;
233   }
234 
235   ///
236   LinkedListRange!T toRange()
237   {
238     return LinkedListRange!T(this);
239   }
240 
241   ///
242   int opApply(int delegate(Node!T result) dg)
243   {
244     int result = 0;
245 
246     for(Node!T node = first; node; node = node.next) {
247       result = dg(node);
248 
249       if(result)
250         break;
251     }
252 
253     return result;
254   }
255 
256   ///
257   int opApply(int delegate(ref T result) dg)
258   {
259     int result = 0;
260 
261     for(Node!T node = first; node; node = node.next) {
262       T value = node.value;
263       result = dg(value);
264       node.value = value;
265 
266       if(result)
267         break;
268     }
269 
270     return result;
271   }
272 }
273 
274 ///
275 struct LinkedListRange(T)
276 {
277   Node!T node;
278 
279   private this(LinkedList!T list)
280   {
281     this.node = list.getFirstNode();
282   }
283 
284   ///
285   T front()
286   {
287     return node.value;
288   }
289 
290   ///
291   void popFront()
292   {
293     node = node.getNext();
294   }
295 
296   ///
297   bool empty()
298   {
299     return node is null;
300   }
301 }