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 }