1 /** 2 * T-Tree. 3 * Copyright: © 2015 Economic Modeling Specialists, Intl. 4 * Authors: Brian Schott 5 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 6 */ 7 8 module containers.ttree; 9 10 private import containers.internal.node : shouldAddGCRange; 11 private import std.experimental.allocator.mallocator : Mallocator; 12 13 /** 14 * Implements a binary search tree with multiple items per tree node. 15 * 16 * T-tree Nodes are (by default) sized to fit within a 64-byte 17 * cache line. The number of items stored per node can be read from the 18 * nodeCapacity field. Each node has 0, 1, or 2 children. Each node has between 19 * 1 and $(B nodeCapacity) items, or it has $(B nodeCapacity) items and 0 or 20 * more children. 21 * Params: 22 * T = the element type 23 * Allocator = the allocator to use. Defaults to `Mallocator`. 24 * allowDuplicates = if true, duplicate values will be allowed in the tree 25 * less = the comparitor function to use 26 * cacheLineSize = Nodes will be sized to fit within this number of bytes. 27 * supportGC = true if the container should support holding references to 28 * GC-allocated memory. 29 * See_also: $(LINK http://en.wikipedia.org/wiki/T-tree) 30 */ 31 struct TTree(T, Allocator = Mallocator, bool allowDuplicates = false, 32 alias less = "a < b", bool supportGC = shouldAddGCRange!T, size_t cacheLineSize = 64) 33 { 34 this(this) @disable; 35 36 static if (stateSize!Allocator != 0) 37 { 38 /// No default construction if an allocator must be provided. 39 this() @disable; 40 41 /** 42 * Use `allocator` to allocate and free nodes in the tree. 43 */ 44 this(Allocator allocator) 45 in 46 { 47 assert(allocator !is null, "Allocator must not be null"); 48 } 49 body 50 { 51 this.allocator = allocator; 52 } 53 54 private alias AllocatorType = Allocator; 55 } 56 else 57 alias AllocatorType = void*; 58 59 ~this() 60 { 61 if (root is null) 62 return; 63 deallocateNode(root, allocator); 64 } 65 66 private enum size_t nodeCapacity = fatNodeCapacity!(T.sizeof, 3, size_t, cacheLineSize); 67 static assert (nodeCapacity <= (size_t.sizeof * 4), "cannot fit height info and registry in size_t"); 68 69 debug(EMSI_CONTAINERS) invariant() 70 { 71 assert (root is null || _length != 0); 72 } 73 74 75 alias Value = ContainerStorageType!T; 76 77 /// $(B tree ~= item) operator overload. 78 void opOpAssign(string op)(T value) if (op == "~") 79 { 80 insert(value); 81 } 82 83 /** 84 * Inserts the given value into the tree. 85 * 86 * This is not a stable insert. You 87 * will get strange results if you insert into a tree while iterating over 88 * it. 89 * 90 * Returns: true if any values were added. 91 */ 92 bool insert(T value) 93 { 94 if (root is null) 95 { 96 root = allocateNode(cast(Value) value, null, allocator); 97 ++_length; 98 return true; 99 } 100 immutable bool r = root.insert(cast(Value) value, root, allocator); 101 if (r) 102 ++_length; 103 return r; 104 } 105 106 /// ditto 107 bool insert(R)(R r) if (isInputRange!R && is(ElementType!R == T)) 108 { 109 immutable bool retVal = false; 110 while (!r.empty) 111 { 112 retVal = insert(r.front()) || retVal; 113 r.popFront(); 114 } 115 return retVal; 116 } 117 118 /// ditto 119 bool insert(T[] values) 120 { 121 bool retVal = false; 122 foreach (ref v; values) 123 retVal = insert(v) || retVal; 124 return retVal; 125 } 126 127 alias put = insert; 128 129 /** 130 * Removes a value from the tree. 131 * 132 * Params: 133 * value = a value equal to the one to be removed 134 * cleanup = a function that should be run on the removed item 135 * Retuns: true if any value was removed 136 */ 137 bool remove(T value, void delegate(T) cleanup = null) 138 { 139 bool removed = root !is null && root.remove(cast(Value) value, root, allocator, cleanup); 140 if (removed) 141 --_length; 142 if (_length == 0) 143 deallocateNode(root, allocator); 144 return removed; 145 } 146 147 /** 148 * Returns: true if the tree _conains the given value 149 */ 150 bool contains(T value) const 151 { 152 return root !is null && root.contains(value); 153 } 154 155 /** 156 * Returns: the number of elements in the tree. 157 */ 158 size_t length() const nothrow pure @property 159 { 160 return _length; 161 } 162 163 /** 164 * Returns: true if the tree is empty. 165 */ 166 bool empty() const nothrow pure @property 167 { 168 return _length == 0; 169 } 170 171 /** 172 * Returns: a range over the tree. Do not insert into the tree while 173 * iterating because you may iterate over the same value multiple times. 174 */ 175 auto range(this This)() 176 { 177 return Range!(This)(cast(const(Node)*) root, RangeType.all, T.init); 178 } 179 180 alias opSlice = range; 181 182 /** 183 * Returns: a range of elements which are less than value. 184 */ 185 auto lowerBound(this This)(inout T value) 186 { 187 return Range!(This)(cast(const(Node)*) root, RangeType.lower, value); 188 } 189 190 /** 191 * Returns: a range of elements which are equivalent (though not necessarily 192 * equal) to value. 193 */ 194 auto equalRange(this This)(inout T value) 195 { 196 return Range!(This)(cast(const(Node)*) root, RangeType.equal, value); 197 } 198 199 /** 200 * Returns: a range of elements which are greater than value. 201 */ 202 auto upperBound(this This)(inout T value) 203 { 204 return Range!(This)(cast(const(Node)*) root, RangeType.upper, value); 205 } 206 207 /** 208 * Tree range 209 */ 210 static struct Range(ThisT) 211 { 212 @disable this(); 213 214 /** 215 * Standard range operations 216 */ 217 ET front() const @property 218 in 219 { 220 assert (!empty); 221 } 222 body 223 { 224 return cast(typeof(return)) current.values[index]; 225 } 226 227 /// ditto 228 bool empty() const nothrow pure @property 229 { 230 return current is null; 231 } 232 233 /// ditto 234 void popFront() 235 { 236 _popFront(); 237 if (current is null) 238 return; 239 with (RangeType) final switch (type) 240 { 241 case upper: 242 case all: break; 243 case equal: 244 if (_less(val, front())) 245 current = null; 246 break; 247 case lower: 248 if (!_less(front(), val)) 249 current = null; 250 break; 251 } 252 } 253 254 private: 255 256 alias ET = ContainerElementType!(ThisT, T); 257 258 void currentToLeftmost() 259 { 260 if (current is null) 261 return; 262 while (current.left !is null) 263 current = current.left; 264 } 265 266 void currentToLeastContaining(inout T val) 267 { 268 if (current is null) 269 return; 270 while (current !is null) 271 { 272 assert(current.registry != 0); 273 auto first = current.values[0]; 274 auto last = current.values[current.nextAvailableIndex - 1]; 275 immutable bool valLessFirst = _less(val, first); 276 immutable bool valLessLast = _less(val, last); 277 immutable bool firstLessVal = _less(first, val); 278 immutable bool lastLessVal = _less(last, val); 279 if (firstLessVal && valLessLast) 280 return; 281 else if (valLessFirst) 282 current = current.left; 283 else if (lastLessVal) 284 current = current.right; 285 else 286 { 287 static if (allowDuplicates) 288 { 289 if (!valLessFirst && !firstLessVal) 290 { 291 auto c = current; 292 current = current.left; 293 currentToLeastContaining(val); 294 if (current is null) 295 current = c; 296 return; 297 } 298 else 299 return; 300 } 301 else 302 return; 303 } 304 305 } 306 } 307 308 this(inout(Node)* n, RangeType type, inout T val) 309 { 310 current = n; 311 this.type = type; 312 this.val = val; 313 with (RangeType) final switch(type) 314 { 315 case all: 316 currentToLeftmost(); 317 break; 318 case lower: 319 currentToLeftmost(); 320 if (_less(val, front())) 321 current = null; 322 break; 323 case equal: 324 currentToLeastContaining(val); 325 while (current !is null && _less(front(), val)) 326 _popFront(); 327 if (current is null || _less(front(), val) || _less(val, front())) 328 current = null; 329 break; 330 case upper: 331 currentToLeastContaining(val); 332 while (current !is null && !_less(val, front())) 333 _popFront(); 334 break; 335 } 336 } 337 338 void _popFront() 339 in 340 { 341 assert (!empty); 342 } 343 body 344 { 345 index++; 346 if (index >= nodeCapacity || current.isFree(index)) 347 { 348 index = 0; 349 if (current.right !is null) 350 { 351 current = current.right; 352 while (current.left !is null) 353 current = current.left; 354 } 355 else if (current.parent is null) 356 current = null; 357 else if (current.parent.left is current) 358 current = current.parent; 359 else 360 { 361 while (current.parent.right is current) 362 { 363 current = current.parent; 364 if (current.parent is null) 365 { 366 current = null; 367 return; 368 } 369 } 370 current = current.parent; 371 } 372 } 373 } 374 375 size_t index; 376 const(Node)* current; 377 const RangeType type; 378 const T val; 379 } 380 381 private: 382 383 private import std.range : ElementType, isInputRange; 384 import containers.internal.element_type : ContainerElementType; 385 import containers.internal.node : fatNodeCapacity, fullBits, shouldAddGCRange, shouldNullSlot; 386 import containers.internal.storage_type : ContainerStorageType; 387 import std.algorithm : sort; 388 import std.experimental.allocator.common : stateSize; 389 import std.functional: binaryFun; 390 import std.traits: isPointer, PointerTarget; 391 392 enum RangeType : ubyte {all, lower, equal, upper} 393 394 // If we're storing a struct that defines opCmp, don't compare pointers as 395 // that is almost certainly not what the user intended. 396 static if (is(typeof(less) == string ) && less == "a < b" && isPointer!T && __traits(hasMember, PointerTarget!T, "opCmp")) 397 alias _less = binaryFun!"a.opCmp(*b) < 0"; 398 else 399 alias _less = binaryFun!less; 400 401 static Node* allocateNode(Value value, Node* parent, AllocatorType allocator) 402 out (result) 403 { 404 assert (result.left is null); 405 assert (result.right is null); 406 } 407 body 408 { 409 import core.memory : GC; 410 import std.experimental.allocator : make; 411 412 static if (stateSize!Allocator == 0) 413 Node* n = make!Node(Allocator.instance); 414 else 415 Node* n = make!Node(allocator); 416 n.parent = parent; 417 n.markUsed(0); 418 n.values[0] = cast(Value) value; 419 static if (supportGC && shouldAddGCRange!T) 420 GC.addRange(n, Node.sizeof); 421 return n; 422 } 423 424 static void deallocateNode(ref Node* n, AllocatorType allocator) 425 in 426 { 427 assert (n !is null); 428 } 429 body 430 { 431 import std.experimental.allocator : dispose; 432 import core.memory : GC; 433 434 if (n.left !is null) 435 deallocateNode(n.left, allocator); 436 if (n.right !is null) 437 deallocateNode(n.right, allocator); 438 439 static if (supportGC && shouldAddGCRange!T) 440 GC.removeRange(n); 441 static if (stateSize!Allocator == 0) 442 dispose(Allocator.instance, n); 443 else 444 dispose(allocator, n); 445 n = null; 446 } 447 448 static assert (nodeCapacity <= (typeof(Node.registry).sizeof * 8)); 449 static assert (Node.sizeof <= cacheLineSize); 450 static struct Node 451 { 452 private size_t nextAvailableIndex() const nothrow pure 453 { 454 import core.bitop : bsf; 455 return bsf(~(registry & fullBits!nodeCapacity)); 456 } 457 458 private void markUsed(size_t index) pure nothrow 459 { 460 registry |= (1 << index); 461 } 462 463 private void markUnused(size_t index) pure nothrow 464 { 465 registry &= ~(1 << index); 466 static if (shouldNullSlot!T) 467 values[index] = null; 468 } 469 470 private bool isFree(size_t index) const pure nothrow 471 { 472 return (registry & (1 << index)) == 0; 473 } 474 475 private bool isFull() const pure nothrow 476 { 477 return (registry & fullBits!nodeCapacity) == fullBits!nodeCapacity; 478 } 479 480 private bool isEmpty() const pure nothrow 481 { 482 return (registry & fullBits!nodeCapacity) == 0; 483 } 484 485 bool contains(Value value) const 486 { 487 import std.range : assumeSorted; 488 size_t i = nextAvailableIndex(); 489 if (_less(value, cast(Value) values[0])) 490 return left !is null && left.contains(value); 491 if (_less(values[i - 1], value)) 492 return right !is null && right.contains(value); 493 return !assumeSorted!_less(values[0 .. i]).equalRange(value).empty; 494 } 495 496 size_t calcHeight() nothrow pure 497 { 498 immutable size_t l = left !is null ? left.height() : 0; 499 immutable size_t r = right !is null ? right.height() : 0; 500 size_t h = 1 + (l > r ? l : r); 501 registry &= fullBits!nodeCapacity; 502 registry |= (h << (size_t.sizeof * 4)); 503 return h; 504 } 505 506 size_t height() const nothrow pure 507 { 508 return registry >>> (size_t.sizeof * 4); 509 } 510 511 int imbalanced() const nothrow pure 512 { 513 if (right !is null 514 && ((left is null && right.height() > 1) 515 || (left !is null && right.height() > left.height() + 1))) 516 return 1; 517 if (left !is null 518 && ((right is null && left.height() > 1) 519 || (right !is null && left.height() > right.height() + 1))) 520 return -1; 521 return 0; 522 } 523 524 bool insert(T value, ref Node* root, AllocatorType allocator) 525 in 526 { 527 static if (isPointer!T || is (T == class)) 528 assert (value !is null); 529 } 530 body 531 { 532 import std.algorithm : sort; 533 import std.range : assumeSorted; 534 if (!isFull()) 535 { 536 immutable size_t index = nextAvailableIndex(); 537 static if (!allowDuplicates) 538 { 539 if (!assumeSorted!_less(values[0 .. index]).equalRange( 540 cast(Value) value).empty) 541 { 542 return false; 543 } 544 } 545 values[index] = cast(Value) value; 546 markUsed(index); 547 sort!_less(values[0 .. index + 1]); 548 return true; 549 } 550 if (_less(value, values[0])) 551 { 552 if (left is null) 553 { 554 left = allocateNode(cast(Value) value, &this, allocator); 555 calcHeight(); 556 return true; 557 } 558 immutable bool b = left.insert(value, root, allocator); 559 if (imbalanced() == -1) 560 rotateRight(root, allocator); 561 calcHeight(); 562 return b; 563 } 564 if (_less(values[$ - 1], cast(Value) value)) 565 { 566 if (right is null) 567 { 568 right = allocateNode(value, &this, allocator); 569 calcHeight(); 570 return true; 571 } 572 immutable bool b = right.insert(value, root, allocator); 573 if (imbalanced() == 1) 574 rotateLeft(root, allocator); 575 calcHeight(); 576 return b; 577 } 578 static if (!allowDuplicates) 579 if (!assumeSorted!_less(values[]).equalRange(cast(Value) value).empty) 580 return false; 581 Value[nodeCapacity + 1] temp = void; 582 temp[0 .. $ - 1] = values[]; 583 temp[$ - 1] = cast(Value) value; 584 sort!_less(temp[]); 585 if (right is null) 586 { 587 values[] = temp[0 .. $ - 1]; 588 right = allocateNode(temp[$ - 1], &this, allocator); 589 return true; 590 } 591 if (left is null) 592 { 593 values[] = temp[1 .. $]; 594 left = allocateNode(temp[0], &this, allocator); 595 return true; 596 } 597 if (right.height < left.height) 598 { 599 values[] = temp[0 .. $ - 1]; 600 immutable bool b = right.insert(temp[$ - 1], root, allocator); 601 if (imbalanced() == 1) 602 rotateLeft(root, allocator); 603 calcHeight(); 604 return b; 605 } 606 values[] = temp[1 .. $]; 607 immutable bool b = left.insert(temp[0], root, allocator); 608 if (imbalanced() == -1) 609 rotateRight(root, allocator); 610 calcHeight(); 611 return b; 612 } 613 614 bool remove(Value value, ref Node* n, AllocatorType allocator, 615 void delegate(T) cleanup = null) 616 { 617 import std.range : assumeSorted; 618 assert (!isEmpty()); 619 if (isFull() && _less(value, values[0])) 620 { 621 immutable bool r = left !is null && left.remove(value, left, allocator, cleanup); 622 if (left.isEmpty()) 623 deallocateNode(left, allocator); 624 return r; 625 } 626 if (isFull() && _less(values[$ - 1], value)) 627 { 628 immutable bool r = right !is null && right.remove(value, right, allocator, cleanup); 629 if (right.isEmpty()) 630 deallocateNode(right, allocator); 631 return r; 632 } 633 size_t i = nextAvailableIndex(); 634 auto sv = assumeSorted!_less(values[0 .. i]); 635 auto tri = sv.trisect(value); 636 if (tri[1].length == 0) 637 return false; 638 // Clean up removed item 639 if (cleanup !is null) 640 cleanup(tri[1][0]); 641 immutable size_t l = tri[0].length; 642 if (right is null && left is null) 643 { 644 Value[nodeCapacity - 1] temp; 645 temp[0 .. l] = values[0 .. l]; 646 temp[l .. $] = values[l + 1 .. $]; 647 values[0 .. $ - 1] = temp[]; 648 markUnused(nextAvailableIndex() - 1); 649 } 650 else if (right !is null) 651 { 652 Value[nodeCapacity - 1] temp; 653 temp[0 .. l] = values[0 .. l]; 654 temp[l .. $] = values[l + 1 .. $]; 655 values[0 .. $ - 1] = temp[]; 656 values[$ - 1] = right.removeSmallest(allocator); 657 if (right.isEmpty()) 658 deallocateNode(right, allocator); 659 } 660 else if (left !is null) 661 { 662 Value[nodeCapacity - 1] temp; 663 temp[0 .. l] = values[0 .. l]; 664 temp[l .. $] = values[l + 1 .. $]; 665 values[1 .. $] = temp[]; 666 values[0] = left.removeLargest(allocator); 667 if (left.isEmpty()) 668 deallocateNode(left, allocator); 669 } 670 return true; 671 } 672 673 Value removeSmallest(AllocatorType allocator) 674 in 675 { 676 assert (!isEmpty()); 677 } 678 body 679 { 680 if (left is null && right is null) 681 { 682 Value r = values[0]; 683 Value[nodeCapacity - 1] temp = void; 684 temp[] = values[1 .. $]; 685 values[0 .. $ - 1] = temp[]; 686 markUnused(nextAvailableIndex() - 1); 687 return r; 688 } 689 if (left !is null) 690 { 691 auto r = left.removeSmallest(allocator); 692 if (left.isEmpty()) 693 deallocateNode(left, allocator); 694 return r; 695 } 696 Value r = values[0]; 697 Value[nodeCapacity - 1] temp = void; 698 temp[] = values[1 .. $]; 699 values[0 .. $ - 1] = temp[]; 700 values[$ - 1] = right.removeSmallest(allocator); 701 if (right.isEmpty()) 702 deallocateNode(right, allocator); 703 return r; 704 } 705 706 Value removeLargest(AllocatorType allocator) 707 in 708 { 709 assert (!isEmpty()); 710 } 711 out (result) 712 { 713 static if (isPointer!T || is (T == class)) 714 assert (result !is null); 715 } 716 body 717 { 718 if (left is null && right is null) 719 { 720 size_t i = nextAvailableIndex() - 1; 721 Value r = values[i]; 722 markUnused(i); 723 return r; 724 } 725 if (right !is null) 726 { 727 auto r = right.removeLargest(allocator); 728 if (right.isEmpty()) 729 deallocateNode(right, allocator); 730 return r; 731 } 732 Value r = values[$ - 1]; 733 Value[nodeCapacity - 1] temp = void; 734 temp[] = values[0 .. $ - 1]; 735 values[1 .. $] = temp[]; 736 values[0] = left.removeLargest(allocator); 737 if (left.isEmpty()) 738 deallocateNode(left, allocator); 739 return r; 740 } 741 742 void rotateLeft(ref Node* root, AllocatorType allocator) 743 { 744 Node* newRoot = void; 745 if (right.left !is null && right.right is null) 746 { 747 newRoot = right.left; 748 newRoot.parent = this.parent; 749 newRoot.left = &this; 750 newRoot.left.parent = newRoot; 751 newRoot.right = right; 752 newRoot.right.parent = newRoot; 753 newRoot.right.left = null; 754 right = null; 755 left = null; 756 } 757 else 758 { 759 newRoot = right; 760 newRoot.parent = this.parent; 761 right = newRoot.left; 762 if (right !is null) 763 right.parent = &this; 764 newRoot.left = &this; 765 this.parent = newRoot; 766 } 767 cleanup(newRoot, root, allocator); 768 } 769 770 void rotateRight(ref Node* root, AllocatorType allocator) 771 { 772 Node* newRoot = void; 773 if (left.right !is null && left.left is null) 774 { 775 newRoot = left.right; 776 newRoot.parent = this.parent; 777 newRoot.right = &this; 778 newRoot.right.parent = newRoot; 779 newRoot.left = left; 780 newRoot.left.parent = newRoot; 781 newRoot.left.right = null; 782 left = null; 783 right = null; 784 } 785 else 786 { 787 newRoot = left; 788 newRoot.parent = this.parent; 789 left = newRoot.right; 790 if (left !is null) 791 left.parent = &this; 792 newRoot.right = &this; 793 this.parent = newRoot; 794 } 795 cleanup(newRoot, root, allocator); 796 } 797 798 void cleanup(Node* newRoot, ref Node* root, AllocatorType allocator) 799 { 800 if (newRoot.parent !is null) 801 { 802 if (newRoot.parent.right is &this) 803 newRoot.parent.right = newRoot; 804 else 805 newRoot.parent.left = newRoot; 806 } 807 else 808 root = newRoot; 809 newRoot.fillFromChildren(root, allocator); 810 if (newRoot.left !is null) 811 { 812 newRoot.left.fillFromChildren(root, allocator); 813 } 814 if (newRoot.right !is null) 815 { 816 newRoot.right.fillFromChildren(root, allocator); 817 } 818 if (newRoot.left !is null) 819 newRoot.left.calcHeight(); 820 if (newRoot.right !is null) 821 newRoot.right.calcHeight(); 822 newRoot.calcHeight(); 823 } 824 825 void fillFromChildren(ref Node* root, AllocatorType allocator) 826 { 827 while (!isFull()) 828 { 829 if (left !is null) 830 { 831 insert(left.removeLargest(allocator), root, allocator); 832 if (left.isEmpty()) 833 deallocateNode(left, allocator); 834 } 835 else if (right !is null) 836 { 837 insert(right.removeSmallest(allocator), root, allocator); 838 if (right.isEmpty()) 839 deallocateNode(right, allocator); 840 } 841 else 842 return; 843 } 844 } 845 846 debug(EMSI_CONTAINERS) invariant() 847 { 848 import std..string : format; 849 assert (&this !is null); 850 assert (left !is &this, "%x, %x".format(left, &this)); 851 assert (right !is &this, "%x, %x".format(right, &this)); 852 if (left !is null) 853 { 854 assert (left.left !is &this, "%s".format(values)); 855 assert (left.right !is &this, "%x, %x".format(left.right, &this)); 856 assert (left.parent is &this, "%x, %x, %x".format(left, left.parent, &this)); 857 } 858 if (right !is null) 859 { 860 assert (right.left !is &this, "%s".format(values)); 861 assert (right.right !is &this, "%s".format(values)); 862 assert (right.parent is &this); 863 } 864 } 865 866 Node* left; 867 Node* right; 868 Node* parent; 869 870 Value[nodeCapacity] values; 871 size_t registry = (cast(size_t) 1) << (size_t.sizeof * 4); 872 } 873 874 AllocatorType allocator; 875 size_t _length = 0; 876 Node* root = null; 877 } 878 879 unittest 880 { 881 import core.memory : GC; 882 import std.algorithm : equal, sort, map, filter, each; 883 import std.array : array; 884 import std.range : iota, walkLength, isInputRange; 885 import std..string : format; 886 import std.uuid : randomUUID; 887 888 { 889 TTree!int kt; 890 assert (kt.empty); 891 foreach (i; 0 .. 200) 892 assert (kt.insert(i)); 893 assert (!kt.empty); 894 assert (kt.length == 200); 895 assert (kt.contains(30)); 896 } 897 898 { 899 TTree!int kt; 900 assert (!kt.contains(5)); 901 kt.insert(2_000); 902 assert (kt.contains(2_000)); 903 foreach_reverse (i; 0 .. 1_000) 904 { 905 assert (kt.insert(i)); 906 } 907 assert (!kt.contains(100_000)); 908 } 909 910 { 911 import std.random : uniform; 912 TTree!int kt; 913 foreach (i; 0 .. 1_000) 914 kt.insert(uniform(0, 100_000)); 915 } 916 917 { 918 TTree!int kt; 919 kt.insert(10); 920 assert (kt.length == 1); 921 assert (!kt.insert(10)); 922 assert (kt.length == 1); 923 } 924 925 { 926 TTree!(int, Mallocator, true) kt; 927 assert (kt.insert(1)); 928 assert (kt.length == 1); 929 assert (kt.insert(1)); 930 assert (kt.length == 2); 931 assert (kt.contains(1)); 932 } 933 934 { 935 TTree!(int) kt; 936 foreach (i; 0 .. 200) 937 assert (kt.insert(i)); 938 assert (kt.length == 200); 939 assert (kt.remove(79)); 940 assert (!kt.remove(79)); 941 assert (kt.length == 199); 942 } 943 944 { 945 string[] strs = [ 946 "2c381d2a-bacd-40db-b6d8-055b144c5ee6", 947 "62104b50-e235-4c95-bcb9-a545e88e2d09", 948 "828c8fc0-a392-4738-a49c-62e991fce090", 949 "62e30465-79eb-446e-b34f-af5d7c491486", 950 "93ec245b-60d2-4422-91ff-66a6d7e299fc", 951 "c1d2f3d7-82cc-4d90-a2c5-9fba335f36cd", 952 "c9d8d980-94eb-4941-b873-00d68021522f", 953 "82dbc4df-cb3c-447a-9d73-cd6291a0ba02", 954 "8d259231-6ab6-49e4-9bb6-fe097c4153ed", 955 "f9f2d719-61e1-4f62-ae2c-bf2a24a13d5b" 956 ]; 957 TTree!string strings; 958 foreach (i, s; strs) 959 assert (strings.insert(s)); 960 sort(strs[]); 961 assert (equal(strs, strings[])); 962 } 963 964 foreach (x; 0 .. 1000) 965 { 966 TTree!string strings; 967 string[] strs = iota(10).map!(a => randomUUID().toString()).array(); 968 foreach (i, s; strs) 969 assert (strings.insert(s)); 970 assert (strings.length == strs.length); 971 sort(strs); 972 assert (equal(strs, strings[])); 973 } 974 975 { 976 TTree!string strings; 977 strings.insert(["e", "f", "a", "b", "c", "d"]); 978 assert (equal(strings[], ["a", "b", "c", "d", "e", "f"])); 979 } 980 981 { 982 TTree!(string, Mallocator, true) strings; 983 assert (strings.insert("b")); 984 assert (strings.insert("c")); 985 assert (strings.insert("a")); 986 assert (strings.insert("d")); 987 assert (strings.insert("d")); 988 assert (strings.length == 5); 989 assert (equal(strings.equalRange("d"), ["d", "d"]), format("%s", strings.equalRange("d"))); 990 assert (equal(strings.equalRange("a"), ["a"]), format("%s", strings.equalRange("a"))); 991 assert (equal(strings.lowerBound("d"), ["a", "b", "c"]), format("%s", strings.lowerBound("d"))); 992 assert (equal(strings.upperBound("c"), ["d", "d"]), format("%s", strings.upperBound("c"))); 993 } 994 995 { 996 static struct S 997 { 998 string x; 999 int opCmp (ref const S other) const 1000 { 1001 if (x < other.x) 1002 return -1; 1003 if (x > other.x) 1004 return 1; 1005 return 0; 1006 } 1007 } 1008 1009 TTree!(S*, Mallocator, true) stringTree; 1010 auto one = S("offset"); 1011 stringTree.insert(&one); 1012 auto two = S("object"); 1013 assert (stringTree.equalRange(&two).empty); 1014 assert (!stringTree.equalRange(&one).empty); 1015 assert (stringTree[].front.x == "offset"); 1016 } 1017 1018 { 1019 static struct TestStruct 1020 { 1021 int opCmp(ref const TestStruct other) const 1022 { 1023 return x < other.x ? -1 : (x > other.x ? 1 : 0); 1024 } 1025 int x; 1026 int y; 1027 } 1028 TTree!(TestStruct*) tsTree; 1029 static assert (isInputRange!(typeof(tsTree[]))); 1030 foreach (i; 0 .. 100) 1031 assert(tsTree.insert(new TestStruct(i, i * 2))); 1032 assert (tsTree.length == 100); 1033 auto r = tsTree[]; 1034 TestStruct* prev = r.front(); 1035 r.popFront(); 1036 while (!r.empty) 1037 { 1038 assert (r.front.x > prev.x, format("%s %s", prev.x, r.front.x)); 1039 prev = r.front; 1040 r.popFront(); 1041 } 1042 TestStruct a = TestStruct(30, 100); 1043 auto eqArray = array(tsTree.equalRange(&a)); 1044 assert (eqArray.length == 1, format("%d", eqArray.length)); 1045 } 1046 1047 { 1048 import std.algorithm : canFind; 1049 TTree!int ints; 1050 foreach (i; 0 .. 50) 1051 ints ~= i; 1052 assert (canFind(ints[], 20)); 1053 assert (walkLength(ints[]) == 50); 1054 assert (walkLength(filter!(a => (a & 1) == 0)(ints[])) == 25); 1055 } 1056 1057 { 1058 TTree!int ints; 1059 foreach (i; 0 .. 50) 1060 ints ~= i; 1061 ints.remove(0); 1062 assert (ints.length == 49); 1063 foreach (i; 1 .. 12) 1064 ints.remove(i); 1065 assert (ints.length == 49 - 11); 1066 } 1067 1068 { 1069 const(TTree!(const(int))) getInts() 1070 { 1071 TTree!(const(int)) t; 1072 t.insert(1); 1073 t.insert(2); 1074 t.insert(3); 1075 return t; 1076 } 1077 auto t = getInts(); 1078 static assert (is (typeof(t[].front) == const(int))); 1079 assert (equal(t[].filter!(a => a & 1), [1, 3])); 1080 } 1081 1082 1083 { 1084 static struct ABC 1085 { 1086 ulong a; 1087 ulong b; 1088 1089 int opCmp(ref const ABC other) const 1090 { 1091 if (this.a < other.a) 1092 return -1; 1093 if (this.a > other.a) 1094 return 1; 1095 return 0; 1096 } 1097 } 1098 1099 TTree!(ABC, Mallocator, true) tree; 1100 foreach (i; 0 .. 10) 1101 tree.insert(ABC(i)); 1102 tree.insert(ABC(15)); 1103 tree.insert(ABC(15)); 1104 tree.insert(ABC(15)); 1105 tree.insert(ABC(15)); 1106 foreach (i; 20 .. 30) 1107 tree.insert(ABC(i)); 1108 assert(tree.equalRange(ABC(15)).walkLength() == 4, 1109 format("Actual length = %d", tree.equalRange(ABC(15)).walkLength())); 1110 } 1111 1112 { 1113 TTree!int ints2; 1114 iota(0, 1_000_000).each!(a => ints2.insert(a)); 1115 assert(equal(iota(0, 1_000_000), ints2[])); 1116 assert(ints2.length == 1_000_000); 1117 foreach (i; 0 .. 1_000_000) 1118 assert(!ints2.equalRange(i).empty, format("Could not find %d", i)); 1119 } 1120 1121 { 1122 TTree!int ints3; 1123 foreach (i; iota(0, 1_000_000).filter!(a => a % 2 == 0)) 1124 ints3.insert(i); 1125 assert(ints3.length == 500_000); 1126 foreach (i; iota(0, 1_000_000).filter!(a => a % 2 == 0)) 1127 assert(!ints3.equalRange(i).empty); 1128 foreach (i; iota(0, 1_000_000).filter!(a => a % 2 == 1)) 1129 assert(ints3.equalRange(i).empty); 1130 } 1131 1132 { 1133 import std.experimental.allocator.building_blocks.free_list : FreeList; 1134 import std.experimental.allocator.building_blocks.allocator_list : AllocatorList; 1135 import std.experimental.allocator.building_blocks.region : Region; 1136 import std.experimental.allocator.building_blocks.stats_collector : StatsCollector; 1137 import std.stdio : stdout; 1138 1139 StatsCollector!(FreeList!(AllocatorList!(a => Region!(Mallocator)(1024 * 1024)), 1140 64)) allocator; 1141 { 1142 auto ints4 = TTree!(int, typeof(&allocator))(&allocator); 1143 foreach (i; 0 .. 10_000) 1144 ints4.insert(i); 1145 assert(walkLength(ints4[]) == 10_000); 1146 } 1147 assert(allocator.numAllocate == allocator.numDeallocate); 1148 assert(allocator.bytesUsed == 0); 1149 } 1150 }