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