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 }