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 					nOfElements--;
236 					return result;
237 				}
238 				crnt = &(*crnt).next;
239 			}
240 			return E.init;
241 		}
242 		/**
243 		 * Inserts an element at the end of the list.
244 		 */
245 		public E put(E value) @safe pure nothrow {
246 			Node** crnt = &root;
247 			while (*crnt) {
248 				if(binaryFun!equal((*crnt).elem, value)) {
249 					(*crnt).elem = value;
250 					return value;
251 				}
252 				crnt = &(*crnt).next;
253 			}
254 			nOfElements++;
255 			end++;
256 			*crnt = new Node(value, null);
257 			return value;
258 		}
259 		/**
260 		 * Returns true if the element is found within the set.
261 		 */
262 		public bool has(E value) @nogc @safe pure nothrow {
263 			Node* crnt = root;
264 			while (crnt) {
265 				if (binaryFun!equal(crnt.elem, value)) return true;
266 				crnt = crnt.next;
267 			}
268 			return false;
269 		}
270 		/**
271 		 * Returns the index where the element can be found, or throws an ElementNotFoundException if not found.
272 		 */
273 		public size_t which(E value) @safe pure {
274 			Node* crnt = root;
275 			size_t result;
276 			while (crnt) {
277 				if (binaryFun!equal(crnt.elem, value)) return result;
278 				crnt = crnt.next;
279 				result++;
280 			}
281 			throw new ElementNotFoundException("Element not found!");
282 		}
283 		/**
284 		 * Returns the amount of elements found in the set.
285 		 */
286 		public size_t hasRange(R)(R range) @nogc @safe pure nothrow {
287 			size_t result;
288 			foreach (key; range) {
289 				if(has(key)) result++;
290 			}
291 			return result;
292 		}
293 		/**
294 		 * Set operators.
295 		 * Enables math operations on sets, like unions and intersections.
296 		 * Could work on ranges in general as long as they implement some basic functions, like iteration.
297 		 */
298 		LinkedList!(E, allowDuplicates, equal) opBinary(string op, R)(R rhs) {
299 			static if(op == "|" || op == "~") {//Union
300 				LinkedList!(E, allowDuplicates, equal) result;
301 				for(size_t i; i < nOfElements ; i++) 
302 					result.put(opIndex(i));
303 				foreach(e ; rhs)
304 					result.put(e);
305 				return result;
306 			} else static if(op == "&" || op == "*") {//Intersection
307 				LinkedList!(E, allowDuplicates, equal) result;
308 				foreach(e ; rhs){
309 					if(this.has(e)) result.put(e);
310 				}
311 				return result;
312 			} else static if(op == "-" || op == "/") {//Complement
313 				LinkedList!(E, allowDuplicates, equal) result = LinkedList!(E, allowDuplicates, equal)(this);
314 				foreach(e ; rhs){
315 					result.removeByElem(e);
316 				}
317 				return result;
318 			} else static if(op == "^"){//Difference
319 				LinkedList!(E, allowDuplicates, equal) result = this | rhs;
320 				LinkedList!(E, allowDuplicates, equal) common = this & rhs;
321 				foreach(e ; common){
322 					result.removeByElem(e);
323 				}
324 				return result;
325 			} else static assert(0, "Operator " ~ op ~ "not supported");
326 		}
327 		/**
328 		 * Set operators.
329 		 */
330 		LinkedList!(E, allowDuplicates, equal) opOpAssign(string op)(E value) {
331 			static if(op == "~=") {//Append
332 				put(value);
333 			} else static if(op == "-=" || op == "/=") {
334 				removeByElem(value);
335 			} else static assert(0, "Operator " ~ op ~ "not supported");
336 			return this;
337 		}
338 		/**
339 		 * Set operators.
340 		 */
341 		LinkedList!(K, E, less) opOpAssign(string op, R)(R range) {
342 			static if(op == "~=" || op == "|=") {//Append
343 				foreach(val; range)
344 					put(val);
345 			} else static if(op == "-=" || op == "/=") {
346 				foreach(val; range)
347 					removeByElem(val);
348 			} else static assert(0, "Operator " ~ op ~ "not supported");
349 			return this;
350 		}
351 	}
352 	/**
353 	 * Returns the element at the front.
354 	 */
355 	@property E front() @nogc @safe nothrow pure {
356 		return opIndex(begin);
357 	}
358 	/**
359 	 * Returns the element at the back.
360 	 */
361 	@property E back() @nogc @safe nothrow pure {
362 		return opIndex(end - 1);
363 	}
364 	/**
365 	 * Returns the element at begin and increments the position by one.
366 	 */
367 	E moveFront() @nogc @safe nothrow pure {
368 		E result = opIndex(begin);
369 		popFront();
370 		return result;
371 	}
372 	/**
373 	 * Increments the front iteration position by one
374 	 */
375 	void popFront() @nogc @safe nothrow pure {
376 		if(begin < end) begin++;
377 	}
378 	/**
379 	 * Decrements the back iteration position by one
380 	 */
381 	void popBack() @nogc @safe nothrow pure {
382 		if(begin < end) end--;
383 	}
384 	/**
385 	 * Returns true when the end of the list have been reached.
386 	 */
387 	@property bool empty() @nogc @safe nothrow pure {
388 		return begin == end;
389 	}
390 	/**
391 	 * Returns the elements of the list copied into an array.
392 	 */
393 	@property E[] arrayOf() @safe nothrow pure {
394 		E[] result;
395 		result.reserve(nOfElements);
396 		Node* crnt = root;
397 		while (crnt) {
398 			result ~= crnt.elem;
399 			crnt = crnt.next;
400 		}
401 		return result;
402 	}
403 	/**
404 	 * Returns a copy of this struct.
405 	 */
406 	@property auto save() @nogc @safe nothrow pure {
407 		return this;
408 	}
409 	/**
410 	 * Moves to the n-th position and returns the element of that position.
411 	 */
412 	E moveAt(size_t n) @nogc @safe nothrow pure {
413 		begin = n;
414 		return opIndex(n);
415 	}
416 }
417 
418 unittest {
419 	alias LinkedNumList = LinkedList!(int);
420 	LinkedNumList lnl;
421 	lnl.put(5);
422 	lnl.put(8);
423 	assert(lnl.arrayOf == [5, 8], lnl.toString);
424 	lnl.put(11);
425 	lnl.put(9);
426 	assert(lnl.arrayOf == [5, 8, 11, 9], lnl.toString);
427 	assert(lnl.length == 4);
428 	lnl.insertAt(10, 1);
429 	assert(lnl.length == 5);
430 	assert(lnl.arrayOf == [5, 10, 8, 11, 9], lnl.toString);
431 	lnl.remove(1);
432 	assert(lnl.arrayOf == [5, 8, 11, 9], lnl.toString);
433 	assert(lnl.length == 4);
434 	lnl.swap(1,3);
435 	assert(lnl.arrayOf == [5, 9, 11, 8], lnl.toString);
436 	lnl.setAsFirst(2);
437 	assert(lnl.arrayOf == [11, 5, 9, 8], lnl.toString);
438 	lnl.remove(2);
439 	assert(lnl.arrayOf == [11, 5, 8], lnl.toString);
440 	assert(lnl.length == 3);
441 	lnl ~= [8, 6, 4, 2, 9];
442 	assert(lnl.arrayOf == [11, 5, 8, 8, 6, 4, 2, 9], lnl.toString);
443 	assert(lnl[2..6].arrayOf == [8, 8, 6, 4], lnl[2..6].toString);
444 }
445 
446 unittest {
447 	import std.exception;
448 	alias LinkedNumSet = LinkedList!(int, false);
449 	LinkedNumSet sa = LinkedNumSet([-1,5,9,3]), sb = LinkedNumSet([-1,6,6,6,8,10]);
450 	assert(sa.length == 4);
451 	assert(sa.arrayOf == [-1,5,9,3], sa.toString);
452 	assert(sb.length == 4);
453 	assert(sb.arrayOf == [-1,6,8,10], sa.toString);
454 	assert(sa.has(-1));
455 	assert(sa.which(-1) == 0);
456 	assertThrown!ElementNotFoundException(sa.which(-2));
457 	assert(sb.has(-1));
458 	assert(!sb.has(0));
459 	LinkedNumSet sc = sa | sb, sd = sa & sb, se = sa ^ sb;
460 	assert(sc.length == 7, sc.toString());
461 	assert(sc.has(-1), sc.toString());
462 	assert(sc.has(3), sc.toString());
463 	assert(sc.has(5), sc.toString());
464 	assert(sc.has(6), sc.toString());
465 	assert(sc.has(8), sc.toString());
466 	assert(sc.has(9), sc.toString());
467 	assert(sc.has(10), sc.toString());
468 	assert(sd.has(-1), sd.toString());
469 	assert(!se.has(-1), se.toString());
470 	sa.removeByElem(-1);
471 	assert(sa.length == 3);
472 	assert(!sa.has(-1));
473 }
474 
475 unittest {	//test set operators
476 	alias IntSet = LinkedList!(int, false);
477 	IntSet a = IntSet([1, 3, 5, 7, 9]), b = IntSet([1, 5, 9]), c = IntSet([3, 7]);
478 	IntSet union_ab = a | b, union_ac = a | c, union_bc = b | c;
479 	IntSet intrsctn_ab = a & b, intrsctn_ac = a & c;
480 	IntSet cmplmnt_ab = a - b, cmplmnt_ac = a - c;
481 	IntSet diff_ab = a ^ b, diff_ac = a ^ c, diff_bc = b ^ c;
482 	assert(union_ab.hasRange([1, 3, 5, 7, 9]) == 5);
483 	assert(union_ac.hasRange([1, 3, 5, 7, 9]) == 5);
484 	assert(union_bc.hasRange([1, 3, 5, 7, 9]) == 5);
485 	assert(intrsctn_ab.hasRange([1, 5, 9]) == 3);
486 	assert(intrsctn_ac.hasRange([3, 7]) == 2);
487 	assert(cmplmnt_ab.hasRange([3, 7]) == 2);
488 	assert(cmplmnt_ac.hasRange([1, 5, 9]) == 3);
489 	assert(diff_ab.hasRange([3, 7]) == 2);
490 	assert(diff_ac.hasRange([1, 5, 9]) == 3);
491 	assert(diff_bc.hasRange([1, 3, 5, 7, 9]) == 5);
492 }