1 module collections.linkedlist;
2 
3 import collections.commons;
4 import std.functional : binaryFun;
5 /**
6  * Simple linked list implementation.
7  * Has very good insertion speed and deletion speeds, but mediocre access.
8  */
9 public struct LinkedList(E, bool allowDuplicates = true, alias equal = "a == b") {
10 	private struct Node {
11 		E               elem;   ///The element hold in this node.
12 		Node*           next;   ///Points to the next element, null if endpoint.
13 		
14 		string toString() const {
15 			import std.conv : to;
16 			string result = to!string(elem);
17 			if (next !is null) result ~= " ; " ~ next.toString();
18 			return result;
19 		}
20 	}
21 	private size_t		nOfElements;///Current number of elements in this collection
22 	private Node*		root;		///The root element of the list
23 	private size_t		begin;		///Front position for foreachable range
24 	private size_t		end;		///Back position for foreachable range
25 	/**
26 	 * Creates a linked list from a compatible range.
27 	 */
28 	public this(R)(R range) @safe pure {
29 		foreach(E elem ; range) {
30 			put(elem);
31 		}
32 	}
33 	/**
34 	 * Returns the number of currently held elements within the list.
35 	 */
36 	public @property size_t length() @nogc @safe pure nothrow const {
37 		return nOfElements;
38 	}
39 	alias opDollar = length;
40 	/**
41 	 * Returns the string representation of the list.
42 	 */
43 	public string toString() const {
44 		if(root !is null)
45 			return "[" ~ root.toString ~ "]";
46 		else
47 			return "Empty";
48 	}
49 	/**
50 	 * Creates a slice from the list.
51 	 */
52 	public LinkedList!(E, allowDuplicates, equal) opSlice(size_t start, size_t end) @safe pure nothrow {
53 		assert(end > start, "Out of bounds error!");
54 		assert(nOfElements > start, "Out of bounds error!");
55 		assert(nOfElements >= end, "Out of bounds error!");
56 		LinkedList!(E, allowDuplicates, equal) result;
57 		for( ; start < end ; start++) result.put(opIndex(start));
58 		return result;
59 	}
60 	/**
61 	 * Sets a given element to the top.
62 	 */
63 	public E setAsFirst(size_t index) @nogc @safe pure nothrow {
64 		assert(nOfElements > index, "Out of bounds error!");
65 		if(index) insertNode(0, removeNode(index));
66 		return root.elem;
67 	}
68 	/+/**
69 	 * Returns the pointer of a given Node.
70 	 */
71 	private Node* getPtr(size_t index) @nogc @safe pure nothrow {
72 		if(index) return root.getPtr(--index);
73 		else return root;
74 	}+/
75 	private Node* removeNode(size_t index) @nogc @safe pure nothrow {
76 		Node** crnt = &root;
77 		while(*crnt) {
78 			if(!index) {
79 				Node* backup = *crnt;
80 				*crnt = (*crnt).next;
81 				backup.next = null;
82 				return backup;
83 			}
84 			crnt = &(*crnt).next;
85 			index--;
86 		}
87 		return null;
88 	}
89 	private void insertNode(size_t index, Node* n) @nogc @safe pure nothrow {
90 		Node** crnt = &root;
91 		while(*crnt) {
92 			if(!index) {
93 				n.next = *crnt;
94 				*crnt = n;
95 				return;
96 			}
97 			crnt = &(*crnt).next;
98 			index--;
99 		}
100 		*crnt = n;
101 	}
102 	/**
103 	 * Swaps two elements.
104 	 */
105 	public void swap(size_t i0, size_t i1) @nogc @safe pure nothrow {
106 		assert(nOfElements > i0, "Out of bounds error!");
107 		assert(nOfElements > i1, "Out of bounds error!");
108 		if(i0 > i1) {
109 			const size_t temp = i0;
110 			i0 = i1;
111 			i1 = temp;
112 		}
113 		Node* n0 = removeNode(i0), n1 = removeNode(i1 - 1);
114 		insertNode(i0, n1);
115 		insertNode(i1, n0);
116 		version (unittest) {
117 			size_t count;
118 			Node* crnt = root;
119 			while (crnt) {
120 				count++;
121 				crnt = crnt.next;
122 			}
123 			assert(count == nOfElements, "Lenght mismatch error!");
124 		}
125 	}
126 	/**
127 	 * Removes the given index of the list.
128 	 * Return the value held at the given position
129 	 */
130 	public E remove(size_t index) @safe pure nothrow {
131 		assert(nOfElements > index, "Out of bounds error!");
132 		nOfElements--;
133 		end--;
134 		return removeNode(index).elem;
135 	}
136 	static if(allowDuplicates) {
137 		/**
138 		 * Returns the element at the given index.
139 		 * Will cause segfault if indexed out of bounds.
140 		 */
141 		ref E opIndex(size_t index) @nogc @safe pure nothrow {
142 			assert(index < nOfElements, "Out of bounds error!");
143 			Node* crnt = root;
144 			while (index) {
145 				crnt = crnt.next;
146 				index--;
147 			}
148 			return crnt.elem;
149 		}
150 		/**
151 		 * Assigns an element to the index.
152 		 */	
153 		public E opIndexAssign(E value, size_t index) @nogc @safe pure nothrow {
154 			assert(index < nOfElements, "Out of bounds error!");
155 			Node* crnt = root;
156 			while (index) {
157 				crnt = crnt.next;
158 				index--;
159 			}
160 			return crnt.elem = value;
161 		}
162 		/**
163 		 * Inserts an element at the given index.
164 		 */
165 		public E insertAt(E value, size_t index) @safe pure nothrow {
166 			assert(index <= nOfElements, "Out of bounds error!");
167 			insertNode(index, new Node(value, null));
168 			nOfElements++;
169 			end++;
170 			return value;
171 			//return E.init;
172 		}
173 		/**
174 		 * Inserts an element at the end of the list.
175 		 */
176 		public E insertAtEnd(E value) @safe pure nothrow {
177 			return insertAt(value, nOfElements);
178 		}
179 		alias put = insertAtEnd;
180 		/**
181 		 * Range operators.
182 		 */
183 		LinkedList!(E, allowDuplicates, equal) opOpAssign(string op)(E value) {
184 			static if(op == "~") {//Append
185 				put(value);
186 			} else static assert(0, "Operator " ~ op ~ "not supported");
187 			return this;
188 		}
189 		/**
190 		 * Range operators.
191 		 */
192 		LinkedList!(E, allowDuplicates, equal) opOpAssign(string op, R)(R range) {
193 			static if(op == "~") {//Append
194 				foreach(E value ; range)
195 					put(value);
196 			} else static assert(0, "Operator " ~ op ~ " not supported");
197 			return this;
198 		}
199 		/**
200 		 * Returns the element at the front.
201 		 */
202 		@property ref E frontRef() @nogc @safe nothrow pure {
203 			return opIndex(begin);
204 		}
205 		/**
206 		 * Returns the element at the back.
207 		 */
208 		@property ref E backRef() @nogc @safe nothrow pure {
209 			return opIndex(end - 1);
210 		}
211 	} else {
212 		/**
213 		 * Returns the element at the given index.
214 		 * Will cause segfault if indexed out of bounds.
215 		 */
216 		E opIndex(size_t index) @nogc @safe pure nothrow {
217 			assert(index < nOfElements, "Out of bounds error!");
218 			Node* crnt = root;
219 			while (index) {
220 				crnt = crnt.next;
221 				index--;
222 			}
223 			return crnt.elem;
224 		}
225 		/**
226 		 * Removes an index if the value is found.
227 		 * Returns the original if found, or E.init if not.
228 		 */
229 		public E removeByElem(E value) @safe pure nothrow {
230 			Node** crnt = &root;
231 			while(*crnt) {
232 				if (binaryFun!equal((*crnt).elem, value)) {
233 					E result = (*crnt).elem;
234 					*crnt = (*crnt).next;
235 					return result;
236 				}
237 				crnt = &(*crnt).next;
238 			}
239 			return E.init;
240 		}
241 		/**
242 		 * Inserts an element at the end of the list.
243 		 */
244 		public E put(E value) @safe pure nothrow {
245 			Node** crnt = &root;
246 			while (*crnt) {
247 				if(binaryFun!equal((*crnt).elem, value)) {
248 					(*crnt).elem = value;
249 					return value;
250 				}
251 				crnt = &(*crnt).next;
252 			}
253 			nOfElements++;
254 			end++;
255 			*crnt = new Node(value, null);
256 			return value;
257 		}
258 		/**
259 		 * Returns true if the element is found within the set.
260 		 */
261 		public bool has(E value) @nogc @safe pure nothrow {
262 			Node* crnt = root;
263 			while (crnt) {
264 				if (binaryFun!equal(crnt.elem, value)) return true;
265 				crnt = crnt.next;
266 			}
267 			return false;
268 		}
269 		/**
270 		 * Returns the index where the element can be found, or throws an ElementNotFoundException if not found.
271 		 */
272 		public size_t which(E value) @safe pure {
273 			Node* crnt = root;
274 			size_t result;
275 			while (crnt) {
276 				if (binaryFun!equal(crnt.elem, value)) return result;
277 				crnt = crnt.next;
278 				result++;
279 			}
280 			throw new ElementNotFoundException("Element not found!");
281 		}
282 		/**
283 		 * Returns the amount of elements found in the set.
284 		 */
285 		public size_t hasRange(R)(R range) @nogc @safe pure nothrow {
286 			size_t result;
287 			foreach (key; range) {
288 				if(has(key)) result++;
289 			}
290 			return result;
291 		}
292 		/**
293 		 * Set operators.
294 		 * Enables math operations on sets, like unions and intersections.
295 		 * Could work on ranges in general as long as they implement some basic functions, like iteration.
296 		 */
297 		LinkedList!(E, allowDuplicates, equal) opBinary(string op, R)(R rhs) {
298 			static if(op == "|" || op == "~") {//Union
299 				LinkedList!(E, allowDuplicates, equal) result;
300 				for(size_t i; i < nOfElements ; i++) 
301 					result.put(opIndex(i));
302 				foreach(e ; rhs)
303 					result.put(e);
304 				return result;
305 			} else static if(op == "&" || op == "*") {//Intersection
306 				LinkedList!(E, allowDuplicates, equal) result;
307 				foreach(e ; rhs){
308 					if(this.has(e)) result.put(e);
309 				}
310 				return result;
311 			} else static if(op == "-" || op == "/") {//Complement
312 				LinkedList!(E, allowDuplicates, equal) result = LinkedList!(E, allowDuplicates, equal)(this);
313 				foreach(e ; rhs){
314 					result.removeByElem(e);
315 				}
316 				return result;
317 			} else static if(op == "^"){//Difference
318 				LinkedList!(E, allowDuplicates, equal) result = this | rhs;
319 				LinkedList!(E, allowDuplicates, equal) common = this & rhs;
320 				foreach(e ; common){
321 					result.removeByElem(e);
322 				}
323 				return result;
324 			} else static assert(0, "Operator " ~ op ~ "not supported");
325 		}
326 		/**
327 		 * Set operators.
328 		 */
329 		LinkedList!(E, allowDuplicates, equal) opOpAssign(string op)(E value) {
330 			static if(op == "~=") {//Append
331 				put(value);
332 			} else static if(op == "-=" || op == "/=") {
333 				removeByElem(value);
334 			} else static assert(0, "Operator " ~ op ~ "not supported");
335 			return this;
336 		}
337 		/**
338 		 * Set operators.
339 		 */
340 		LinkedList!(K, E, less) opOpAssign(string op, R)(R range) {
341 			static if(op == "~=" || op == "|=") {//Append
342 				foreach(val; range)
343 					put(val);
344 			} else static if(op == "-=" || op == "/=") {
345 				foreach(val; range)
346 					removeByElem(val);
347 			} else static assert(0, "Operator " ~ op ~ "not supported");
348 			return this;
349 		}
350 	}
351 	/**
352 	 * Returns the element at the front.
353 	 */
354 	@property E front() @nogc @safe nothrow pure {
355 		return opIndex(begin);
356 	}
357 	/**
358 	 * Returns the element at the back.
359 	 */
360 	@property E back() @nogc @safe nothrow pure {
361 		return opIndex(end - 1);
362 	}
363 	/**
364 	 * Returns the element at begin and increments the position by one.
365 	 */
366 	E moveFront() @nogc @safe nothrow pure {
367 		E result = opIndex(begin);
368 		popFront();
369 		return result;
370 	}
371 	/**
372 	 * Increments the front iteration position by one
373 	 */
374 	void popFront() @nogc @safe nothrow pure {
375 		if(begin < end) begin++;
376 	}
377 	/**
378 	 * Decrements the back iteration position by one
379 	 */
380 	void popBack() @nogc @safe nothrow pure {
381 		if(begin < end) end--;
382 	}
383 	/**
384 	 * Returns true when the end of the list have been reached.
385 	 */
386 	@property bool empty() @nogc @safe nothrow pure {
387 		return begin == end;
388 	}
389 	/**
390 	 * Returns the elements of the list copied into an array.
391 	 */
392 	@property E[] arrayOf() @safe nothrow pure {
393 		E[] result;
394 		result.reserve(nOfElements);
395 		Node* crnt = root;
396 		while (crnt) {
397 			result ~= crnt.elem;
398 			crnt = crnt.next;
399 		}
400 		return result;
401 	}
402 	/**
403 	 * Returns a copy of this struct.
404 	 */
405 	@property auto save() @nogc @safe nothrow pure {
406 		return this;
407 	}
408 	/**
409 	 * Moves to the n-th position and returns the element of that position.
410 	 */
411 	E moveAt(size_t n) @nogc @safe nothrow pure {
412 		begin = n;
413 		return opIndex(n);
414 	}
415 }
416 
417 unittest {
418 	alias LinkedNumList = LinkedList!(int);
419 	LinkedNumList lnl;
420 	lnl.put(5);
421 	lnl.put(8);
422 	assert(lnl.arrayOf == [5, 8], lnl.toString);
423 	lnl.put(11);
424 	lnl.put(9);
425 	assert(lnl.arrayOf == [5, 8, 11, 9], lnl.toString);
426 	assert(lnl.length == 4);
427 	lnl.insertAt(10, 1);
428 	assert(lnl.length == 5);
429 	assert(lnl.arrayOf == [5, 10, 8, 11, 9], lnl.toString);
430 	lnl.remove(1);
431 	assert(lnl.arrayOf == [5, 8, 11, 9], lnl.toString);
432 	assert(lnl.length == 4);
433 	lnl.swap(1,3);
434 	assert(lnl.arrayOf == [5, 9, 11, 8], lnl.toString);
435 	lnl.setAsFirst(2);
436 	assert(lnl.arrayOf == [11, 5, 9, 8], lnl.toString);
437 	lnl.remove(2);
438 	assert(lnl.arrayOf == [11, 5, 8], lnl.toString);
439 	assert(lnl.length == 3);
440 	lnl ~= [8, 6, 4, 2, 9];
441 	assert(lnl.arrayOf == [11, 5, 8, 8, 6, 4, 2, 9], lnl.toString);
442 	assert(lnl[2..6].arrayOf == [8, 8, 6, 4], lnl[2..6].toString);
443 }
444 
445 unittest {
446 	import std.exception;
447 	alias LinkedNumSet = LinkedList!(int, false);
448 	LinkedNumSet sa = LinkedNumSet([-1,5,9,3]), sb = LinkedNumSet([-1,6,6,6,8,10]);
449 	assert(sa.length == 4);
450 	assert(sa.arrayOf == [-1,5,9,3], sa.toString);
451 	assert(sb.length == 4);
452 	assert(sb.arrayOf == [-1,6,8,10], sa.toString);
453 	assert(sa.has(-1));
454 	assert(sa.which(-1) == 0);
455 	assertThrown!ElementNotFoundException(sa.which(-2));
456 	assert(sb.has(-1));
457 	assert(!sb.has(0));
458 	LinkedNumSet sc = sa | sb, sd = sa & sb, se = sa ^ sb;
459 	assert(sc.length == 7, sc.toString());
460 	assert(sc.has(-1), sc.toString());
461 	assert(sc.has(3), sc.toString());
462 	assert(sc.has(5), sc.toString());
463 	assert(sc.has(6), sc.toString());
464 	assert(sc.has(8), sc.toString());
465 	assert(sc.has(9), sc.toString());
466 	assert(sc.has(10), sc.toString());
467 	assert(sd.has(-1), sd.toString());
468 	assert(!se.has(-1), se.toString());
469 }
470 
471 unittest {	//test set operators
472 	alias IntSet = LinkedList!(int, false);
473 	IntSet a = IntSet([1, 3, 5, 7, 9]), b = IntSet([1, 5, 9]), c = IntSet([3, 7]);
474 	IntSet union_ab = a | b, union_ac = a | c, union_bc = b | c;
475 	IntSet intrsctn_ab = a & b, intrsctn_ac = a & c;
476 	IntSet cmplmnt_ab = a - b, cmplmnt_ac = a - c;
477 	IntSet diff_ab = a ^ b, diff_ac = a ^ c, diff_bc = b ^ c;
478 	assert(union_ab.hasRange([1, 3, 5, 7, 9]) == 5);
479 	assert(union_ac.hasRange([1, 3, 5, 7, 9]) == 5);
480 	assert(union_bc.hasRange([1, 3, 5, 7, 9]) == 5);
481 	assert(intrsctn_ab.hasRange([1, 5, 9]) == 3);
482 	assert(intrsctn_ac.hasRange([3, 7]) == 2);
483 	assert(cmplmnt_ab.hasRange([3, 7]) == 2);
484 	assert(cmplmnt_ac.hasRange([1, 5, 9]) == 3);
485 	assert(diff_ab.hasRange([3, 7]) == 2);
486 	assert(diff_ac.hasRange([1, 5, 9]) == 3);
487 	assert(diff_bc.hasRange([1, 3, 5, 7, 9]) == 5);
488 }