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 }