1 /** 2 Copyright: Copyright (c) 2016- Alexander Orlov. All rights reserved. 3 License: $(LINK2 https://opensource.org/licenses/MIT, MIT License). 4 Authors: Alexander Orlov, $(LINK2 mailto:sascha.orlov@gmail.com, sascha.orlov@gmail.com) 5 */ 6 7 /** 8 This module implements a Van Emde Boas tree container. 9 10 The module is still a work in progress. So, if you find an error by chance, please let me know in any way. 11 12 The main idea of the container is, to restrict the capacity of the tree by the next power of two universe size, given a 13 maximum element at the initialization. As long as the usage is intended to contains keys, as in the current version, 14 this restriction is not only a restriction of the amount of elements but also on the contained element values. 15 */ 16 17 // TODO: provide functionality to contain non-unique keys, i. e. exercise 20.3.1 from Cormen 18 // TODO: provide functionality to contain associated data with the keys, i. e. exercise 20.3.2 from Cormen 19 20 /** 21 In this version, the maximum size of the universe possible is 2^32. With this restriction all unsigned integers could 22 be used as keys, if the appropriate maximum value is given on initialization. 23 24 The main advantage of the Van Emde Boas tree appears on a large amount of elements, as the provided standard operations 25 of the tree are constant in time and of order O(lg2(lg2(U))), where U is the capacity of the tree. For small amount of 26 elements the overhead coming along with the structure take over. For example, for a universe size of 2^14 and 15872 27 insertion operatios the duration for the Van Emde Boas tree is about 1*10^(-3) times smaller. As one of the unittests 28 shows. 29 */ 30 31 /** 32 Be aware, the current container is intended to be used with keys. This implies, that the capacity, fixed on its 33 initialization has two meanings. As usual, it shows the maximum amount of elements the instanciated tree can keep. But 34 also, it states, that no value bigger then capacity - 1 exists in the tree. This, and the fact, that only non-negative 35 values can be used are infered from the term "key". 36 */ 37 38 /** 39 See_also: Thomas H. Cormen, Clifford Stein, Ronald L. Rivest, and Charles E. Leiserson. 2001. <em>Introduction to 40 Algorithms</em> (2nd ed.). McGraw-Hill Higher Education. 41 */ 42 43 module vebtree; 44 45 import std.typecons; /// used for Nullable!uint 46 import core.bitop; 47 import comp = std.algorithm.comparison; 48 import std.algorithm.iteration; 49 import std.exception; 50 import std.range; 51 52 version(unittest) 53 { 54 import std.random; 55 import std.datetime; 56 import std.conv : to; 57 import std.container; 58 import std.algorithm.setops; 59 import std.algorithm.sorting; 60 import std.algorithm.searching; 61 } 62 63 // defines the base universe size of a tree node. 64 ubyte BASE_SIZE = 2; 65 66 // Convinience function to return the ceiling to the next power of two number of the given input. 67 uint nextPowerOfTwo(uint value) { return 1 << (bsr(value) + 1); } 68 /// 69 unittest 70 { 71 assert(nextPowerOfTwo(1000) == 1024); 72 assert(nextPowerOfTwo(1024) == 2048); 73 } 74 75 /* 76 This function returns the upper square root of the given input as integer. It is needed in the initialization step of 77 the VEB tree to calculate the number of children of a given layer. The upper square root is defined by 2^{\lceil(\lg 78 u)/2\rceil} 79 */ 80 uint higherSquareRoot(uint value){return 1 << (value.lowerSqrtShift + (value > (1 << value.bsr) || value.bsr & 1));} 81 /// 82 unittest 83 { 84 assert(higherSquareRoot(5) == 4); 85 assert(higherSquareRoot(17) == 8); 86 assert(higherSquareRoot(88) == 16); 87 assert(higherSquareRoot(64) == 8); 88 assert(higherSquareRoot(128) == 16); 89 } 90 91 /* 92 This function returns the floored log2 value of the input. This is done by counting up to the position of the leading 93 bit position and dividing this value by two. This method is needed both by the higher and lower square root 94 calculation. 95 */ 96 uint lowerSqrtShift(uint value) { return bsr(value)/2; } 97 /// 98 unittest 99 { 100 assert(lowerSqrtShift(8) == 1); 101 } 102 103 /* 104 This function returns the lower square root of the given input as integer. It is needed by the indexing functions 105 high(x), low(x) and index(x,y) of elements in the tree. The lower square root is defined by 2^{\lfloor(\lg u)/2\rfloor} 106 */ 107 uint lowerSquareRoot(uint value) { return 1 << lowerSqrtShift(value); } 108 /// 109 unittest 110 { 111 assert(lowerSquareRoot(5) == 2); 112 assert(lowerSquareRoot(17) == 4); 113 assert(lowerSquareRoot(88) == 8); 114 assert(lowerSquareRoot(64) == 8); 115 } 116 117 /* 118 This is an index function defined as \lfloor x/lowerSquareRoot(u)\rfloor. It is needed to find the appropriate cluster 119 of a element in the tree. 120 */ 121 uint high(uint value, uint universeSize) { return value/lowerSquareRoot(universeSize); } 122 /// 123 unittest 124 { 125 assert(high(7,16) == 1); 126 } 127 128 /* 129 This is an index function defined as fmod(value, lowerSquareRoot(universeSize)). It is needed to find the appropriate 130 value inside a cluster. 131 */ 132 uint low(uint value, uint universeSize){ return value & (lowerSquareRoot(universeSize) - 1); } 133 /// 134 unittest 135 { 136 assert(low(7,16) == 3); 137 } 138 139 /* 140 This is an index function to retain the searched value. It is defined as x * lowerSquareRoot(u) + y. Beyond this, the 141 relation holds: x = index(high(x), low(x)) 142 */ 143 uint index(uint universeSize, uint x, uint y){ return (x * lowerSquareRoot(universeSize) + y); } 144 /// 145 unittest 146 { 147 uint currentSeed = unpredictableSeed(); 148 Random rndGenInUse; 149 rndGenInUse.seed(currentSeed); //initialize the random generator 150 uint M = uniform(0U,1 << 14, rndGenInUse); //set universe size to some integer. 151 uint U = nextPowerOfTwo(M); 152 uint x = uniform(0U, U, rndGenInUse); 153 uint y = uniform(0U, U, rndGenInUse); 154 155 assert(index(U, high(x, U), low(x, U)) == x); 156 } 157 158 /** 159 This is the struct to represent a VEB tree node. As memebers it contains the universeSize, the min and the max value as 160 well as a link to a summary node and a cluster, which is a range of VEB tree nodes of size higherSquareRoot(u). Each 161 child node has a universe size of lowerSquareRoot(u) 162 */ 163 private struct vebNode 164 { 165 uint _universeSize; 166 @property uint universeSize(){ return _universeSize; } 167 168 // min value is contained in the node as a separate value, this value can't be found in child nodes. 169 Nullable!uint _min; 170 @property void min(uint value){ _min = value; } 171 @property Nullable!uint min() { return _min; } 172 173 // max value is contained in the node as a separate value, this can be found in child nodes. 174 Nullable!uint _max; 175 @property void max(uint value){ _max = value; } 176 @property Nullable!uint max(){ return _max; } 177 178 // the node is empty, iff neither min nor max is set. 179 @property bool empty() { return min.isNull; } 180 181 // VEB node containing the summary node. 182 private vebNode* _summary; 183 // VEB cluster containing the child nodes. 184 private vebNode[] _cluster; 185 186 187 @disable this(); // initializing without providing a universe size is prohibited. 188 this(uint universeSize) // ditto 189 { 190 this._universeSize = universeSize; 191 initializeChildren(universeSize); 192 } 193 194 // after constructing the node, construct every of its children. 195 private void initializeChildren(uint universeSize) 196 { 197 if(universeSize > BASE_SIZE) 198 { 199 auto childUniverseSize = higherSquareRoot(universeSize); 200 _summary = new vebNode(childUniverseSize); 201 _cluster = iota(0,childUniverseSize).map!(a => vebNode(lowerSquareRoot(universeSize))).array; 202 } 203 } 204 205 /* 206 this function inserts a value into an empty node. The difference between empty and non empty insert is, that the 207 algorithm doesn't look into deepness of the tree, it just inserts the node to the separately stored min and max 208 members. 209 */ 210 private void emptyInsert(uint x) 211 { 212 min = x; 213 max = x; 214 } 215 216 // this function inserts a value into a generic node. If the member exists, no insertion will be done. 217 void insert(uint x) 218 { 219 if(this.empty) 220 emptyInsert(x); 221 else 222 { 223 if(x < min) 224 { 225 auto temp = x; x = min; min = temp; 226 } 227 228 if(universeSize > BASE_SIZE) 229 { 230 if(_cluster[high(x)].min.isNull) 231 { 232 _summary.insert(high(x)); 233 _cluster[high(x)].emptyInsert(low(x)); 234 } 235 else 236 _cluster[high(x)].insert(low(x)); 237 } 238 if(x > max) 239 max = x; 240 } 241 } 242 243 // this function removes a value from the tree. If the value doesn't exist in the tree nothing will be happen. 244 void remove(uint x) 245 { 246 // case: there is only single element 247 if(min == max) 248 { 249 _min.nullify; 250 _max.nullify; 251 } 252 else if(BASE_SIZE == universeSize) 253 { 254 min = (x == 0) ? 1 : 0; 255 max = min; 256 } 257 else 258 { 259 if(x == min) 260 { 261 auto firstcluster = _summary.min; 262 x = index(firstcluster, _cluster[firstcluster].min); 263 min = x; 264 } 265 _cluster[high(x)].remove(low(x)); 266 if(_cluster[high(x)].min.isNull) 267 { 268 _summary.remove(high(x)); 269 if(x == max) 270 { 271 auto summarymax = _summary.max; 272 if(summarymax.isNull) 273 max = min; 274 else 275 max = index(summarymax, _cluster[summarymax].max); 276 } 277 } 278 else if(x == max) 279 max = index(high(x), _cluster[high(x)].max); 280 } 281 } 282 283 /* 284 this function returns the successor of the given value, even, if the value is not present in the tree. 285 If the value is maximum or greater then the maximum of the tree null is returned. 286 */ 287 Nullable!uint successor(uint x) 288 { 289 Nullable!uint result; 290 291 if(BASE_SIZE == universeSize) 292 { 293 if(x == 0 && max == 1) 294 result = 1; 295 } 296 else 297 { 298 if(!min.isNull && x < min) 299 result = min; 300 else 301 { 302 if(!max.isNull && x < max) 303 { 304 auto maxlow = _cluster[high(x)].max; 305 if(!maxlow.isNull && low(x) < maxlow) 306 { 307 auto offset = _cluster[high(x)].successor(low(x)); 308 result = index(high(x), offset); 309 } 310 else 311 { 312 auto succcluster = _summary.successor(high(x)); 313 if(!succcluster.isNull) 314 { 315 auto offset = _cluster[succcluster].min; 316 result = index(succcluster, offset); 317 } 318 } 319 } 320 } 321 } 322 return result; 323 } 324 325 /* 326 this function returns the predecessor of the given value, even, if the value is not present in the tree. 327 if the value is the minimum or smaller then the minimum of the tree null is returned. 328 */ 329 Nullable!uint predecessor(uint x) 330 { 331 Nullable!uint result; 332 if(BASE_SIZE == universeSize) 333 { 334 if(x == 1 && !min.isNull && min == 0) 335 result = 0; 336 } 337 else 338 { 339 if(!max.isNull && x > max) 340 result = max; 341 else 342 { 343 auto minlow = _cluster[high(x)].min; 344 if(!minlow.isNull && low(x) > minlow) 345 { 346 auto offset = _cluster[high(x)].predecessor(low(x)); 347 result = index(high(x), offset); 348 } 349 else 350 { 351 auto predcluster = _summary.predecessor(high(x)); 352 if(predcluster.isNull) 353 { 354 if(!min.isNull && x > min) 355 result = min; 356 } 357 else 358 { 359 auto offset = _cluster[predcluster].max; 360 result = index(predcluster, offset); 361 } 362 } 363 } 364 } 365 return result; 366 } 367 368 // This function returns whether the input key is currently member of the tree. 369 bool member(uint x) 370 { 371 bool returnVal; 372 373 if(x < universeSize) 374 { 375 if(!min.isNull && (min == x || max == x)) 376 returnVal = true; 377 else 378 { 379 if(BASE_SIZE != universeSize) 380 returnVal = _cluster[high(x)].member(low(x)); 381 } 382 } 383 return returnVal; 384 } 385 386 // this function is an concretization of the module wide indexing function 387 uint index(uint x, uint y){ return .index(universeSize, x, y); } 388 389 // this function is an concretization of the module wide indexing function 390 uint high(uint x){ return .high(x, universeSize); } 391 392 // this function is an concretization of the module wide indexing function 393 uint low(uint x){ return .low(x, universeSize); } 394 } 395 396 /** 397 This struct represents the VEB tree itself. For the sake of convinience it saves the provided at the initialization step 398 wished maximum element. However at the point of development it is only used for testing. Beyond this, it stores only 399 the reference to the root element, as the theory tells. The tree implements not only the documented interface of a 400 van VEB tree, but is also a bidirectional range. It supports two slice operations and a non-trivial opIndex operator. 401 */ 402 class vebTree 403 { 404 // the root element of the tree. 405 private vebNode root; 406 // this member is updated on every insertion and deletion to give the current element count on O(1) 407 private uint _elementCount; 408 // this member stores the initialization size, as it would be lost forever after initialization, otherwise 409 immutable uint expectedSize; 410 411 /// default constructor of a VEB tree is disabled. 412 @disable this(); 413 414 /// to construct a VEB tree one should provide the maximum element one wish to be able to store. 415 this(uint expectedSize) 416 { 417 root = vebNode(nextPowerOfTwo(expectedSize - 1)); 418 this.expectedSize = expectedSize; 419 } 420 421 /// another possibility is to construct a VEB tree by providing an array. 422 this(uint[] range) 423 { 424 // check, whether the range is not too long. I. e. expressable with an uint. 425 enforce(bsr(range.length) < bsr(uint.max)); 426 427 // first, we have to determine the size of the tree. 428 // it is either derived from the length of the given tree or its greatest element 429 uint limit = cast(uint)range.length; 430 431 foreach(uint i; range) limit = comp.max(limit,i); 432 433 enforce(bsr(limit) < bsr(uint.max), "you are crazy, dude!"); 434 this(limit + 1); 435 436 foreach(uint i; range) insert(i); 437 } 438 439 /** 440 this method returns the capacity of the tree. It is equal to the next power of two with regard to the maximum 441 element 442 */ 443 uint capacity(){ return root.universeSize; } 444 445 /// this method is used to add an element to the tree. duplicate values will be ignored. 446 void insert(uint x) 447 { 448 if(x < capacity && !member(x)) 449 { 450 root.insert(x); 451 _elementCount++; 452 } 453 } 454 455 /// this method overrides the insert method to directly use arrays 456 void insert(uint[] arr){ foreach(uint i; arr) insert(i); } 457 458 /// this method is used to remove elements from the tree. not existing values will be ignored. 459 void remove(uint x) 460 { 461 if(member(x)) 462 { 463 root.remove(x); 464 _elementCount--; 465 } 466 } 467 468 /// this method is used to determine, whether an element is currently present in the tree 469 bool member(uint x){ return root.member(x); } 470 471 /// this method is used to determine the minimum of the tree 472 @property Nullable!uint min(){ return front; } 473 474 /// this method is used to determine the maximum of the tree 475 @property Nullable!uint max(){ return back; } 476 477 /// this method retrieves the successor of the given input. 478 Nullable!uint successor(uint x){ return root.successor(x); } 479 480 /// this method retrieves the predecessor of the given input. 481 Nullable!uint predecessor(uint x){ return root.predecessor(x); } 482 483 // this method is used to determine, whether the tree is currently containing an element. 484 @property bool empty(){ return root.empty; } 485 486 // this method returns the minimium. 487 @property Nullable!uint front(){ return root.min; } 488 489 // this method removes the minimum element 490 void popFront(){ if(!empty) remove(min); } 491 492 // forward range also needs save. This is a draft version of the save function, it uses the opslice of the struct 493 // to construct a new one via an array 494 @property vebTree save() { return new vebTree(this[]); } 495 496 /** 497 opSlice operator to get the underlying array. 498 This is a draft version, as it uses the successor method of the class. So getting the underlying array is 499 proportional to n. As this functionaly is not seen as crucial, it is enough for the first time. 500 */ 501 uint[] opSlice() 502 { 503 uint[] retArray; 504 if(!min.isNull) 505 { 506 retArray ~= min; 507 if(min != max) 508 { 509 retArray.reserve(max - min + 1); 510 while(retArray[$-1] != max) 511 retArray ~= successor(retArray[$-1]); 512 } 513 } 514 return retArray; 515 } 516 517 /** 518 opSlice operator to get the underlying array between given bounds. 519 This is a draft version, as it uses the successor method of the class. So getting the underlying array is 520 proportional to min(n, m), where n is the number of elements between bounds and m are the number of present 521 elements in the tree. 522 */ 523 uint[] opSlice(uint begin, uint end) 524 { 525 uint[] retArray; 526 527 if(!max.isNull) 528 { 529 uint limit = comp.min(end,this.max); 530 if(begin < limit) 531 { 532 if(this.member(begin)) 533 retArray ~= begin; 534 else 535 { 536 auto i = successor(begin); 537 if(!i.isNull && i < end) 538 retArray ~= i; 539 } 540 if(min != max) 541 { 542 retArray.reserve(limit - begin + 1); 543 auto i = successor(retArray[$-1]); 544 while(!i.isNull && i < limit) 545 { 546 retArray ~= i; 547 i = successor(retArray[$-1]); 548 } 549 } 550 } 551 } 552 return retArray; 553 } 554 555 /** 556 This is a nontrivial opIndex operator on indizies of the tree. Given an index a range (!) is returned, which is, 557 either the range of elements in the tree build up by [predecessor(i) .. successor(i)] (i. e. excluding the 558 successor(i)), when the given index is not set. Or, if the given index is set, [member(i), successor(i)]. If an 559 index out of bounds is given, an empty array is returned. The tree must not be empty to use this function. 560 */ 561 auto opIndex(uint i) 562 { 563 enforce(!this.empty); 564 565 auto retVal = iota(0U, 0U); 566 if(i < this.capacity) 567 retVal = iota(aMin(i), aMax(i)); 568 569 return retVal; 570 } 571 572 // find the maximum for the slice for the opIndex operation 573 private uint aMax(uint i) 574 { 575 uint retVal = (this.max < expectedSize && i < expectedSize) ? expectedSize : this.capacity; 576 577 if(i < this.min) 578 { 579 retVal = this.min; 580 } 581 else 582 { 583 if(i < this.max) 584 { 585 retVal = successor(i); 586 } 587 } 588 return retVal; 589 } 590 591 // find the minimum for the slice for the opIndex operation 592 private uint aMin(uint i) 593 { 594 uint retVal; 595 if(i >= this.min) 596 { 597 if(i >= this.max) 598 { 599 retVal = this.max; 600 } 601 else 602 { 603 if(this.member(i)) 604 retVal = i; 605 else 606 retVal = predecessor(i); 607 } 608 } 609 return retVal; 610 } 611 612 // TODO: implement some kind of cool output as a graphViz pic, similiar to the graphs in Cormen. 613 614 // bidirectional range also needs 615 @property Nullable!uint back() { return root.max; } 616 617 // this method removes the maximum element 618 void popBack() { if(!empty) remove(max); } 619 620 /** 621 This method returns the amount of elements currently present in the tree. 622 This is a draft version, as it uses the slice operator of the class. So getting this number has a complexity 623 proportional to n. As this functionaly is not seen as crucial, it is enough for the first time. 624 */ 625 @property uint length(){ return _elementCount; } 626 627 version(unittest) 628 { 629 // this member stores the provided input on initialization. This value could be used to hard prevent of adding 630 // elements between this value and the capacity of the tree. 631 @property auto elementCount(){ return this[].length; } 632 633 uint fill(uint m, Random rndGenInUse) 634 { 635 uint n; 636 for(uint i = 0; i < m; ++i) 637 { 638 uint x = uniform(0, expectedSize, rndGenInUse); 639 // the check for membership is done to add only on inserting to the counter, not just 640 // because visiting the the loop 641 if(!member(x)) 642 { 643 insert(x); 644 ++n; 645 } 646 } 647 return n; 648 } 649 650 uint fill(ref uint[] arr, Random rndGenInUse, uint fillPercentage = 31) 651 { 652 uint n; 653 while(n != fillPercentage * capacity/32) 654 { 655 uint x = uniform(0, cast(uint)(capacity - 1), rndGenInUse); 656 // the check for membership is done to add only on inserting to the counter, not just 657 // because visiting the the loop 658 if(!member(x)) 659 { 660 insert(x); 661 arr ~= x; 662 ++n; 663 } 664 665 } 666 assert(n == fillPercentage*capacity/32); 667 return n; 668 } 669 } 670 } 671 672 /// 673 version(unittest) 674 { 675 vebTree fill(uint M, Random rndGenInUse) 676 { 677 vebTree vT = new vebTree(M); 678 for(auto i = 0; i < 1000; i++) 679 { 680 uint x = uniform(0U, vT.expectedSize, rndGenInUse); 681 vT.insert(x); 682 } 683 return vT; 684 } 685 } 686 687 /// 688 unittest 689 { 690 vebTree vT = new vebTree(100); 691 vT.insert(2); 692 assert(vT.member(2)); 693 vebTree vT2 = vT.save(); 694 assert(vT2.member(2)); 695 vT2.insert(3); 696 assert(vT2.member(3)); 697 assert(!vT.member(3)); 698 } 699 700 /// 701 unittest 702 { 703 assert(!__traits(compiles, new vebTree())); 704 vebTree vT = new vebTree(1000); 705 assert(vT.capacity == 1024); 706 assert(vT.min.isNull); 707 708 vT.insert(2); 709 vT.insert(5); 710 assert(!vT.member(8)); 711 vT.insert(88); 712 vT.insert(8); 713 assert(vT.predecessor(75) == 8); 714 assert(vT.successor(6) == 8); 715 assert(!vT.member(1029)); 716 vT.insert(1029); 717 assert(!vT.member(1029)); 718 719 720 assert(!vT.member(865)); 721 vT.insert(865); 722 assert(vT.member(865)); 723 vT.insert(865); 724 assert(vT.member(865)); 725 assert(!vT.member(866)); 726 vT.remove(866); 727 assert(vT.member(865)); 728 vT.remove(865); 729 assert(!vT.member(865)); 730 } 731 732 /// 733 unittest 734 { 735 Random rndGenInUse; 736 uint currentSeed = 83843; // unpredictableSeed(); 737 rndGenInUse.seed(currentSeed); //initialize the random generator 738 uint M = uniform(0U,1 << 14, rndGenInUse); //set universe size to some integer. 739 //M = 30_000_000; 740 vebTree vT = new vebTree(M); //create the tree 741 assert(vT.capacity == nextPowerOfTwo(M)); 742 uint m = vT.fill(1000, rndGenInUse); //(try to) fill the tree with thousend values 743 uint n; 744 Nullable!uint i = vT.predecessor(vT.max)+1; 745 // discover the thousend (or little less) values with the predecessor method 746 while(!i.isNull) 747 { 748 ++n; 749 i = vT.predecessor(i); 750 } 751 752 // assert, that all members are discovered, iff when no predecessors are left 753 assert(n == m); 754 } 755 756 /// 757 unittest 758 { 759 Random rndGenInUse; 760 uint currentSeed = 433849; //unpredictableSeed(); 761 rndGenInUse.seed(currentSeed); //initialize the random generator 762 uint M = uniform(0U, 1 << 16, rndGenInUse); // set universe size to some integer. 763 vebTree vT = fill(M, rndGenInUse); //fill the tree with some values 764 Nullable!uint i = vT.max; 765 766 // remove all members beginning from the maximum 767 while(!i.isNull) 768 { 769 vT.remove(i); 770 auto j = vT.predecessor(i); 771 if(!j.isNull) 772 assert(j != i); 773 i = j; 774 } 775 776 // assert, that all members are removed, iff when no predecessors are left. 777 assert(vT.empty); 778 } 779 780 /// 781 unittest 782 { 783 Random rndGenInUse; 784 uint currentSeed = 438749; //unpredictableSeed(); 785 rndGenInUse.seed(currentSeed); //initialize the random generator 786 uint M = uniform(0U, 1 << 16, rndGenInUse); // set universe size to some integer. 787 vebTree vT = fill(M, rndGenInUse); //fill the tree with some values 788 Nullable!uint i = vT.min-1; 789 790 // remove all members beginning from the minimum 791 while(!i.isNull) 792 { 793 vT.remove(i); 794 auto j = vT.successor(i); 795 if(!j.isNull) 796 assert(j != i); 797 i = j; 798 } 799 800 // assert, that all members are removed, iff when no successors are left. 801 assert(vT.empty); 802 } 803 804 /// 805 unittest 806 { 807 uint M = 1 << 16; 808 vebTree vT = new vebTree(M); 809 vT.insert(0x000f); 810 assert(vT.predecessor(0x000f).isNull); 811 vT.insert(0x00f0); 812 assert(vT.predecessor(0x00f0) == 0x000f); 813 vT.insert(0x0f00); 814 assert(vT.predecessor(0x0f00) == 0x00f0); 815 vT.insert(0xf000); 816 assert(vT.predecessor(0xf000) == 0x0f00); 817 818 vT.remove(0xf000); 819 assert(vT.predecessor(0xf000) == 0x0f00); 820 vT.remove(0x0f00); 821 assert(vT.predecessor(0x0f00) == 0x00f0); 822 vT.remove(0x00f0); 823 assert(vT.predecessor(0x00f0) == 0x000f); 824 vT.remove(0x000f); 825 assert(vT.predecessor(0x000f).isNull); 826 } 827 828 /// 829 unittest 830 { 831 uint M = 1 << 16; 832 vebTree vT = new vebTree(M); 833 vT.insert(0xf000); 834 assert(vT.member(0xf000)); 835 vT.insert(0x0f00); 836 assert(vT.member(0x0f00)); 837 vT.insert(0x00f0); 838 assert(vT.member(0x00f0)); 839 vT.insert(0x000f); 840 assert(vT.member(0x000f)); 841 842 vT.remove(0xf000); 843 assert(!vT.member(0xf000)); 844 vT.remove(0x0f00); 845 assert(!vT.member(0x0f00)); 846 vT.remove(0x00f0); 847 assert(!vT.member(0x00f0)); 848 vT.remove(0x000f); 849 assert(!vT.member(0x000f)); 850 } 851 852 /// 853 unittest 854 { 855 Random rndGenInUse; 856 //stress test 857 uint currentSeed = 1948642567; //unpredictableSeed(); 858 rndGenInUse.seed(currentSeed); //initialize the random generator 859 // do not use more then "1 << 15", as for the red-black tree the insertion duration is almost 4 (!) minutes. 860 // last test says: inserting and removing with veb of 8.126.464 elements lasts: 19secs,217ms 861 uint M = uniform(0U, 1 << 16, rndGenInUse); // set universe size to some integer. 862 vebTree vT = new vebTree(M); 863 uint[] arr; 864 auto howMuchFilled = vT.fill(arr, rndGenInUse); 865 866 assert(arr.length == howMuchFilled); 867 868 vebTree vT2 = new vebTree(M); 869 870 assert(vT2.capacity == vT.capacity); 871 872 auto rbt = redBlackTree!uint(0); 873 rbt.clear; 874 875 void fill1() 876 { 877 foreach(uint i; arr) 878 { 879 vT2.insert(i); 880 } 881 /* 882 foreach(uint i; arr) 883 { 884 vT2.remove(i); 885 } 886 assert(vT2.empty); 887 */ 888 } 889 890 void fill2() 891 { 892 foreach(uint i; arr) 893 { 894 rbt.insert(i); 895 } 896 } 897 898 /* 899 import std.stdio; 900 writeln("howMuchFilled: ", howMuchFilled); 901 auto r = benchmark!(fill1, fill2)(1); 902 //auto r = benchmark!(fill1)(1); 903 auto f0Result = to!Duration(r[0]); 904 auto f1Result = to!Duration(r[1]); 905 writeln("VEB: ", f0Result); //10ms 906 writeln("rbt: ", f1Result); //40sec 907 assert(f0Result < f1Result); 908 //*/ 909 } 910 911 /// 912 unittest 913 { 914 Random rndGenInUse; 915 uint currentSeed = 1230394; //unpredictableSeed(); 916 rndGenInUse.seed(currentSeed); //initialize the random generator 917 uint M = uniform(0U, 1 << 16, rndGenInUse); // set universe size to some integer. 918 uint[] sourceArr; 919 sourceArr.length = M; 920 // generate a random array as the source for the tree 921 for(uint i = 0; i < M; i++) sourceArr[i] = uniform(0U, M, rndGenInUse); 922 // constructor to test 923 vebTree vT = new vebTree(sourceArr); 924 // make the array values unique. 925 auto uniqueArr = sort(sourceArr).uniq; 926 // check, that all values are filled 927 foreach(uint i; uniqueArr) 928 { 929 assert(vT.member(i)); 930 vT.remove(i); 931 } 932 // check, that no other elements are present. 933 assert(vT.empty); 934 } 935 936 /// 937 unittest 938 { 939 Random rndGenInUse; 940 uint currentSeed = 2349062; //unpredictableSeed(); 941 rndGenInUse.seed(currentSeed); //initialize the random generator 942 // do not use more then "1 << 15", as for the red-black tree the insertion duration is almost 4 (!) minutes. 943 uint M = uniform(0U, 1 << 16, rndGenInUse); // set universe size to some integer. 944 vebTree vT = new vebTree(M); 945 uint[] arr; 946 vT.fill(arr, rndGenInUse, 16); 947 948 assert(setSymmetricDifference(vT[], sort(arr)).empty); 949 } 950 951 /// 952 unittest 953 { 954 Random rndGenInUse; 955 uint currentSeed = 2309532090; //unpredictableSeed(); 956 rndGenInUse.seed(currentSeed); //initialize the random generator 957 // do not use more then "1 << 15", as for the red-black tree the insertion duration is almost 4 (!) minutes. 958 uint M = uniform(0U, 1 << 16, rndGenInUse); // set universe size to some integer. 959 vebTree vT = new vebTree(M); 960 uint[] arr; 961 vT.fill(arr, rndGenInUse, 16); 962 uint begin = 5; 963 uint end = 100; 964 auto filterRes = sort(arr).filter!(a => a >= begin && a < end); 965 assert(setSymmetricDifference(filterRes, vT[begin..end]).empty); 966 } 967 968 /// 969 unittest 970 { 971 Random rndGenInUse; 972 uint currentSeed = 1223409; //unpredictableSeed(); 973 rndGenInUse.seed(currentSeed); //initialize the random generator 974 // do not use more then "1 << 15", as for the red-black tree the insertion duration is almost 4 (!) minutes. 975 uint M = uniform(0U, 1 << 16, rndGenInUse); // set universe size to some integer. 976 vebTree vT = new vebTree(M); 977 uint[] arr; 978 vT.fill(arr, rndGenInUse, 16); 979 assert(vT.length == vT.elementCount); 980 } 981 982 /// 983 unittest 984 { 985 Random rndGenInUse; 986 uint currentSeed = 1435856534; //unpredictableSeed(); 987 rndGenInUse.seed(currentSeed); //initialize the random generator 988 // do not use more then "1 << 15", as for the red-black tree the insertion duration is almost 4 (!) minutes. 989 uint M = uniform(0U, 1 << 16, rndGenInUse); // set universe size to some integer. 990 vebTree vT = new vebTree(M); 991 uint[] arr; 992 vT.fill(arr, rndGenInUse, 16); 993 994 assert(!vT.member(0)); 995 assert(!vT.member(1)); 996 assert(startsWith(vT[1], 0)); 997 assert(vT.successor(vT[1][$-1]) == vT.successor(1)); 998 assert(startsWith(vT[vT.successor(1)],vT.min)); 999 assert(!vT.member(65535)); 1000 assert(vT[65535].array == [65534, 65535]); 1001 assert(vT.member(4)); 1002 assert(startsWith(vT[4],4)); 1003 assert(!vT.member(vT[4][$-1])); 1004 assert(!vT.member(5)); 1005 assert(startsWith(vT[5],4)); 1006 assert(vT[5][$-1] == vT[4][$-1]); 1007 } 1008 1009 /// 1010 unittest 1011 { 1012 assert(isBidirectionalRange!vebTree); 1013 } 1014 1015 /// 1016 unittest 1017 { 1018 vebTree vT = new vebTree(14); 1019 vT.insert(2); 1020 vT.insert(5); 1021 vT.insert(10); 1022 assert(vT[] == [2, 5, 10]); 1023 assert(vT[6].array == [5, 6, 7, 8, 9]); 1024 assert(vT[11].array == [10, 11, 12, 13]); 1025 assert(vT[-1].array == []); 1026 assert(vT[18].array == []); 1027 assert(vT[1].array == [0, 1]); 1028 assert(vT[15].array == [10, 11, 12, 13, 14, 15]); 1029 assert(vT[16].array == []); 1030 } 1031 1032 /// 1033 unittest 1034 { 1035 vebTree vT = new vebTree(15); 1036 vT.insert(2); 1037 auto testrange = vT[3]; 1038 assert(testrange.array == [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]); 1039 vT.insert(6); 1040 testrange = vT[3]; 1041 assert(testrange.array == [2, 3, 4, 5]); 1042 } 1043 1044 /// 1045 unittest 1046 { 1047 //another stress test 1048 Random rndGenInUse; 1049 auto currentSeed = 11351568; //unpredictableSeed(); 1050 rndGenInUse.seed(currentSeed); //initialize the random generator 1051 1052 void fill16(){ vebTree vT = new vebTree(1 << 16); } 1053 void fill17(){ vebTree vT = new vebTree(1 << 17); } 1054 void fill18(){ vebTree vT = new vebTree(1 << 18); } 1055 void fill19(){ vebTree vT = new vebTree(1 << 19); } 1056 void fill20(){ vebTree vT = new vebTree(1 << 20); } 1057 void fill21(){ vebTree vT = new vebTree(1 << 21); } 1058 void fill22(){ vebTree vT = new vebTree(1 << 22); } 1059 void fill23(){ vebTree vT = new vebTree(1 << 23); } 1060 void fill24(){ vebTree vT = new vebTree(1 << 24); } 1061 void fill25(){ vebTree vT = new vebTree(1 << 25); } 1062 1063 /* 1064 import std.stdio; 1065 auto r = benchmark!(fill16, fill17, fill18, fill19, fill20, fill21, fill22, fill23, fill24, fill25)(1); 1066 //auto r = benchmark!(fill1)(1); 1067 auto f16Result = to!Duration(r[0]); 1068 auto f17Result = to!Duration(r[1]); 1069 auto f18Result = to!Duration(r[2]); 1070 auto f19Result = to!Duration(r[3]); 1071 auto f20Result = to!Duration(r[4]); 1072 auto f21Result = to!Duration(r[5]); 1073 auto f22Result = to!Duration(r[6]); 1074 auto f23Result = to!Duration(r[7]); 1075 auto f24Result = to!Duration(r[8]); 1076 auto f25Result = to!Duration(r[9]); 1077 writeln("VEB with M of ", 1 << 16, ": ", f16Result); 1078 writeln("VEB with M of ", 1 << 17, ": ", f17Result); 1079 writeln("VEB with M of ", 1 << 18, ": ", f18Result); 1080 writeln("VEB with M of ", 1 << 19, ": ", f19Result); 1081 writeln("VEB with M of ", 1 << 20, ": ", f20Result); 1082 writeln("VEB with M of ", 1 << 21, ": ", f21Result); 1083 writeln("VEB with M of ", 1 << 22, ": ", f22Result); 1084 writeln("VEB with M of ", 1 << 23, ": ", f23Result); 1085 writeln("VEB with M of ", 1 << 24, ": ", f24Result); 1086 writeln("VEB with M of ", 1 << 25, ": ", f25Result); 1087 //*/ 1088 } 1089 1090 /// 1091 unittest 1092 { 1093 /* 1094 uint[] arr = [1, 2, 8, 2147483647]; 1095 auto vT = new vebTree(arr)); 1096 */ 1097 }