1 module collections.treemap; 2 3 import std.functional : binaryFun; 4 public import collections.commons; 5 /** 6 * Implements an AVL tree backed treemap. 7 * Intended to used as an alternative to D's own associative array. 8 * If E set to void, then it works more like a regular tree datastructure (as a tree set), and can be indexed with any 9 * type K has an opCmp override. 10 * `nogcIndexing` changes the behavior of `opIndex` if no match is found. If set to true, indexing returns the default 11 * value if no match found, which will need some design consideration. If set to false, indexing throws an exception 12 * if no match found. 13 * Nodes should have the lesser elements on the left side, but this behavior can somewhat changed with alias less. 14 */ 15 public struct TreeMap(K, E, bool nogcIndexing = true, alias less = "a < b") { 16 private struct Node { 17 K key; ///Identifier key, also used for automatic sorting 18 static if (E.stringof != "void") 19 E elem; ///The element stored in this field if exists 20 Node* left; ///The node that holds a key with a lesser value 21 Node* right; ///The node that holds a key with a greater value 22 string toString() const { 23 import std.conv : to; 24 string result = "{K: " ~ to!string(key) ~ " ; "; 25 static if (E.stringof != "void") 26 result ~= "E: " ~ to!string(elem) ~ " ; "; 27 if (left) result ~= "L: " ~ left.toString() ~ " ; "; 28 if (right) result ~= "R: " ~ right.toString() ~ " ; "; 29 return result ~ "}"; 30 } 31 ///Returns the balance of the node. 32 @property sizediff_t balance() @nogc @safe pure nothrow const { 33 sizediff_t result; 34 if(left) result -= left.height; 35 if(right) result += right.height; 36 return result; 37 } 38 ///Returns the height of the node. 39 @property size_t height() @nogc @safe pure nothrow const { 40 const size_t lhs = left ? left.height + 1 : 0; 41 const size_t rhs = right ? right.height + 1 : 0; 42 return lhs >= rhs ? lhs : rhs; 43 } 44 static if (E.stringof != "void"){ 45 /** 46 * Implements a simple left-to-right tree traversal. 47 */ 48 int opApply(scope int delegate(ref E) dg) { 49 if(left !is null) 50 if(left.opApply(dg)) 51 return 1; 52 if(dg(elem)) 53 return 1; 54 if(right !is null) 55 if(right.opApply(dg)) 56 return 1; 57 return 0; 58 } 59 /** 60 * Implements a simple left-to-right tree traversal. 61 */ 62 int opApply(scope int delegate(K, ref E) dg) { 63 if(left !is null) 64 if(left.opApply(dg)) 65 return 1; 66 if(dg(key, elem)) 67 return 1; 68 if(right !is null) 69 if(right.opApply(dg)) 70 return 1; 71 return 0; 72 } 73 /** 74 * Implements a simple right-to-left tree traversal. 75 */ 76 int opApplyReverse(scope int delegate(ref E) dg) { 77 if(right !is null) 78 if(right.opApply(dg)) 79 return 1; 80 if(dg(elem)) 81 return 1; 82 if(left !is null) 83 if(left.opApply(dg)) 84 return 1; 85 return 0; 86 } 87 /** 88 * Implements a simple right-to-left tree traversal. 89 */ 90 int opApplyReverse(scope int delegate(K, ref E) dg) { 91 if(right !is null) 92 if(right.opApply(dg)) 93 return 1; 94 if(dg(key, elem)) 95 return 1; 96 if(left !is null) 97 if(left.opApply(dg)) 98 return 1; 99 return 0; 100 } 101 ///Generates an `opApply` and `opApplyReverse` pair for a TreeMap from potential attributes 102 package static string makeFuncTMNode() { 103 string makeFunc(string attr) { 104 return " 105 int opApply(scope int delegate(ref E) " ~ attr ~ " dg) " ~ attr ~ " { 106 if(left !is null) 107 if(left.opApply(dg)) 108 return 1; 109 if(dg(elem)) 110 return 1; 111 if(right !is null) 112 if(right.opApply(dg)) 113 return 1; 114 return 0; 115 } 116 int opApply(scope int delegate(K, ref E) " ~ attr ~ " dg) " ~ attr ~ " { 117 if(left !is null) 118 if(left.opApply(dg)) 119 return 1; 120 if(dg(key, elem)) 121 return 1; 122 if(right !is null) 123 if(right.opApply(dg)) 124 return 1; 125 return 0; 126 } 127 int opApplyReverse(scope int delegate(ref E) " ~ attr ~ " dg) " ~ attr ~ " { 128 if(right !is null) 129 if(right.opApply(dg)) 130 return 1; 131 if(dg(elem)) 132 return 1; 133 if(left !is null) 134 if(left.opApply(dg)) 135 return 1; 136 return 0; 137 } 138 int opApplyReverse(scope int delegate(K, ref E) " ~ attr ~ " dg) " ~ attr ~ " { 139 if(right !is null) 140 if(right.opApply(dg)) 141 return 1; 142 if(dg(key, elem)) 143 return 1; 144 if(left !is null) 145 if(left.opApply(dg)) 146 return 1; 147 return 0; 148 }"; 149 } 150 string result; 151 foreach (attr; attrList) { 152 result ~= makeFunc(attr); 153 } 154 return result; 155 } 156 mixin(makeFuncTMNode); 157 } else { 158 /** 159 * Implements a simple left-to-right tree traversal. 160 */ 161 int opApply(scope int delegate(K) dg) { 162 if(left !is null) 163 if(left.opApply(dg)) 164 return 1; 165 if(dg(key)) 166 return 1; 167 if(right !is null) 168 if(right.opApply(dg)) 169 return 1; 170 return 0; 171 } 172 /** 173 * Implements a simple right-to-left tree traversal. 174 */ 175 int opApplyReverse(scope int delegate(K) dg) { 176 if(right !is null) 177 if(right.opApply(dg)) 178 return 1; 179 if(dg(key)) 180 return 1; 181 if(left !is null) 182 if(left.opApply(dg)) 183 return 1; 184 return 0; 185 } 186 ///Generates an `opApply` and `opApplyReverse` pair for a TreeSet with the supplied attributes 187 package static string makeFuncTSNode() { 188 string makeFunc(string attr){ 189 return "int opApply(scope int delegate(K) " ~ attr ~ " dg) " ~ attr ~ " { 190 if(left !is null) 191 if(left.opApply(dg)) 192 return 1; 193 if(dg(key)) 194 return 1; 195 if(right !is null) 196 if(right.opApply(dg)) 197 return 1; 198 return 0; 199 } 200 int opApplyReverse(scope int delegate(K) " ~ attr ~ " dg) " ~ attr ~ " { 201 if(right !is null) 202 if(right.opApply(dg)) 203 return 1; 204 if(dg(key)) 205 return 1; 206 if(left !is null) 207 if(left.opApply(dg)) 208 return 1; 209 return 0; 210 }"; 211 } 212 string result; 213 foreach (attr; attrList) { 214 result ~= makeFunc(attr); 215 } 216 return result; 217 } 218 mixin(makeFuncTSNode); 219 } 220 } 221 private size_t nOfElements;///Current number of elements in this collection 222 private Node* root; ///The root element of the tree 223 224 static if (E.stringof != "void") { 225 static if (nogcIndexing) { 226 /** 227 * @nogc capable indexing. 228 * Returns the found element if match found. 229 * Returns E.init if match not found. 230 */ 231 E opIndex(K key) @nogc @safe pure nothrow { 232 Node* crnt = root; 233 while(crnt) { 234 if(binaryFun!less(key, crnt.key)) { //key is smaller than current element's, look at lesser elements 235 crnt = crnt.left; 236 } else if(binaryFun!less(crnt.key, key)) { //key is greater than current element's, look at greater elements 237 crnt = crnt.right; 238 } else { //match found, return element 239 return crnt.elem; 240 241 } 242 } 243 return E.init; 244 } 245 /** 246 * Returns the pointer of the element, or null if key not found. 247 */ 248 E* ptrOf(K key) @nogc @safe pure nothrow { 249 Node* crnt = root; 250 while(crnt) { 251 if(binaryFun!less(key, crnt.key)) { //key is smaller than current element's, look at lesser elements 252 crnt = crnt.left; 253 } else if(binaryFun!less(crnt.key, key)) { //key is greater than current element's, look at greater elements 254 crnt = crnt.right; 255 } else { //match found, return element 256 return &crnt.elem; 257 258 } 259 } 260 return null; 261 } 262 } else { 263 /** 264 * Indexing function that relies on the GC, and throws if no match found 265 * Can be indexed with any type of value as long as K.opCmp supports it. 266 * Returns the found element if match found. 267 */ 268 ref E opIndex(K key) @safe pure { 269 Node* crnt = root; 270 while(crnt) { 271 if(binaryFun!less(key, crnt.key)) { //key is smaller than current element's, look at lesser elements 272 crnt = crnt.left; 273 } else if(binaryFun!less(crnt.key, key)) { //key is greater than current element's, look at greater elements 274 crnt = crnt.right; 275 } else { //match found, return element 276 return crnt.elem; 277 } 278 } 279 throw new ElementNotFoundException("No match found"); 280 } 281 } 282 } else { 283 /** 284 * Creates a treeset from a preexisting range. 285 */ 286 public this(R)(R src) @safe pure nothrow { 287 foreach (key; src) put(key); 288 } 289 /** 290 * Returns true if the element exists within the set, false otherwise. 291 */ 292 public bool has(T)(T key) @nogc @safe pure nothrow { 293 Node* crnt = root; 294 while(crnt) { 295 if(binaryFun!less(key, crnt.key)) { //key is smaller than current element's, look at lesser elements 296 crnt = crnt.left; 297 } else if(binaryFun!less(crnt.key, key)) { //key is greater than current element's, look at greater elements 298 crnt = crnt.right; 299 } else { //match found, return true 300 return true; 301 } 302 } 303 return false; 304 } 305 /** 306 * Returns the amount of elements found in the set. 307 */ 308 public size_t hasRange(R)(R range) @nogc @safe pure nothrow { 309 size_t result; 310 foreach (key; range) { 311 if(has(key)) result++; 312 } 313 return result; 314 } 315 /** 316 * Set operators. 317 * Enables math operations on sets, like unions and intersections. 318 * Could work on ranges in general as long as they implement some basic functions, like iteration. 319 */ 320 TreeMap!(K, E, nogcIndexing, less) opBinary(string op, R)(R rhs) { 321 static if(op == "|" || op == "~") {//Union 322 TreeMap!(K, E, nogcIndexing, less) result; 323 foreach(e ; this) 324 result.put(e); 325 foreach(e ; rhs) 326 result.put(e); 327 return result; 328 } else static if(op == "&" || op == "*") {//Intersection 329 TreeMap!(K, E, nogcIndexing, less) result; 330 foreach(e ; rhs){ 331 if(this.has(e)) result.put(e); 332 } 333 return result; 334 } else static if(op == "-" || op == "/") {//Complement 335 TreeMap!(K, E, nogcIndexing, less) result; 336 foreach(e ; this) 337 result.put(e); 338 foreach(e ; rhs){ 339 result.removeByElem(e); 340 } 341 return result; 342 } else static if(op == "^"){//Difference 343 TreeMap!(K, E, nogcIndexing, less) result = this | rhs; 344 TreeMap!(K, E, nogcIndexing, less) common = this & rhs; 345 foreach(e ; common){ 346 result.removeByElem(e); 347 } 348 return result; 349 } else static assert(0, "Operator " ~ op ~ "not supported"); 350 } 351 /** 352 * Set operators. 353 */ 354 TreeMap!(K, E, nogcIndexing, less) opOpAssign(string op)(K value) { 355 static if(op == "~=") {//Append 356 put(value); 357 } else static if(op == "-=" || op == "/=") { 358 removeByElem(value); 359 } else static assert(0, "Operator " ~ op ~ "not supported"); 360 return this; 361 } 362 /** 363 * Set operators. 364 */ 365 TreeMap!(K, E, nogcIndexing, less) opOpAssign(string op, R)(R range) { 366 static if(op == "~=" || op == "|=") {//Append 367 foreach(val; range) 368 put(val); 369 } else static if(op == "-=" || op == "/=") { 370 foreach(val; range) 371 removeByElem(val); 372 } else static assert(0, "Operator " ~ op ~ "not supported"); 373 return this; 374 } 375 static if (nogcIndexing) { 376 /** 377 * @nogc capable indexing. 378 * Can be indexed with any type of value as long as K.opCmp supports it and `alias less` hasn't been changed. 379 * Returns the found element if match found. 380 * Returns E.init if match not found. 381 */ 382 K opIndex(T)(T key) @nogc @safe pure nothrow { 383 Node* crnt = root; 384 while(crnt) { 385 if(binaryFun!less(key, crnt.key)) { //key is smaller than current element's, look at lesser elements 386 crnt = crnt.left; 387 } else if(binaryFun!less(crnt.key, key)) { //key is greater than current element's, look at greater elements 388 crnt = crnt.right; 389 } else { //match found, return element 390 return crnt.key; 391 } 392 } 393 return K.init; 394 } 395 396 } else { 397 /** 398 * Indexing function that relies on the GC, and throws if no match found 399 * Can be indexed with any type of value as long as K.opCmp supports it and `alias less` hasn't been changed. 400 * Returns the found element if match found. 401 */ 402 K opIndex(T)(T key) @safe pure { 403 Node* crnt = root; 404 while(crnt) { 405 if(binaryFun!less(key, crnt.key)) { //key is smaller than current element's, look at lesser elements 406 crnt = crnt.left; 407 } else if(binaryFun!less(crnt.key, key)) { //key is greater than current element's, look at greater elements 408 crnt = crnt.right; 409 } else { //match found, return element 410 return crnt.key; 411 } 412 } 413 throw new ElementNotFoundException("No match found"); 414 } 415 } 416 } 417 static if (E.stringof != "void"){ 418 /** 419 * Assigns a value to the given key. 420 * If key found, the value will be overwritten without node insertion. 421 * If key isn't found, a new node will be inserted. 422 */ 423 auto opIndexAssign(E elem, K key) @safe pure nothrow { 424 if(!root){ //Best case scenario: root is empty 425 nOfElements++; 426 root = new Node(key, elem, null, null); 427 return elem; 428 } 429 Node* crnt = root; 430 while(crnt) { 431 if(binaryFun!less(key, crnt.key)) { //Key is smaller, look at left hand side 432 if(crnt.left is null) { 433 crnt.left = new Node(key, elem, null, null); 434 crnt = null; 435 nOfElements++; 436 } 437 else crnt = crnt.left; 438 } else if(binaryFun!less(crnt.key, key)) { //Key is greater, look ay right hand side 439 if(crnt.right is null) { 440 crnt.right = new Node(key, elem, null, null); 441 crnt = null; 442 nOfElements++; 443 } 444 else crnt = crnt.right; 445 } else { //Another best case scenario: a keymatch is found 446 crnt.elem = elem; 447 crnt = null; 448 } 449 } 450 rebalance(); 451 return elem; 452 } 453 /** 454 * Removes an item by key. 455 * Returns the removed item if found, or E.init if not. 456 */ 457 public E remove(K key) @safe pure nothrow { 458 import core.memory : GC; 459 Node* crnt = root, prev; 460 while(crnt !is null) { 461 if(binaryFun!less(key, crnt.key)) { //Key has a lesser value, search on the left. 462 prev = crnt; 463 crnt = crnt.left; 464 } else if(binaryFun!less(crnt.key, key)) { //Key has a greater value, search on the right 465 prev = crnt; 466 crnt = crnt.right; 467 } else { //Keymatch must have been found 468 E result = crnt.elem; 469 //dispose of the node properly if needed 470 if(prev !is null) { 471 if(crnt.left && crnt.right) { //Worst case scenario: find the smallest node on the right hand side 472 Node* temp = findMin(crnt.right); 473 remove(temp.key); 474 crnt.key = temp.key; 475 crnt.elem = temp.elem; 476 return result; 477 } else if(!crnt.left && crnt.right) { 478 if(binaryFun!less(key, prev.key)) { //The node was on the left side of the previous one 479 prev.left = crnt.right; 480 } else { 481 prev.right = crnt.right; 482 } 483 } else if(crnt.left && !crnt.right) { 484 if(binaryFun!less(key, prev.key)) { //The node was on the left side of the previous one 485 prev.left = crnt.left; 486 } else { 487 prev.right = crnt.left; 488 } 489 } else { //Best case scenario: there are no child nodes, just dereference from prev 490 if(binaryFun!less(key, prev.key)) { //The node was on the left side of the previous one 491 prev.left = null; 492 } else { 493 prev.right = null; 494 } 495 } 496 } else {//must be root element 497 if(crnt.left && crnt.right) { //Worst case scenario: find the smallest node on the right hand side 498 Node* temp = findMin(crnt.right); 499 remove(temp.key); 500 crnt.key = temp.key; 501 crnt.elem = temp.elem; 502 return result; 503 } else if(!crnt.left && crnt.right) { 504 root = crnt.right; 505 } else if(crnt.left && !crnt.right) { 506 root = crnt.left; 507 } else { //Best case scenario: there are no child nodes, just dereference from root 508 root = null; 509 } 510 } 511 nOfElements--; 512 rebalance(); 513 return result; 514 } 515 } 516 return E.init; 517 } 518 } else { 519 /** 520 * Puts an element into the treeset 521 */ 522 public K put(K key) @safe pure nothrow { 523 if(!root){ //Best case scenario: root is empty 524 nOfElements++; 525 root = new Node(key, null, null); 526 return key; 527 } 528 Node* crnt = root; 529 while(crnt) { 530 if(binaryFun!less(key, crnt.key)) { //Key is smaller, look at left hand side 531 if(crnt.left is null) { 532 crnt.left = new Node(key, null, null); 533 crnt = null; 534 nOfElements++; 535 } 536 else crnt = crnt.left; 537 } else if(binaryFun!less(crnt.key, key)) { //Key is greater, look ay right hand side 538 if(crnt.right is null) { 539 crnt.right = new Node(key, null, null); 540 crnt = null; 541 nOfElements++; 542 } 543 else crnt = crnt.right; 544 } else { //Kaymatch found 545 crnt.key = key; 546 crnt = null; 547 } 548 } 549 rebalance(); 550 return key; 551 } 552 /** 553 * Removes an item by key. 554 * Returns the removed item if found, or K.init if not. 555 */ 556 public K removeByElem(K key) @safe pure nothrow { 557 import core.memory : GC; 558 Node* crnt = root, prev; 559 while(crnt !is null) { 560 if(binaryFun!less(key,crnt.key)) { //Key has a lesser value, search on the left. 561 prev = crnt; 562 crnt = crnt.left; 563 } else if(binaryFun!less(crnt.key, key)) { //Key has a greater value, search on the right 564 prev = crnt; 565 crnt = crnt.right; 566 } else { //Key must have been found 567 K result = crnt.key; 568 //dispose of the node properly if needed 569 if(prev !is null) { 570 if(crnt.left && crnt.right) { //Worst case scenario: find the smallest node on the right hand side 571 Node* temp = findMin(crnt.right); 572 remove(temp.key); 573 crnt.key = temp.key; 574 return result; 575 } else if(!crnt.left && crnt.right) { 576 if(binaryFun!less(key, prev.key)) { //The node was on the left side of the previous one 577 prev.left = crnt.right; 578 } else { 579 prev.right = crnt.right; 580 } 581 } else if(crnt.left && !crnt.right) { 582 if(binaryFun!less(key, prev.key)) { //The node was on the left side of the previous one 583 prev.left = crnt.left; 584 } else { 585 prev.right = crnt.left; 586 } 587 } else { //Best case scenario: there are no child nodes, just dereference from prev 588 if(binaryFun!less(key, prev.key)) { //The node was on the left side of the previous one 589 prev.left = null; 590 } else { 591 prev.right = null; 592 } 593 } 594 } else {//must be root element 595 if(crnt.left && crnt.right) { //Worst case scenario: find the smallest node on the right hand side 596 Node* temp = findMin(crnt.right); 597 remove(temp.key); 598 crnt.key = temp.key; 599 return result; 600 } else if(!crnt.left && crnt.right) { 601 root = crnt.right; 602 } else if(crnt.left && !crnt.right) { 603 root = crnt.left; 604 } else { //Best case scenario: there are no child nodes, just dereference from root 605 root = null; 606 } 607 } 608 nOfElements--; 609 rebalance(); 610 return result; 611 } 612 } 613 return K.init; 614 } 615 alias remove = removeByElem; 616 } 617 /** 618 * Returns the smallest node 619 */ 620 private Node* findMin(Node* currentNode) @nogc @safe pure nothrow { 621 while(currentNode.left !is null){ 622 currentNode = currentNode.left; 623 } 624 return currentNode; 625 } 626 /** 627 * Rebalances the tree. 628 */ 629 public void rebalance() @nogc @safe pure nothrow { 630 void rebalanceLocal(ref Node* node) @nogc @safe pure nothrow { 631 if(node.balance >= 2) { //Right hand imbalance 632 if(node.right.balance > 0) { 633 rotateLeft(node); 634 } else if(node.right.balance < 0) { 635 rotateLeftRight(node); 636 } 637 } else if(node.balance <= -2) { //Left hand imbalance 638 if(node.left.balance < 0) { 639 rotateRight(node); 640 } else if(node.left.balance > 0) { 641 rotateRightLeft(node); 642 } 643 } 644 if(node.left) rebalanceLocal(node.left); 645 if(node.right) rebalanceLocal(node.right); 646 } 647 if(root !is null) 648 rebalanceLocal(root); 649 } 650 static if (E.stringof != "void"){ 651 /** 652 * Implements a simple left-to-right tree traversal. 653 */ 654 int opApply(scope int delegate(ref E) dg) { 655 if(root !is null) return root.opApply(dg); 656 else return 0; 657 } 658 /** 659 * Implements a simple left-to-right tree traversal. 660 */ 661 int opApply(scope int delegate(K, ref E) dg) { 662 if(root !is null) return root.opApply(dg); 663 else return 0; 664 } 665 /** 666 * Implements a simple right-to-left tree traversal. 667 */ 668 int opApplyReverse(scope int delegate(ref E) dg) { 669 if(root !is null) return root.opApplyReverse(dg); 670 else return 0; 671 } 672 /** 673 * Implements a simple right-to-left tree traversal. 674 */ 675 int opApplyReverse(scope int delegate(K, ref E) dg) { 676 if(root !is null) return root.opApplyReverse(dg); 677 else return 0; 678 } 679 ///Generates an `opApply` and `opApplyReverse` pair for a TreeMap with the supplied attributes 680 package static string makeFuncTM() { 681 string makeFunc(string attr) { 682 return "int opApply(scope int delegate(ref E) " ~ attr ~ " dg) " ~ attr ~ " { 683 if(root !is null) return root.opApply(dg); 684 else return 0; 685 } 686 int opApply(scope int delegate(K, ref E) " ~ attr ~ " dg) " ~ attr ~ " { 687 if(root !is null) return root.opApply(dg); 688 else return 0; 689 } 690 int opApplyReverse(scope int delegate(ref E) " ~ attr ~ " dg) " ~ attr ~ " { 691 if(root !is null) return root.opApplyReverse(dg); 692 else return 0; 693 } 694 int opApplyReverse(scope int delegate(K, ref E) " ~ attr ~ " dg) " ~ attr ~ " { 695 if(root !is null) return root.opApplyReverse(dg); 696 else return 0; 697 }"; 698 } 699 string result; 700 foreach (attr; attrList) { 701 result ~= makeFunc(attr); 702 } 703 return result; 704 } 705 mixin(makeFuncTM); 706 } else { 707 /** 708 * Implements a simple left-to-right tree traversal by depth. 709 */ 710 int opApply(scope int delegate(K) dg) { 711 if(root !is null) return root.opApply(dg); 712 else return 0; 713 } 714 /** 715 * Implements a simple right-to-left tree traversal. 716 */ 717 int opApplyReverse(scope int delegate(K) dg) { 718 if(root !is null) return root.opApplyReverse(dg); 719 else return 0; 720 } 721 /** 722 * Returns an array representation of the set. 723 */ 724 @property K[] arrayOf() { 725 K[] result; 726 result.reserve(nOfElements); 727 int putToResult(K elem) @safe pure nothrow { 728 result ~= elem; 729 return 0; 730 } 731 root.opApply(&putToResult); 732 return result; 733 } 734 ///Generates an `opApply` and `opApplyReverse` pair for a TreeMap with the supplied attributes 735 package static string makeFuncTS() { 736 string makeFunc(string attr) { 737 return "int opApply(scope int delegate(K) " ~ attr ~ " dg) " ~ attr ~ " { 738 if(root !is null) return root.opApply(dg); 739 else return 0; 740 } 741 int opApplyReverse(scope int delegate(K) " ~ attr ~ " dg) " ~ attr ~ " { 742 if(root !is null) return root.opApplyReverse(dg); 743 else return 0; 744 }"; 745 } 746 string result; 747 foreach (attr; attrList) { 748 result ~= makeFunc(attr); 749 } 750 return result; 751 } 752 mixin(makeFuncTS); 753 } 754 /** 755 * Tree rotation for rebalancing. 756 * Rotates the node to the left. 757 */ 758 private void rotateLeft(ref Node* node) @nogc @safe pure nothrow { 759 Node* temp = node.right; 760 node.right = temp.left; 761 temp.left = node; 762 node = temp; 763 } 764 /** 765 * Tree rotation for rebalancing. 766 * Rotates the node to the left. 767 */ 768 private void rotateRight(ref Node* node) @nogc @safe pure nothrow { 769 Node* temp = node.left; 770 node.left = temp.right; 771 temp.right = node; 772 node = temp; 773 } 774 /** 775 * Tree rotation for rebalancing. 776 * Rotates the node's right to the left, then the node to the right. 777 */ 778 private void rotateRightLeft(ref Node* node) @nogc @safe pure nothrow { 779 rotateLeft(node.left); 780 rotateRight(node); 781 } 782 /** 783 * Tree rotation for rebalancing. 784 * Rotates the node's left to the right, then the node to the left. 785 */ 786 private void rotateLeftRight(ref Node* node) @nogc @safe pure nothrow { 787 rotateRight(node.right); 788 rotateLeft(node); 789 } 790 /** 791 * Returns the number of currently held elements within the tree. 792 */ 793 public @property size_t length() @nogc @safe pure nothrow const { 794 return nOfElements; 795 } 796 /** 797 * Returns the key of the n-th element. 798 * NOT WORKING! 799 */ 800 /+public K keyOf(size_t n) { 801 static if(E.stringof != "void"){ 802 foreach (key, elem; root) { 803 if (n == 0) { 804 return key; 805 } else n--; 806 } 807 } else { 808 foreach (key; root) { 809 if (n == 0) { 810 return key; 811 } else n--; 812 } 813 } 814 return K.init; 815 }+/ 816 /** 817 * Returns the string representation of the tree. 818 */ 819 public string toString() const { 820 if(root !is null) 821 return root.toString; 822 else 823 return "Empty"; 824 } 825 } 826 827 unittest { 828 import std.stdio : writeln, write; 829 import std.random : uniform; 830 import std.exception : assertThrown; 831 { 832 alias IntMap = TreeMap!(int, int, true); 833 IntMap test0, test1, test2, test3; 834 for(int i ; i < 1024 ; i++)//Stress test to see if large number of elements would cause any issues 835 test0[uniform(0, 65536)] = i; 836 foreach(k, e; test0){ 837 838 } 839 foreach_reverse(k, e; test0){ 840 841 } 842 for(int i ; i < 16 ; i++) 843 test1[uniform(0, 65536)] = i; 844 //writeln(test1.toString); 845 for(int i ; i < 32 ; i++) 846 test2[uniform(0, 65536)] = i; 847 //writeln(test2.toString); 848 for(int i ; i < 64 ; i++) 849 test3[i] = i; 850 for(int i ; i < 64 ; i++) 851 write(test3[i],";"); 852 writeln(); 853 for(int i ; i < 16 ; i++) 854 test3.remove(uniform(0,64)); 855 foreach(i ; test3) 856 write(i,";"); 857 writeln(); 858 } 859 { 860 alias IntMap = TreeMap!(int, int, false); 861 IntMap test0, test1; 862 for(int i ; i < 64 ; i++) 863 test0[i] = i; 864 assert(test0.length == 64, "TreeMap length mismatch"); 865 assertThrown!ElementNotFoundException(test0[420]); 866 assertThrown!ElementNotFoundException(test0[666]); 867 for(int i ; i < 64 ; i++) 868 test0.remove(i); 869 assert(test0.length == 0, "Treemap item removal failure"); 870 for(int i ; i < 16 ; i++) { 871 test1[i] = i; 872 writeln(test1.toString); 873 } 874 } 875 { 876 alias IntMap = TreeMap!(int, void, true); 877 IntMap test0; 878 for(int i ; i < 64 ; i++) { 879 test0.put(i); 880 writeln(test0.toString()); 881 } 882 assert(test0.length == 64, "TreeMap length mismatch"); 883 for(int i ; i < 64 ; i++) { 884 test0.remove(i); 885 writeln(test0.toString()); 886 } 887 assert(test0.length == 0, "Treemap item removal failure"); 888 } 889 { 890 alias IntMap = TreeMap!(int, void, false); 891 IntMap test0; 892 for(int i ; i < 64 ; i++) { 893 test0.put(i); 894 writeln(test0.toString()); 895 } 896 assert(test0.length == 64, "TreeMap length mismatch"); 897 assertThrown!ElementNotFoundException(test0[420]); 898 assertThrown!ElementNotFoundException(test0[666]); 899 for(int i ; i < 64 ; i++) { 900 test0.remove(i); 901 writeln(test0.toString()); 902 } 903 assert(test0.length == 0, "Treemap item removal failure"); 904 } 905 { //test set operators 906 alias IntSet = TreeMap!(int, void, true); 907 IntSet a = IntSet([1, 3, 5, 7, 9]), b = IntSet([1, 5, 9]), c = IntSet([3, 7]); 908 IntSet union_ab = a | b, union_ac = a | c, union_bc = b | c; 909 IntSet intrsctn_ab = a & b, intrsctn_ac = a & c; 910 IntSet cmplmnt_ab = a - b, cmplmnt_ac = a - c; 911 IntSet diff_ab = a ^ b, diff_ac = a ^ c, diff_bc = b ^ c; 912 assert(union_ab.hasRange([1, 3, 5, 7, 9]) == 5); 913 assert(union_ac.hasRange([1, 3, 5, 7, 9]) == 5); 914 assert(union_bc.hasRange([1, 3, 5, 7, 9]) == 5); 915 assert(intrsctn_ab.hasRange([1, 5, 9]) == 3); 916 assert(intrsctn_ac.hasRange([3, 7]) == 2); 917 assert(cmplmnt_ab.hasRange([3, 7]) == 2); 918 assert(cmplmnt_ac.hasRange([1, 5, 9]) == 3); 919 assert(diff_ab.hasRange([3, 7]) == 2); 920 assert(diff_ac.hasRange([1, 5, 9]) == 3); 921 assert(diff_bc.hasRange([1, 3, 5, 7, 9]) == 5); 922 } 923 } 924 @safe pure unittest { 925 alias IntMap = TreeMap!(int, int, false); 926 IntMap test; 927 test[5] = 5; 928 test[7] = 7; 929 test[3] = 3; 930 foreach(elem, key; test) { 931 assert(elem == key); 932 } 933 }