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 (typeid(E) !is typeid(void))//(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 (typeid(E) !is typeid(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 (typeid(E) !is typeid(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(const 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(const 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 return null; 260 } 261 /** 262 * Returns true if the treemap has the key. 263 */ 264 bool has(const K key) @nogc @safe pure nothrow { 265 return ptrOf(key) !is null; 266 } 267 auto opBinaryRight(string op, K)(const K key) @nogc @safe pure nothrow { 268 static if (op == "in") { 269 return has(key); 270 } else static assert(0, "Operator not supported!"); 271 } 272 } else { 273 /** 274 * Indexing function that relies on the GC, and throws if no match found 275 * Can be indexed with any type of value as long as K.opCmp supports it. 276 * Returns the found element if match found. 277 */ 278 ref E opIndex(K key) @safe pure { 279 Node* crnt = root; 280 while(crnt) { 281 if(binaryFun!less(key, crnt.key)) { //key is smaller than current element's, look at lesser elements 282 crnt = crnt.left; 283 } else if(binaryFun!less(crnt.key, key)) { //key is greater than current element's, look at greater elements 284 crnt = crnt.right; 285 } else { //match found, return element 286 return crnt.elem; 287 } 288 } 289 throw new ElementNotFoundException("No match found"); 290 } 291 /** 292 * Returns true if the treemap has the key. 293 */ 294 bool has(const K key) @nogc @safe pure nothrow { 295 Node* crnt = root; 296 while(crnt) { 297 if(binaryFun!less(key, crnt.key)) { //key is smaller than current element's, look at lesser elements 298 crnt = crnt.left; 299 } else if(binaryFun!less(crnt.key, key)) { //key is greater than current element's, look at greater elements 300 crnt = crnt.right; 301 } else { //match found, return element 302 return true; 303 } 304 } 305 return false; 306 } 307 auto opBinaryRight(string op, K)(const K key) @nogc @safe pure nothrow { 308 static if (op == "in") { 309 return has(key); 310 } else static assert(0, "Operator not supported!"); 311 } 312 } 313 } else { 314 /** 315 * Creates a treeset from a preexisting range. 316 */ 317 public this(R)(R src) @safe pure nothrow { 318 foreach (key; src) put(key); 319 } 320 /** 321 * Returns true if the element exists within the set, false otherwise. 322 */ 323 public bool has(T)(const T key) @nogc @safe pure nothrow { 324 Node* crnt = root; 325 while(crnt) { 326 if(binaryFun!less(key, crnt.key)) { //key is smaller than current element's, look at lesser elements 327 crnt = crnt.left; 328 } else if(binaryFun!less(crnt.key, key)) { //key is greater than current element's, look at greater elements 329 crnt = crnt.right; 330 } else { //match found, return true 331 return true; 332 } 333 } 334 return false; 335 } 336 auto opBinaryRight(string op, T)(const T key) { 337 static if (op == "in") { 338 return has(key); 339 } else static assert(0, "Operator not supported!"); 340 } 341 /** 342 * Returns the amount of elements found in the set. 343 */ 344 public size_t hasRange(R)(R range) @nogc @safe pure nothrow { 345 size_t result; 346 foreach (key; range) { 347 if(has(key)) result++; 348 } 349 return result; 350 } 351 /** 352 * Set operators. 353 * Enables math operations on sets, like unions and intersections. 354 * Could work on ranges in general as long as they implement some basic functions, like iteration. 355 */ 356 TreeMap!(K, E, nogcIndexing, less) opBinary(string op, R)(R rhs) { 357 static if(op == "|" || op == "~") {//Union 358 TreeMap!(K, E, nogcIndexing, less) result; 359 foreach(e ; this) 360 result.put(e); 361 foreach(e ; rhs) 362 result.put(e); 363 return result; 364 } else static if(op == "&" || op == "*") {//Intersection 365 TreeMap!(K, E, nogcIndexing, less) result; 366 foreach(e ; rhs){ 367 if(this.has(e)) result.put(e); 368 } 369 return result; 370 } else static if(op == "-" || op == "/") {//Complement 371 TreeMap!(K, E, nogcIndexing, less) result; 372 foreach(e ; this) 373 result.put(e); 374 foreach(e ; rhs){ 375 result.removeByElem(e); 376 } 377 return result; 378 } else static if(op == "^"){//Difference 379 TreeMap!(K, E, nogcIndexing, less) result = this | rhs; 380 TreeMap!(K, E, nogcIndexing, less) common = this & rhs; 381 foreach(e ; common){ 382 result.removeByElem(e); 383 } 384 return result; 385 } else static assert(0, "Operator " ~ op ~ "not supported"); 386 } 387 /** 388 * Set operators. 389 */ 390 TreeMap!(K, E, nogcIndexing, less) opOpAssign(string op)(K value) { 391 static if(op == "~=") {//Append 392 put(value); 393 } else static if(op == "-=" || op == "/=") { 394 removeByElem(value); 395 } else static assert(0, "Operator " ~ op ~ "not supported"); 396 return this; 397 } 398 /** 399 * Set operators. 400 */ 401 TreeMap!(K, E, nogcIndexing, less) opOpAssign(string op, R)(R range) { 402 static if(op == "~=" || op == "|=") {//Append 403 foreach(val; range) 404 put(val); 405 } else static if(op == "-=" || op == "/=") { 406 foreach(val; range) 407 removeByElem(val); 408 } else static assert(0, "Operator " ~ op ~ "not supported"); 409 return this; 410 } 411 static if (nogcIndexing) { 412 /** 413 * @nogc capable indexing. 414 * Can be indexed with any type of value as long as K.opCmp supports it and `alias less` hasn't been changed. 415 * Returns the found element if match found. 416 * Returns E.init if match not found. 417 */ 418 K opIndex(T)(T key) @nogc @safe pure nothrow { 419 Node* crnt = root; 420 while(crnt) { 421 if(binaryFun!less(key, crnt.key)) { //key is smaller than current element's, look at lesser elements 422 crnt = crnt.left; 423 } else if(binaryFun!less(crnt.key, key)) { //key is greater than current element's, look at greater elements 424 crnt = crnt.right; 425 } else { //match found, return element 426 return crnt.key; 427 } 428 } 429 return K.init; 430 } 431 432 } else { 433 /** 434 * Indexing function that relies on the GC, and throws if no match found 435 * Can be indexed with any type of value as long as K.opCmp supports it and `alias less` hasn't been changed. 436 * Returns the found element if match found. 437 */ 438 K opIndex(T)(T key) @safe pure { 439 Node* crnt = root; 440 while(crnt) { 441 if(binaryFun!less(key, crnt.key)) { //key is smaller than current element's, look at lesser elements 442 crnt = crnt.left; 443 } else if(binaryFun!less(crnt.key, key)) { //key is greater than current element's, look at greater elements 444 crnt = crnt.right; 445 } else { //match found, return element 446 return crnt.key; 447 } 448 } 449 throw new ElementNotFoundException("No match found"); 450 } 451 } 452 } 453 static if (E.stringof != "void"){ 454 /** 455 * Assigns a value to the given key. 456 * If key found, the value will be overwritten without node insertion. 457 * If key isn't found, a new node will be inserted. 458 */ 459 auto opIndexAssign(E elem, K key) @safe pure nothrow { 460 if(!root){ //Best case scenario: root is empty 461 nOfElements++; 462 root = new Node(key, elem, null, null); 463 return elem; 464 } 465 Node* crnt = root; 466 while(crnt) { 467 if(binaryFun!less(key, crnt.key)) { //Key is smaller, look at left hand side 468 if(crnt.left is null) { 469 crnt.left = new Node(key, elem, null, null); 470 crnt = null; 471 nOfElements++; 472 } 473 else crnt = crnt.left; 474 } else if(binaryFun!less(crnt.key, key)) { //Key is greater, look ay right hand side 475 if(crnt.right is null) { 476 crnt.right = new Node(key, elem, null, null); 477 crnt = null; 478 nOfElements++; 479 } 480 else crnt = crnt.right; 481 } else { //Another best case scenario: a keymatch is found 482 crnt.elem = elem; 483 crnt = null; 484 } 485 } 486 rebalance(); 487 return elem; 488 } 489 /** 490 * Removes an item by key. 491 * Returns the removed item if found, or E.init if not. 492 */ 493 public E remove(K key) @safe pure nothrow { 494 import core.memory : GC; 495 Node* crnt = root, prev; 496 while(crnt !is null) { 497 if(binaryFun!less(key, crnt.key)) { //Key has a lesser value, search on the left. 498 prev = crnt; 499 crnt = crnt.left; 500 } else if(binaryFun!less(crnt.key, key)) { //Key has a greater value, search on the right 501 prev = crnt; 502 crnt = crnt.right; 503 } else { //Keymatch must have been found 504 E result = crnt.elem; 505 //dispose of the node properly if needed 506 if(prev !is null) { 507 if(crnt.left && crnt.right) { //Worst case scenario: find the smallest node on the right hand side 508 Node* temp = findMin(crnt.right); 509 remove(temp.key); 510 crnt.key = temp.key; 511 crnt.elem = temp.elem; 512 return result; 513 } else if(!crnt.left && crnt.right) { 514 if(binaryFun!less(key, prev.key)) { //The node was on the left side of the previous one 515 prev.left = crnt.right; 516 } else { 517 prev.right = crnt.right; 518 } 519 } else if(crnt.left && !crnt.right) { 520 if(binaryFun!less(key, prev.key)) { //The node was on the left side of the previous one 521 prev.left = crnt.left; 522 } else { 523 prev.right = crnt.left; 524 } 525 } else { //Best case scenario: there are no child nodes, just dereference from prev 526 if(binaryFun!less(key, prev.key)) { //The node was on the left side of the previous one 527 prev.left = null; 528 } else { 529 prev.right = null; 530 } 531 } 532 } else {//must be root element 533 if(crnt.left && crnt.right) { //Worst case scenario: find the smallest node on the right hand side 534 Node* temp = findMin(crnt.right); 535 remove(temp.key); 536 crnt.key = temp.key; 537 crnt.elem = temp.elem; 538 return result; 539 } else if(!crnt.left && crnt.right) { 540 root = crnt.right; 541 } else if(crnt.left && !crnt.right) { 542 root = crnt.left; 543 } else { //Best case scenario: there are no child nodes, just dereference from root 544 root = null; 545 } 546 } 547 nOfElements--; 548 rebalance(); 549 return result; 550 } 551 } 552 return E.init; 553 } 554 } else { 555 /** 556 * Puts an element into the treeset 557 */ 558 public K put(K key) @safe pure nothrow { 559 if(!root){ //Best case scenario: root is empty 560 nOfElements++; 561 root = new Node(key, null, null); 562 return key; 563 } 564 Node* crnt = root; 565 while(crnt) { 566 if(binaryFun!less(key, crnt.key)) { //Key is smaller, look at left hand side 567 if(crnt.left is null) { 568 crnt.left = new Node(key, null, null); 569 crnt = null; 570 nOfElements++; 571 } 572 else crnt = crnt.left; 573 } else if(binaryFun!less(crnt.key, key)) { //Key is greater, look ay right hand side 574 if(crnt.right is null) { 575 crnt.right = new Node(key, null, null); 576 crnt = null; 577 nOfElements++; 578 } 579 else crnt = crnt.right; 580 } else { //Kaymatch found 581 crnt.key = key; 582 crnt = null; 583 } 584 } 585 rebalance(); 586 return key; 587 } 588 /** 589 * Removes an item by key. 590 * Returns the removed item if found, or K.init if not. 591 */ 592 public K removeByElem(K key) @safe pure nothrow { 593 import core.memory : GC; 594 Node* crnt = root, prev; 595 while(crnt !is null) { 596 if(binaryFun!less(key,crnt.key)) { //Key has a lesser value, search on the left. 597 prev = crnt; 598 crnt = crnt.left; 599 } else if(binaryFun!less(crnt.key, key)) { //Key has a greater value, search on the right 600 prev = crnt; 601 crnt = crnt.right; 602 } else { //Key must have been found 603 K result = crnt.key; 604 //dispose of the node properly if needed 605 if(prev !is null) { 606 if(crnt.left && crnt.right) { //Worst case scenario: find the smallest node on the right hand side 607 Node* temp = findMin(crnt.right); 608 remove(temp.key); 609 crnt.key = temp.key; 610 return result; 611 } else if(!crnt.left && crnt.right) { 612 if(binaryFun!less(key, prev.key)) { //The node was on the left side of the previous one 613 prev.left = crnt.right; 614 } else { 615 prev.right = crnt.right; 616 } 617 } else if(crnt.left && !crnt.right) { 618 if(binaryFun!less(key, prev.key)) { //The node was on the left side of the previous one 619 prev.left = crnt.left; 620 } else { 621 prev.right = crnt.left; 622 } 623 } else { //Best case scenario: there are no child nodes, just dereference from prev 624 if(binaryFun!less(key, prev.key)) { //The node was on the left side of the previous one 625 prev.left = null; 626 } else { 627 prev.right = null; 628 } 629 } 630 } else {//must be root element 631 if(crnt.left && crnt.right) { //Worst case scenario: find the smallest node on the right hand side 632 Node* temp = findMin(crnt.right); 633 remove(temp.key); 634 crnt.key = temp.key; 635 return result; 636 } else if(!crnt.left && crnt.right) { 637 root = crnt.right; 638 } else if(crnt.left && !crnt.right) { 639 root = crnt.left; 640 } else { //Best case scenario: there are no child nodes, just dereference from root 641 root = null; 642 } 643 } 644 nOfElements--; 645 rebalance(); 646 return result; 647 } 648 } 649 return K.init; 650 } 651 alias remove = removeByElem; 652 } 653 /** 654 * Returns the smallest node 655 */ 656 private Node* findMin(Node* currentNode) @nogc @safe pure nothrow { 657 while(currentNode.left !is null){ 658 currentNode = currentNode.left; 659 } 660 return currentNode; 661 } 662 /** 663 * Rebalances the tree. 664 */ 665 public void rebalance() @nogc @safe pure nothrow { 666 void rebalanceLocal(ref Node* node) @nogc @safe pure nothrow { 667 if(node.balance >= 2) { //Right hand imbalance 668 if(node.right.balance > 0) { 669 rotateLeft(node); 670 } else if(node.right.balance < 0) { 671 rotateLeftRight(node); 672 } 673 } else if(node.balance <= -2) { //Left hand imbalance 674 if(node.left.balance < 0) { 675 rotateRight(node); 676 } else if(node.left.balance > 0) { 677 rotateRightLeft(node); 678 } 679 } 680 if(node.left) rebalanceLocal(node.left); 681 if(node.right) rebalanceLocal(node.right); 682 } 683 if(root !is null) 684 rebalanceLocal(root); 685 } 686 static if (E.stringof != "void"){ 687 /** 688 * Implements a simple left-to-right tree traversal. 689 */ 690 int opApply(scope int delegate(ref E) dg) { 691 if(root !is null) return root.opApply(dg); 692 else return 0; 693 } 694 /** 695 * Implements a simple left-to-right tree traversal. 696 */ 697 int opApply(scope int delegate(K, ref E) dg) { 698 if(root !is null) return root.opApply(dg); 699 else return 0; 700 } 701 /** 702 * Implements a simple right-to-left tree traversal. 703 */ 704 int opApplyReverse(scope int delegate(ref E) dg) { 705 if(root !is null) return root.opApplyReverse(dg); 706 else return 0; 707 } 708 /** 709 * Implements a simple right-to-left tree traversal. 710 */ 711 int opApplyReverse(scope int delegate(K, ref E) dg) { 712 if(root !is null) return root.opApplyReverse(dg); 713 else return 0; 714 } 715 ///Generates an `opApply` and `opApplyReverse` pair for a TreeMap with the supplied attributes 716 package static string makeFuncTM() { 717 string makeFunc(string attr) { 718 return "int opApply(scope int delegate(ref E) " ~ attr ~ " dg) " ~ attr ~ " { 719 if(root !is null) return root.opApply(dg); 720 else return 0; 721 } 722 int opApply(scope int delegate(K, ref E) " ~ attr ~ " dg) " ~ attr ~ " { 723 if(root !is null) return root.opApply(dg); 724 else return 0; 725 } 726 int opApplyReverse(scope int delegate(ref E) " ~ attr ~ " dg) " ~ attr ~ " { 727 if(root !is null) return root.opApplyReverse(dg); 728 else return 0; 729 } 730 int opApplyReverse(scope int delegate(K, ref E) " ~ attr ~ " dg) " ~ attr ~ " { 731 if(root !is null) return root.opApplyReverse(dg); 732 else return 0; 733 }"; 734 } 735 string result; 736 foreach (attr; attrList) { 737 result ~= makeFunc(attr); 738 } 739 return result; 740 } 741 mixin(makeFuncTM); 742 } else { 743 /** 744 * Implements a simple left-to-right tree traversal by depth. 745 */ 746 int opApply(scope int delegate(K) dg) { 747 if(root !is null) return root.opApply(dg); 748 else return 0; 749 } 750 /** 751 * Implements a simple right-to-left tree traversal. 752 */ 753 int opApplyReverse(scope int delegate(K) dg) { 754 if(root !is null) return root.opApplyReverse(dg); 755 else return 0; 756 } 757 /** 758 * Returns an array representation of the set. 759 */ 760 @property K[] arrayOf() { 761 K[] result; 762 result.reserve(nOfElements); 763 int putToResult(K elem) @safe pure nothrow { 764 result ~= elem; 765 return 0; 766 } 767 root.opApply(&putToResult); 768 return result; 769 } 770 ///Generates an `opApply` and `opApplyReverse` pair for a TreeMap with the supplied attributes 771 package static string makeFuncTS() { 772 string makeFunc(string attr) { 773 return "int opApply(scope int delegate(K) " ~ attr ~ " dg) " ~ attr ~ " { 774 if(root !is null) return root.opApply(dg); 775 else return 0; 776 } 777 int opApplyReverse(scope int delegate(K) " ~ attr ~ " dg) " ~ attr ~ " { 778 if(root !is null) return root.opApplyReverse(dg); 779 else return 0; 780 }"; 781 } 782 string result; 783 foreach (attr; attrList) { 784 result ~= makeFunc(attr); 785 } 786 return result; 787 } 788 mixin(makeFuncTS); 789 } 790 /** 791 * Tree rotation for rebalancing. 792 * Rotates the node to the left. 793 */ 794 private void rotateLeft(ref Node* node) @nogc @safe pure nothrow { 795 Node* temp = node.right; 796 node.right = temp.left; 797 temp.left = node; 798 node = temp; 799 } 800 /** 801 * Tree rotation for rebalancing. 802 * Rotates the node to the left. 803 */ 804 private void rotateRight(ref Node* node) @nogc @safe pure nothrow { 805 Node* temp = node.left; 806 node.left = temp.right; 807 temp.right = node; 808 node = temp; 809 } 810 /** 811 * Tree rotation for rebalancing. 812 * Rotates the node's right to the left, then the node to the right. 813 */ 814 private void rotateRightLeft(ref Node* node) @nogc @safe pure nothrow { 815 rotateLeft(node.left); 816 rotateRight(node); 817 } 818 /** 819 * Tree rotation for rebalancing. 820 * Rotates the node's left to the right, then the node to the left. 821 */ 822 private void rotateLeftRight(ref Node* node) @nogc @safe pure nothrow { 823 rotateRight(node.right); 824 rotateLeft(node); 825 } 826 /** 827 * Returns the number of currently held elements within the tree. 828 */ 829 public @property size_t length() @nogc @safe pure nothrow const { 830 return nOfElements; 831 } 832 /** 833 * Returns the key of the n-th element. 834 * NOT WORKING! 835 */ 836 /+public K keyOf(size_t n) { 837 static if(E.stringof != "void"){ 838 foreach (key, elem; root) { 839 if (n == 0) { 840 return key; 841 } else n--; 842 } 843 } else { 844 foreach (key; root) { 845 if (n == 0) { 846 return key; 847 } else n--; 848 } 849 } 850 return K.init; 851 }+/ 852 /** 853 * Returns the string representation of the tree. 854 */ 855 public string toString() const { 856 if(root !is null) 857 return root.toString; 858 else 859 return "Empty"; 860 } 861 } 862 863 unittest { 864 import std.stdio : writeln, write; 865 import std.random : uniform; 866 import std.exception : assertThrown; 867 { 868 alias IntMap = TreeMap!(int, int, true); 869 IntMap test0, test1, test2, test3; 870 for(int i ; i < 1024 ; i++)//Stress test to see if large number of elements would cause any issues 871 test0[uniform(0, 65_536)] = i; 872 foreach(k, e; test0){ 873 874 } 875 foreach_reverse(k, e; test0){ 876 877 } 878 for(int i ; i < 16 ; i++) 879 test1[uniform(0, 65_536)] = i; 880 //writeln(test1.toString); 881 for(int i ; i < 32 ; i++) 882 test2[uniform(0, 65_536)] = i; 883 //writeln(test2.toString); 884 for(int i ; i < 64 ; i++) 885 test3[i] = i; 886 for(int i ; i < 64 ; i++) 887 write(test3[i],";"); 888 writeln(); 889 assert(5 in test3); 890 for(int i ; i < 16 ; i++) 891 test3.remove(uniform(0,64)); 892 foreach(i ; test3) 893 write(i,";"); 894 writeln(); 895 } 896 { 897 alias IntMap = TreeMap!(int, int, false); 898 IntMap test0, test1; 899 for(int i ; i < 64 ; i++) 900 test0[i] = i; 901 assert(test0.length == 64, "TreeMap length mismatch"); 902 assertThrown!ElementNotFoundException(test0[420]); 903 assertThrown!ElementNotFoundException(test0[666]); 904 for(int i ; i < 64 ; i++) 905 test0.remove(i); 906 assert(test0.length == 0, "Treemap item removal failure"); 907 for(int i ; i < 16 ; i++) { 908 test1[i] = i; 909 writeln(test1.toString); 910 } 911 assert(5 in test1); 912 } 913 { 914 alias IntMap = TreeMap!(int, void, true); 915 IntMap test0; 916 for(int i ; i < 64 ; i++) { 917 test0.put(i); 918 //writeln(test0.toString()); 919 } 920 assert(5 in test0); 921 assert(test0.length == 64, "TreeMap length mismatch"); 922 for(int i ; i < 64 ; i++) { 923 test0.remove(i); 924 //writeln(test0.toString()); 925 } 926 assert(test0.length == 0, "Treemap item removal failure"); 927 } 928 { 929 alias IntMap = TreeMap!(int, void, false); 930 IntMap test0; 931 for(int i ; i < 64 ; i++) { 932 test0.put(i); 933 //writeln(test0.toString()); 934 } 935 assert(5 in test0); 936 assert(test0.length == 64, "TreeMap length mismatch"); 937 assertThrown!ElementNotFoundException(test0[420]); 938 assertThrown!ElementNotFoundException(test0[666]); 939 for(int i ; i < 64 ; i++) { 940 test0.remove(i); 941 writeln(test0.toString()); 942 } 943 assert(test0.length == 0, "Treemap item removal failure"); 944 } 945 { //test set operators 946 alias IntSet = TreeMap!(int, void, true); 947 IntSet a = IntSet([1, 3, 5, 7, 9]), b = IntSet([1, 5, 9]), c = IntSet([3, 7]); 948 IntSet union_ab = a | b, union_ac = a | c, union_bc = b | c; 949 IntSet intrsctn_ab = a & b, intrsctn_ac = a & c; 950 IntSet cmplmnt_ab = a - b, cmplmnt_ac = a - c; 951 IntSet diff_ab = a ^ b, diff_ac = a ^ c, diff_bc = b ^ c; 952 assert(union_ab.hasRange([1, 3, 5, 7, 9]) == 5); 953 assert(union_ac.hasRange([1, 3, 5, 7, 9]) == 5); 954 assert(union_bc.hasRange([1, 3, 5, 7, 9]) == 5); 955 assert(intrsctn_ab.hasRange([1, 5, 9]) == 3); 956 assert(intrsctn_ac.hasRange([3, 7]) == 2); 957 assert(cmplmnt_ab.hasRange([3, 7]) == 2); 958 assert(cmplmnt_ac.hasRange([1, 5, 9]) == 3); 959 assert(diff_ab.hasRange([3, 7]) == 2); 960 assert(diff_ac.hasRange([1, 5, 9]) == 3); 961 assert(diff_bc.hasRange([1, 3, 5, 7, 9]) == 5); 962 } 963 } 964 @safe pure unittest { 965 alias IntMap = TreeMap!(int, int, false); 966 IntMap test; 967 test[5] = 5; 968 test[7] = 7; 969 test[3] = 3; 970 foreach(elem, key; test) { 971 assert(elem == key); 972 } 973 }