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 48 version(unittest) { import std.random; Random rndGenInUse; } 49 50 // this would be useful in case of coding the keys as a bitfield 51 // enum uint WORD = uint.sizeof * 8; 52 53 // defines the base universe size of a tree node. 54 ubyte BASE_SIZE = 2; 55 56 // Convinience function to return the ceiling to the next power of two number of the given input. 57 size_t nextPowerOfTwo(size_t value) { return 1 << (bsr(value) + 1); } 58 /// 59 unittest 60 { 61 assert(nextPowerOfTwo(1000) == 1024); 62 } 63 64 /** 65 This is the interface of a VEB tree. Besides the methods described below, the tree class implements the needed methods 66 for being a range. It is a bidirectional range with slice operations. 67 */ 68 interface Iveb 69 { 70 // 71 void insert(uint x); 72 // 73 void remove(uint x); 74 // 75 bool member(uint x); 76 // 77 Nullable!uint min(); 78 // 79 Nullable!uint max(); 80 // 81 Nullable!uint successor(uint x); 82 // 83 Nullable!uint predecessor(uint x); 84 } 85 86 /* 87 This function returns the upper square root of the given input as integer. It is needed in the initialization step of 88 the VEB tree to calculate the number of children of a given layer. The upper square root is defined by 2^{\lceil(\lg 89 u)/2\rceil} 90 */ 91 uint higherSquareRoot(size_t value){return 1 << (value.lowerSqrtShift + (value > (1 << value.bsr) || value.bsr & 1));} 92 /// 93 unittest 94 { 95 assert(higherSquareRoot(5) == 4); 96 assert(higherSquareRoot(17) == 8); 97 assert(higherSquareRoot(88) == 16); 98 assert(higherSquareRoot(64) == 8); 99 assert(higherSquareRoot(128) == 16); 100 } 101 102 /* 103 This function returns the floored log2 value of the input. This is done by counting up to the position of the leading 104 bit position and dividing this value by two. This method is needed both by the higher and lower square root 105 calculation. 106 */ 107 uint lowerSqrtShift(size_t value) { return bsr(value)/2; } 108 109 /* 110 This function returns the lower square root of the given input as integer. It is needed by the indexing functions 111 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} 112 */ 113 uint lowerSquareRoot(size_t value) { return 1 << lowerSqrtShift(value); } 114 /// 115 unittest 116 { 117 assert(lowerSquareRoot(5) == 2); 118 assert(lowerSquareRoot(17) == 4); 119 assert(lowerSquareRoot(88) == 8); 120 assert(lowerSquareRoot(64) == 8); 121 } 122 123 /* 124 This is an index function defined as \lfloor x/lowerSquareRoot(u)\rfloor. It is needed to find the appropriate cluster 125 of a element in the tree. 126 */ 127 uint high(uint value, size_t universeSize) { return value/lowerSquareRoot(universeSize); } 128 /// 129 unittest 130 { 131 assert(.high(7,16) == 1); 132 } 133 134 /* 135 This is an index function defined as fmod(value, lowerSquareRoot(universeSize)). It is needed to find the appropriate 136 value inside a cluster. 137 */ 138 uint low(uint value, size_t universeSize){ return value & (lowerSquareRoot(universeSize) - 1); } 139 /// 140 unittest 141 { 142 assert(.low(7,16) == 3); 143 } 144 145 /* 146 This is an index function to retain the searched value. It is defined as x * lowerSquareRoot(u) + y. Beyond this, the 147 relation holds: x = index(high(x), low(x)) 148 */ 149 uint index(size_t universeSize, uint x, uint y){ return (x * lowerSquareRoot(universeSize) + y); } 150 151 /** 152 This is the class to represent a VEB tree node. As memebers it contains the universeSize, the min and the max value as 153 well as a link to a summary node and a cluster, which is a range of VEB tree nodes of size higherSquareRoot(u). Each 154 child node has a universe size of lowerSquareRoot(u) 155 */ 156 private class vebNode 157 { 158 immutable size_t _universeSize; 159 @property size_t universeSize(){ return _universeSize; } 160 161 version(unittest){ size_t elementCount; } 162 163 // min value is contained in the node as a separate value, this value can't be found in child nodes. 164 Nullable!uint _min; 165 @property void min(uint value){ _min = value; } 166 @property Nullable!uint min() { return _min; } 167 168 // max value is contained in the node as a separate value, this can be found in child nodes. 169 Nullable!uint _max; 170 @property void max(uint value){ _max = value; } 171 @property Nullable!uint max(){ return _max; } 172 173 // the node is empty, iff neither min nor max is set. 174 @property bool empty() { return min.isNull; } 175 176 // VEB node containing the summary node. 177 private vebNode _summary; 178 // VEB cluster containing the child nodes. 179 private vebNode[] _cluster; 180 181 182 @disable this(); // initializing without providing a universe size is prohibited. 183 this(size_t universeSize) // ditto 184 { 185 this._universeSize = universeSize; 186 initializeChildren(universeSize); 187 } 188 189 // after constructing the node, construct every of its children. 190 private void initializeChildren(size_t universeSize) 191 { 192 if(universeSize > BASE_SIZE) 193 { 194 auto childUniverseSize = higherSquareRoot(universeSize); 195 _summary = new vebNode(childUniverseSize); 196 _cluster = new vebNode[childUniverseSize]; 197 foreach(ref vn; _cluster) 198 vn = new vebNode(lowerSquareRoot(universeSize)); 199 } 200 } 201 202 // this function inserts a value into an empty node. The difference between empty and non empty insert is, that the 203 // algorithm doesn't look into deepness of the tree, it just inserts the node to the separately stored min and max 204 // members. 205 private void emptyInsert(uint x) 206 { 207 min = x; 208 max = x; 209 } 210 211 // this function inserts a value into a generic node. If the member exists, no insertion will be done. 212 void insert(uint x) 213 { 214 // TODO: to check, how this could be checked in a better way. 215 if(member(x)) 216 return; 217 218 version(unittest){ elementCount++; } 219 220 if(this.empty) 221 emptyInsert(x); 222 else 223 { 224 if(x < min) 225 {//import std.algorithm; swap(min.get, x); 226 auto temp = x; x = min; min = temp; 227 } 228 229 if(universeSize > BASE_SIZE) 230 { 231 if(_cluster[high(x)].min.isNull) 232 { 233 _summary.insert(high(x)); 234 _cluster[high(x)].emptyInsert(low(x)); 235 } 236 else 237 _cluster[high(x)].insert(low(x)); 238 } 239 if(x > max) 240 max = x; 241 } 242 } 243 244 // this function removes a value from the tree. If the value doesn't exist in the tree nothing will be happen. 245 void remove(uint x) 246 { 247 // TODO: to check, how this could be checked in a better way. 248 if(!member(x)) 249 return; 250 251 version(unittest){ elementCount--; } 252 253 // case: there is only single element 254 if(min == max) 255 { 256 _min.nullify; 257 _max.nullify; 258 } 259 else if(BASE_SIZE == universeSize) 260 { 261 min = (x == 0) ? 1 : 0; 262 max = min; 263 } 264 else 265 { 266 if(x == min) 267 { 268 auto firstcluster = _summary.min; 269 x = index(firstcluster, _cluster[firstcluster].min); 270 min = x; 271 } 272 _cluster[high(x)].remove(low(x)); 273 if(_cluster[high(x)].min.isNull) 274 { 275 _summary.remove(high(x)); 276 if(x == max) 277 { 278 auto summarymax = _summary.max; 279 if(summarymax.isNull) 280 max = min; 281 else 282 max = index(summarymax, _cluster[summarymax].max); 283 } 284 } 285 else if(x == max) 286 max = index(high(x), _cluster[high(x)].max); 287 } 288 } 289 290 // this function returns the successor of the given value, even, if the value is not present in the tree. 291 // If the value is maximum or greater then the maximum of the tree null is returned. 292 Nullable!uint successor(uint x) 293 { 294 Nullable!uint result; 295 296 if(BASE_SIZE == universeSize) 297 { 298 if(x == 0 && max == 1) 299 result = 1; 300 } 301 else 302 { 303 if(!min.isNull && x < min) 304 result = min; 305 else 306 { 307 if(!max.isNull && x < max) 308 { 309 auto maxlow = _cluster[high(x)].max; 310 if(!maxlow.isNull && low(x) < maxlow) 311 { 312 auto offset = _cluster[high(x)].successor(low(x)); 313 result = index(high(x), offset); 314 } 315 else 316 { 317 auto succcluster = _summary.successor(high(x)); 318 if(!succcluster.isNull) 319 { 320 auto offset = _cluster[succcluster].min; 321 result = index(succcluster, offset); 322 } 323 } 324 } 325 } 326 } 327 return result; 328 } 329 330 // this function returns the predecessor of the given value, even, if the value is not present in the tree. 331 // if the value is the minimum or smaller then the minimum of the tree null is returned. 332 Nullable!uint predecessor(uint x) 333 { 334 Nullable!uint result; 335 if(BASE_SIZE == universeSize) 336 { 337 if(x == 1 && !min.isNull && min == 0) 338 result = 0; 339 } 340 else 341 { 342 if(!max.isNull && x > max) 343 result = max; 344 else 345 { 346 auto minlow = _cluster[high(x)].min; 347 if(!minlow.isNull && low(x) > minlow) 348 { 349 auto offset = _cluster[high(x)].predecessor(low(x)); 350 result = index(high(x), offset); 351 } 352 else 353 { 354 auto predcluster = _summary.predecessor(high(x)); 355 if(predcluster.isNull) 356 { 357 if(!min.isNull && x > min) 358 result = min; 359 } 360 else 361 { 362 auto offset = _cluster[predcluster].max; 363 result = index(predcluster, offset); 364 } 365 } 366 } 367 } 368 return result; 369 } 370 371 // This function returns whether the input key is currently member of the tree. 372 bool member(uint x) 373 { 374 bool returnVal; 375 376 if(x < universeSize) 377 { 378 if(!min.isNull && (min == x || max == x)) 379 returnVal = true; 380 else 381 { 382 if(BASE_SIZE != universeSize) 383 returnVal = _cluster[high(x)].member(low(x)); 384 } 385 } 386 return returnVal; 387 } 388 389 // this function is an concretization of the module wide indexing function 390 uint index(uint x, uint y){ return .index(universeSize, x, y); } 391 392 // this function is an concretization of the module wide indexing function 393 uint high(uint x){ return .high(x, universeSize); } 394 395 // this function is an concretization of the module wide indexing function 396 uint low(uint x){ return .low(x, universeSize); } 397 } 398 399 /** 400 This class represents the VEB tree itself. For the sake of convinience it saves the provided at the initialization step 401 wished maximum element. However at the point of development it is only used for testing. Beyond this, it stores only 402 the reference to the root element, as the theory tells. 403 */ 404 class vebTree : Iveb 405 { 406 // the root element of the tree. 407 private vebNode root; 408 409 /// default constructor of a VEB tree is disabled. 410 @disable this(); 411 /// to construct a VEB tree one should provide the maximum element one wish to be able to store. 412 this(uint maximumElement) 413 { 414 root = new vebNode(nextPowerOfTwo(maximumElement)); 415 416 version(unittest){ _maximumElement = maximumElement; } 417 } 418 419 /// another possibility is to construct a VEB tree by providing an array. 420 this(uint[] range) 421 { 422 import std.algorithm.comparison; 423 import std.algorithm.iteration; 424 // first, we have to determine the size of the tree. 425 // it is either derived from the length of the given tree or its greatest element 426 size_t limit = max(range.length, reduce!(max)(range)); 427 428 // without std.algorithm.iteration: 429 // size_t limit = range.length; 430 // foreach(uint i; range) limit = max(limit, i); 431 432 root = new vebNode(nextPowerOfTwo(limit)); 433 foreach(uint i; range) root.insert(i); 434 } 435 436 /** 437 this method returns the capacity of the tree. It is equal to the next power of two with regard to the maximum 438 element 439 */ 440 size_t capacity(){ return root.universeSize; } 441 442 /// this method is used to add an element to the tree. duplicate values will be ignored. 443 void insert(uint x){ if(x < capacity) root.insert(x); } 444 445 /// this method is used to remove elements from the tree. not existing values will be ignored. 446 void remove(uint x){ root.remove(x); } 447 448 /// this method is used to determine, whether an element is currently present in the tree 449 bool member(uint x){ return root.member(x); } 450 451 /// this method is used to determine the minimum of the tree 452 @property Nullable!uint min(){ return front; } 453 454 /// this method is used to determine the maximum of the tree 455 @property Nullable!uint max(){ return back; } 456 457 /// this method retrieves the successor of the given input. 458 Nullable!uint successor(uint x){ return root.successor(x); } 459 460 /// this method retrieves the predecessor of the given input. 461 Nullable!uint predecessor(uint x){ return root.predecessor(x); } 462 463 // this method is used to determine, whether the tree is currently containing an element. 464 @property bool empty(){ return root.empty; } 465 466 // this method returns the minimium. 467 @property Nullable!uint front(){ return root.min; } 468 469 // this method removes the minimum element 470 void popFront(){ if(!empty) root.remove(min); } 471 472 // forward range also needs save. This is a draft version of the save function, it uses the opslice of the class to 473 // construct a new one via an array 474 @property vebTree save(){ return new vebTree(this[]); } 475 476 /** 477 opSlice operator to get the underlying array. 478 This is a draft version, as it uses the successor method of the class. So getting the underlying array is 479 proportional to n. As this functionaly is not seen as crucial, it is enough for the first time. 480 */ 481 uint[] opSlice() 482 { 483 uint[] retArray; 484 if(!min.isNull) 485 { 486 retArray ~= min; 487 if(min != max) 488 { 489 retArray.reserve(capacity); 490 while(retArray[$-1] != max) 491 retArray ~= successor(retArray[$-1]); 492 } 493 } 494 return retArray; 495 } 496 497 /** 498 opSlice operator to get the underlying array between given bounds. 499 This is a draft version, as it uses the successor method of the class. So getting the underlying array is 500 proportional to min(n, m), where n is the number of elements between bounds and m are the number of present 501 elements in the tree. 502 */ 503 uint[] opSlice(uint begin, uint end) 504 { 505 uint[] retArray; 506 if(begin < end && begin < max) 507 { 508 if(!min.isNull) 509 { 510 if(this.member(begin)) 511 retArray ~= begin; 512 else 513 { 514 uint i = successor(begin); 515 if(i < end) 516 retArray ~= i; 517 } 518 if(min != max) 519 { 520 import std.algorithm.comparison; 521 uint limit = min(end, this.max); 522 523 retArray.reserve(limit-begin); 524 uint i = successor(retArray[$-1]); 525 while(i < limit) 526 { 527 retArray ~= i; 528 i = successor(retArray[$-1]); 529 } 530 } 531 } 532 } 533 return retArray; 534 } 535 536 /** 537 This is a nontrivial opIndex operator on indizies of the tree. Given an index a range (!) is returned, which is, 538 either the range of elements in the tree build up by [predecessor(i) .. successor(i)] (i. e. excluding the 539 successor(i)), when the given index is not set. Or, if the given index is set, [member(i), successor(i)]. If an 540 index out of bounds is given, an empty array is returned. The tree must not be empty to use this function. 541 */ 542 uint[] opIndex(uint i) 543 { 544 import std.algorithm; 545 import std.range; 546 import std.exception; 547 enforce(!this.empty); 548 549 uint[] retArr; 550 if(i < this.capacity) 551 { 552 uint absoluteMin; 553 uint absoluteMax; 554 555 if(i < this.min) 556 { 557 558 absoluteMin = 0; 559 absoluteMax = this.min; 560 } 561 else 562 { 563 if(i >= this.max) 564 { 565 absoluteMax = cast(uint)(capacity - 1); 566 absoluteMin = this.max; 567 } 568 else 569 { 570 absoluteMax = successor(i); 571 if(this.member(i)) 572 absoluteMin = i; 573 else 574 absoluteMin = predecessor(i); 575 } 576 } 577 retArr = iota(absoluteMin, absoluteMax).array; 578 if(i >= this.max) 579 retArr ~= absoluteMax; 580 } 581 return retArr; 582 } 583 584 // TODO: implement some kind of cool output as a graphViz pic, similiar to the graphs in Cormen. 585 586 // bidirectional range also needs 587 @property Nullable!uint back() { return root.max; } 588 589 // this method removes the maximum element 590 void popBack() { if(!empty) root.remove(max); } 591 592 /** 593 This method returns the amount of elements currently present in the tree. 594 This is a draft version, as it uses the slice operator of the class. So getting this number has a complexity 595 proportional to n. As this functionaly is not seen as crucial, it is enough for the first time. 596 */ 597 @property size_t elementCount(){ return this[].length; } 598 599 version(unittest) 600 { 601 // this member stores the provided input on initialization. This value could be used to hard prevent of adding 602 // elements between this value and the capacity of the tree. 603 private uint _maximumElement; 604 @property size_t elementCount_ut(){ return root.elementCount; } 605 606 uint fill(uint m) 607 { 608 uint n; 609 for(uint i = 0; i < m; ++i) 610 { 611 uint x = uniform(0, _maximumElement+1, rndGenInUse); 612 // the check for membership is done to add only on inserting to the counter, not just 613 // because visiting the the loop 614 if(!member(x)) 615 { 616 insert(x); 617 ++n; 618 } 619 } 620 return n; 621 } 622 623 uint fill(ref uint[] arr, uint fillPercentage = 31) 624 { 625 uint n; 626 while(n != fillPercentage * capacity/32) 627 { 628 uint x = uniform(0, cast(uint)(capacity - 1), rndGenInUse); 629 // the check for membership is done to add only on inserting to the counter, not just 630 // because visiting the the loop 631 if(!member(x)) 632 { 633 insert(x); 634 arr ~= x; 635 ++n; 636 } 637 638 } 639 assert(n == fillPercentage*capacity/32); 640 return n; 641 } 642 } 643 } 644 645 /// 646 unittest 647 { 648 assert(!__traits(compiles, new vebTree())); 649 vebTree vT = new vebTree(1000); 650 assert(vT.capacity == 1024); 651 assert(vT.min.isNull); 652 653 vT.insert(2); 654 vT.insert(5); 655 assert(!vT.member(8)); 656 vT.insert(88); 657 vT.insert(8); 658 assert(vT.predecessor(75) == 8); 659 assert(vT.successor(6) == 8); 660 assert(!vT.member(1029)); 661 vT.insert(1029); 662 assert(!vT.member(1029)); 663 664 665 assert(!vT.member(865)); 666 vT.insert(865); 667 assert(vT.member(865)); 668 vT.insert(865); 669 assert(vT.member(865)); 670 assert(!vT.member(866)); 671 vT.remove(866); 672 assert(vT.member(865)); 673 vT.remove(865); 674 assert(!vT.member(865)); 675 } 676 677 /// 678 unittest 679 { 680 uint currentSeed = 83843; // unpredictableSeed(); 681 rndGenInUse.seed(currentSeed); //initialize the random generator 682 uint M = uniform(0U,1 << 14, rndGenInUse); //set universe size to some integer. 683 //M = 30_000_000; 684 vebTree vT = new vebTree(M); //create the tree 685 assert(vT.capacity == nextPowerOfTwo(M)); 686 uint m = vT.fill(1000); //(try to) fill the tree with thousend values 687 uint n; 688 Nullable!uint i = vT.predecessor(vT.max)+1; 689 // discover the thousend (or little less) values with the predecessor method 690 while(!i.isNull) 691 { 692 ++n; 693 i = vT.predecessor(i); 694 } 695 // assert, that all members are discovered, iff when no predecessors are left 696 assert(n == m); 697 } 698 699 /// 700 version(unittest) 701 { 702 /// 703 vebTree fill(uint M) 704 { 705 vebTree vT = new vebTree(M); 706 for(auto i = 0; i < 1000; i++) 707 { 708 uint x = uniform(0U, vT._maximumElement, rndGenInUse); 709 vT.insert(x); 710 } 711 return vT; 712 } 713 } 714 715 /// 716 unittest 717 { 718 uint currentSeed = 433849; //unpredictableSeed(); 719 rndGenInUse.seed(currentSeed); //initialize the random generator 720 uint M = uniform(0U, 1 << 16, rndGenInUse); // set universe size to some integer. 721 vebTree vT = fill(M); //fill the tree with some values 722 Nullable!uint i = vT.max; 723 724 // remove all members beginning from the maximum 725 while(!i.isNull) 726 { 727 vT.remove(i); 728 auto j = vT.predecessor(i); 729 if(!j.isNull) 730 assert(j != i); 731 i = j; 732 } 733 // assert, that all members are removed, iff when no predecessors are left. 734 assert(vT.empty); 735 } 736 737 /// 738 unittest 739 { 740 uint currentSeed = 438749; //unpredictableSeed(); 741 rndGenInUse.seed(currentSeed); //initialize the random generator 742 uint M = uniform(0U, 1 << 16, rndGenInUse); // set universe size to some integer. 743 vebTree vT = fill(M); //fill the tree with some values 744 Nullable!uint i = vT.min-1; 745 746 // remove all members beginning from the minimum 747 while(!i.isNull) 748 { 749 vT.remove(i); 750 auto j = vT.successor(i); 751 if(!j.isNull) 752 assert(j != i); 753 i = j; 754 } 755 // assert, that all members are removed, iff when no successors are left. 756 assert(vT.empty); 757 } 758 759 /// 760 unittest 761 { 762 uint M = 1 << 16; 763 vebTree vT = new vebTree(M); 764 vT.insert(0x000f); 765 assert(vT.predecessor(0x000f).isNull); 766 vT.insert(0x00f0); 767 assert(vT.predecessor(0x00f0) == 0x000f); 768 vT.insert(0x0f00); 769 assert(vT.predecessor(0x0f00) == 0x00f0); 770 vT.insert(0xf000); 771 assert(vT.predecessor(0xf000) == 0x0f00); 772 773 vT.remove(0xf000); 774 assert(vT.predecessor(0xf000) == 0x0f00); 775 vT.remove(0x0f00); 776 assert(vT.predecessor(0x0f00) == 0x00f0); 777 vT.remove(0x00f0); 778 assert(vT.predecessor(0x00f0) == 0x000f); 779 vT.remove(0x000f); 780 assert(vT.predecessor(0x000f).isNull); 781 } 782 783 /// 784 unittest 785 { 786 uint M = 1 << 16; 787 vebTree vT = new vebTree(M); 788 vT.insert(0xf000); 789 assert(vT.member(0xf000)); 790 vT.insert(0x0f00); 791 assert(vT.member(0x0f00)); 792 vT.insert(0x00f0); 793 assert(vT.member(0x00f0)); 794 vT.insert(0x000f); 795 assert(vT.member(0x000f)); 796 797 vT.remove(0xf000); 798 assert(!vT.member(0xf000)); 799 vT.remove(0x0f00); 800 assert(!vT.member(0x0f00)); 801 vT.remove(0x00f0); 802 assert(!vT.member(0x00f0)); 803 vT.remove(0x000f); 804 assert(!vT.member(0x000f)); 805 } 806 807 /// 808 unittest 809 { 810 //stress test 811 uint currentSeed = 1948642567; //unpredictableSeed(); 812 rndGenInUse.seed(currentSeed); //initialize the random generator 813 // do not use more then "1 << 15", as for the red-black tree the insertion duration is almost 4 (!) minutes. 814 uint M = uniform(0U, 1 << 14, rndGenInUse); // set universe size to some integer. 815 vebTree vT = new vebTree(M); 816 uint[] arr; 817 auto howMuchFilled = vT.fill(arr); 818 assert(arr.length == howMuchFilled); 819 820 vebTree vT2 = new vebTree(M); 821 assert(vT2.capacity == vT.capacity); 822 823 import std.datetime; import std.conv : to; 824 import std.container; 825 auto rbt = redBlackTree!uint(0); 826 rbt.clear; 827 828 void fill1() 829 { 830 foreach(uint i; arr) 831 { 832 vT2.insert(i); 833 } 834 } 835 836 void fill2() 837 { 838 foreach(uint i; arr) 839 { 840 rbt.insert(i); 841 } 842 } 843 844 /* 845 import std.stdio; 846 writeln("howMuchFilled: ", howMuchFilled); 847 auto r = benchmark!(fill1, fill2)(1); 848 auto f0Result = to!Duration(r[0]); 849 auto f1Result = to!Duration(r[1]); 850 writeln("VEB: ", f0Result); 851 writeln("rbt: ", f1Result); 852 assert(f0Result < f1Result); 853 */ 854 } 855 856 /// 857 unittest 858 { 859 import std.algorithm; 860 uint currentSeed = 1230394; //unpredictableSeed(); 861 rndGenInUse.seed(currentSeed); //initialize the random generator 862 uint M = uniform(0U, 1 << 16, rndGenInUse); // set universe size to some integer. 863 uint[] sourceArr; 864 sourceArr.length = M; 865 // generate a random array as the source for the tree 866 for(uint i = 0; i < M; i++) sourceArr[i] = uniform(0U, M, rndGenInUse); 867 868 // constructor to test 869 vebTree vT = new vebTree(sourceArr); 870 871 // make the array values unique. 872 auto uniqueArr = sort(sourceArr).uniq; 873 // check, that all values are filled 874 foreach(uint i; uniqueArr) 875 { 876 assert(vT.member(i)); 877 vT.remove(i); 878 } 879 // check, that no other elements are present. 880 assert(vT.empty); 881 } 882 883 /// 884 unittest 885 { 886 uint currentSeed = 2349062; //unpredictableSeed(); 887 rndGenInUse.seed(currentSeed); //initialize the random generator 888 // do not use more then "1 << 15", as for the red-black tree the insertion duration is almost 4 (!) minutes. 889 uint M = uniform(0U, 1 << 16, rndGenInUse); // set universe size to some integer. 890 vebTree vT = new vebTree(M); 891 uint[] arr; 892 vT.fill(arr, 16); 893 import std.algorithm; 894 assert(setSymmetricDifference(vT[], sort(arr)).empty); 895 } 896 897 /// 898 unittest 899 { 900 uint currentSeed = 2309532090; //unpredictableSeed(); 901 rndGenInUse.seed(currentSeed); //initialize the random generator 902 // do not use more then "1 << 15", as for the red-black tree the insertion duration is almost 4 (!) minutes. 903 uint M = uniform(0U, 1 << 16, rndGenInUse); // set universe size to some integer. 904 vebTree vT = new vebTree(M); 905 uint[] arr; 906 vT.fill(arr, 16); 907 908 uint begin = 5; 909 uint end = 100; 910 911 import std.algorithm; 912 auto filterRes = sort(arr).filter!(a => a >= begin && a < end); 913 assert(setSymmetricDifference(filterRes, vT[begin..end]).empty); 914 } 915 916 /// 917 unittest 918 { 919 uint currentSeed = 1223409; //unpredictableSeed(); 920 rndGenInUse.seed(currentSeed); //initialize the random generator 921 // do not use more then "1 << 15", as for the red-black tree the insertion duration is almost 4 (!) minutes. 922 uint M = uniform(0U, 1 << 16, rndGenInUse); // set universe size to some integer. 923 vebTree vT = new vebTree(M); 924 uint[] arr; 925 vT.fill(arr, 16); 926 assert(vT.elementCount == vT.elementCount_ut); 927 } 928 929 /// 930 unittest 931 { 932 uint currentSeed = 1435856534; //unpredictableSeed(); 933 rndGenInUse.seed(currentSeed); //initialize the random generator 934 // do not use more then "1 << 15", as for the red-black tree the insertion duration is almost 4 (!) minutes. 935 uint M = uniform(0U, 1 << 16, rndGenInUse); // set universe size to some integer. 936 vebTree vT = new vebTree(M); 937 uint[] arr; 938 vT.fill(arr, 16); 939 940 assert(!vT.member(0)); 941 assert(!vT.member(1)); 942 import std.algorithm; 943 assert(startsWith(vT[1], 0)); 944 assert(vT.successor(vT[1][$-1]) == vT.successor(1)); 945 assert(startsWith(vT[vT.successor(1)],vT.min)); 946 assert(!vT.member(65535)); 947 assert(vT[65535] == [65534, 65535]); 948 assert(vT.member(4)); 949 assert(startsWith(vT[4],4)); 950 assert(!vT.member(vT[4][$-1])); 951 assert(!vT.member(5)); 952 assert(startsWith(vT[5],4)); 953 assert(vT[5][$-1] == vT[4][$-1]); 954 } 955 956 unittest 957 { 958 import std.range; 959 assert(isBidirectionalRange!vebTree); 960 }