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 The module is still a work in progress. So, if you find an error by chance, please let me know in any way. 10 The main idea of the container is, to restrict the capacity of the tree by the next power of two universe size, 11 given an arbitrary size at the initialization. As long as the usage is intended to contains keys, as in the 12 current version, this restriction is not only a restriction of the amount of elements but also on the contained 13 element values. 14 For optimization purposes, the size limit is size_t.max/2 + 1. The tree was tested on 64- and 32-bit arch. 15 So the largest element which can be stored is 4.294.967.295 on a 64-bit architecture. 16 */ 17 18 // (optional) todo: provide functionality to contain non-unique keys, i. e. exercise 20.3.1 from Cormen 19 20 /** 21 The library is supposed to contain a tree on keys only, for the data could be managed outside. Then, there could be 22 a simple method to combine the keys and the data together. 23 */ 24 25 /** 26 The main advantage of the Van Emde Boas tree appears on a large amount of elements, as the provided standard 27 operations of the tree are constant in time and of order O(lg2(lg2(U))), where U is the capacity of the tree. For 28 small amount of elements the overhead coming along with the structure take over. For example, for a universe size of 29 2^14 and 15872 insertion operatios the duration for the Van Emde Boas tree is about 1*10^(-3) times smaller. As one 30 of the unittests shows. 31 */ 32 33 /** 34 Be aware, the current container is intended to be used with keys. This implies, that the capacity, fixed on its 35 initialization has two meanings. As usual, it shows the maximum amount of elements the instanciated tree can keep. 36 But also, it states, that no value bigger then capacity - 1 exists in the tree. This, and the fact, that only 37 non-negative values can be used are infered from the term "key". 38 */ 39 40 /** 41 See_also: Thomas H. Cormen, Clifford Stein, Ronald L. Rivest, and Charles E. Leiserson. 2001. <em>Introduction to 42 Algorithms</em> (2nd ed.). McGraw-Hill Higher Education. 43 */ 44 45 /** 46 As an important optimization a bottom out technique is used to compactify the tree to the level of nodes, where bit 47 operations become possible. As a side effect, the optimization now relies on the underlying architecture. This leads 48 to the fact, that the maximum of elements which can be stored is 49 2^16 on a 32-bit architecture and 50 2^32 on a 64-bit architecture. 51 This was intentionally chosen for two reasons: 52 i) to keep the size of a single node also depending from the underlying architecture. 53 ii) for bitoperations, which the tree is relying on, to use the native word size of the architecture without 54 emulating bigger entities. 55 */ 56 57 module vebtree; 58 59 import std.typecons; // used for Nullable 60 import core.bitop; // used for bit operations 61 import std.bitmanip; // used for bitfields 62 import std.traits; // used for generating the tree given an iterable 63 import std.range; 64 import std.math : nextPow2; 65 import core.stdc.limits : CHAR_BIT; 66 67 private enum vdebug = false; 68 69 version(unittest) 70 { 71 static if(vdebug){import std.stdio;} 72 import std.algorithm; 73 import std.random; 74 import std.datetime.stopwatch; 75 import std.conv : to; 76 import std.container; // red black tree may be used in unittests for comparison. 77 import std.math : sqrt; 78 size_t[] powersOfTwo = iota(0, CHAR_BIT * size_t.sizeof).map!(a => size_t(1) << a).array; 79 Random rndGenInUse; 80 81 // helping function for output a given value in binary representation 82 static if(vdebug) 83 { 84 void bin(size_t n) 85 { 86 /* step 1 */ 87 if (n > 1) bin(n/2); 88 /* step 2 */ 89 write(n % 2); 90 } 91 } 92 93 // during tests it is ok a tree with a random capacity not going up to the maximum one. 94 enum testedSize = 1 << 2 * size_t.sizeof; //3 * size_t.sizeof; 95 // during tests helping static arrays are used, which have an absolute maximum of size_t.sizeof * 2^20 elements 96 enum allowedArraySize = 1 << size_t.sizeof; //(2 * size_t.sizeof + size_t.sizeof/2); 97 // choosed arbitrary, to avoid seg. faults 98 99 // some different filling functions for the tree. This simply tries to fill the tree with random numbers. Duplicates 100 // will be ignored, the given tree is modified. 101 auto fill(ref VEBtree vT, size_t m, Random rndGenInUse) 102 { 103 size_t n; 104 for(size_t i; i < m; ++i) 105 { 106 auto x = uniform(0, vT.expectedSize, rndGenInUse); 107 // the check for membership is done to add only on inserting to the counter, not just 108 // because visiting the the loop 109 if(!(x in vT)) 110 { 111 vT.insert(x); 112 ++n; 113 } 114 } 115 return n; 116 } 117 118 // Ditto. This method asserts, that a given filling percentage is achieved. 119 auto fill(ref VEBtree vT, ref size_t[] arr, Random rndGenInUse, size_t fillPercentage = 31) 120 { 121 size_t n; 122 arr.length = fillPercentage * vT.capacity/32; 123 while(n != fillPercentage * vT.capacity/32) 124 { 125 auto x = uniform(0, vT.capacity - 1, rndGenInUse); 126 // the check for membership is done to add only on inserting to the counter, not just 127 // because visiting the the loop 128 if(!(x in vT)) 129 { 130 vT.insert(x); 131 arr[n] = x; 132 ++n; 133 } 134 } 135 assert(n == fillPercentage*vT.capacity/32); 136 return n; 137 } 138 } 139 140 /** 141 the baseSize defines the cutoff limit, where the node goes into the bit array mode. It is parametrized on the size 142 of size_t and changes dynamically with the architecture used. 143 */ 144 enum baseSize = CHAR_BIT * size_t.sizeof; 145 146 /** 147 the maxSizeBound defines the maximum the tree can be constructed with. It is parametrized on the size of size_t and 148 changes dynamically with the architecture used. 149 */ 150 enum maxSizeBound = (size_t(1) << baseSize/2) + 1; 151 152 /** 153 This function returns the higher square root of the given input. It is needed in the initialization step 154 of the VEB tree to calculate the number of children of a given layer. And this is the universe size of the 155 summary of a node. The upper square root is defined by 2^{\lceil(\lg u)/2\rceil} 156 */ 157 @nogc nothrow size_t hSR(size_t value) 158 { 159 return size_t(1) << (bsr(value)/2 + ((bsr(value) & 1) || ((value != 0) && (value & (value - 1))))); 160 } 161 /// 162 unittest 163 { 164 const size_t currentSeed = unpredictableSeed(); 165 static if(vdebug){write("UT: hSR. "); writeln("seed: ", currentSeed);} 166 rndGenInUse.seed(currentSeed); //initialize the random generator 167 const size_t M = uniform(1U,size_t.max/2, rndGenInUse); //set universe size to some integer. 168 auto hSR = hSR(M); 169 170 assert((hSR & (hSR - 1)) == 0); 171 if(hSR < uint.max) assert(hSR >= sqrt(to!float(M))); 172 173 const auto check = powersOfTwo.until(hSR).array; 174 assert((check[$-1]) * (check[$-1]) < M); 175 } 176 177 /** 178 This function returns the lower square root of the given input. It is needed by the indexing functions 179 high(x), low(x) and index(x,y) of elements in the tree. Also, this is the universe size of a child of a node. The 180 lower square root is defined by 2^{\lfloor(\lgu)/2\rfloor} 181 */ 182 @nogc nothrow size_t lSR(size_t value) { return size_t(1) << (bsr(value)/2); } 183 /// 184 unittest 185 { 186 const size_t currentSeed = unpredictableSeed(); 187 static if(vdebug){write("UT: lSR. "); writeln("seed: ", currentSeed);} 188 rndGenInUse.seed(currentSeed); //initialize the random generator 189 const size_t M = uniform(1U,size_t.max/2, rndGenInUse); //set universe size to some integer. 190 auto lSR = lSR(M); 191 192 assert((lSR & (lSR - 1)) == 0); 193 assert(lSR * lSR < M); 194 const auto check = powersOfTwo.find(lSR).array; 195 196 if(lSR < size_t.max/2) assert((check[1]) > sqrt(to!float(M))); 197 } 198 199 /* 200 This is an index function defined as \lfloor x/lSR(u)\rfloor. It is needed to find the appropriate cluster 201 of a element in the tree. It is a part of the ideal indexing function. 202 */ 203 private @nogc nothrow size_t high(size_t value, size_t uS) { return value >> (bsr(uS) / 2); } 204 // 205 unittest 206 { 207 const size_t currentSeed = unpredictableSeed(); 208 static if(vdebug){write("UT: high. "); writeln("seed: ", currentSeed);} 209 rndGenInUse.seed(currentSeed); //initialize the random generator 210 const size_t M = uniform(1U,size_t.max/2, rndGenInUse); //set universe size to some integer. 211 const size_t U = nextPow2(M); 212 const size_t x = uniform(0U, U, rndGenInUse); 213 214 assert(high(x,U) == x / lSR(U)); 215 } 216 217 /* 218 This is an index function defined as fmod(value, lSR(uS)). It is needed to find the appropriate 219 value inside a cluster. It is part of the ideal indexing function 220 */ 221 private @nogc nothrow size_t low(size_t value, size_t uS) { return value & ((size_t(1) << (bsr(uS) / 2)) - 1); } 222 // 223 unittest 224 { 225 const size_t currentSeed = unpredictableSeed(); 226 static if(vdebug){write("UT: low. "); writeln("seed: ", currentSeed);} 227 rndGenInUse.seed(currentSeed); //initialize the random generator 228 const size_t M = uniform(1U,size_t.max/2, rndGenInUse); //set universe size to some integer. 229 const size_t U = nextPow2(M); 230 const size_t x = uniform(0U, U, rndGenInUse); 231 232 assert(low(x, U) == (x & (lSR(U) - 1))); 233 } 234 235 /* 236 This is an index function to retain the searched value. It is defined as x * lSR(u) + y. Beyond this, the 237 relation holds: x = index(high(x), low(x)). This is the ideal indexing function of the tree. 238 */ 239 private @nogc nothrow size_t index(size_t uS, size_t x, size_t y){ return (x * lSR(uS) + y); } 240 // 241 unittest 242 { 243 const size_t currentSeed = unpredictableSeed(); 244 static if(vdebug){write("UT: index. "); writeln("seed: ", currentSeed);} 245 rndGenInUse.seed(currentSeed); //initialize the random generator 246 const size_t M = uniform(0U,size_t.max/2, rndGenInUse); //set universe size to some integer. 247 248 const size_t U = nextPow2(M); 249 const size_t x = uniform(0U, U, rndGenInUse); 250 251 assert(index(U, high(x, U), low(x, U)) == x); 252 } 253 254 /** 255 This is the struct to represent a VEB tree node. As memebers it contains a value and a pointer to the children 256 array. As the pointer does not know the size of the array, it has to be passed in all methods, which require an 257 access to it. 258 Dependent from the (universe) size passed in a method the stored value will be interpretated in two different ways: 259 If the parameter passed shows, that the universe size is below the bit size of the stored value, then, it can be 260 handled as a bit array and all operations decay to operations defined in core.bitop. This case correspond to 261 the state of the node being a leaf. No children exist and the pointer should stay uninitialized 262 Otherwise, the value is interpretated as two values, the minimum and maximum stored at the same place in memory. The 263 minimum and maximum shows, which range is achievable through this node. The parameters passed to a function of the 264 node have to be well chosen, to hit the appropriate child. 265 The first element of the children array, if present is handled different. According to literature, it has the role 266 of the summary of the remaining children cluster. With it help it is possible to achieve a very small recursion 267 level during an access. 268 */ 269 struct VEBnode 270 { 271 /* 272 This pointer acts as an array to nodes like this one. As the universe size is not provided, the length of the 273 array can not be used to calculate the most of operations, so no explicit array is needed. The only remaining 274 property is whether the pointer is set at all. From this property the node can decide, whether it is a leaf or 275 an intermediate node. 276 */ 277 private VEBnode* ptrArr; // the first member behaves different, as the others, as it is the summary node. 278 279 /// property returning the summary node 280 @nogc nothrow @property ref inout(VEBnode) summary() inout 281 in { assert(!isLeaf); } 282 body { return ptrArr[0]; } 283 unittest 284 { 285 auto currentSeed = unpredictableSeed(); // 83_843; 898_797_859; 286 static if(vdebug){write("UT: vN, summary. "); writeln("seed: ", currentSeed);} 287 rndGenInUse.seed(currentSeed); //initialize the random generator 288 289 VEBnode vN = VEBnode(512); 290 assert(!vN.isLeaf); 291 vN.ptrArr[0]._val = 73; 292 assert(vN.summary._val == 73); 293 } 294 295 /// property returning the cluster array 296 @nogc nothrow @property inout(VEBnode*) cluster() inout 297 in { assert(!isLeaf); } 298 body { return (ptrArr + 1); } 299 unittest 300 { 301 auto currentSeed = unpredictableSeed(); // 83_843; 898_797_859; 302 static if(vdebug){write("UT: vN, cluster. "); writeln("seed: ", currentSeed);} 303 rndGenInUse.seed(currentSeed); //initialize the random generator 304 305 const auto value = uniform!"[]"(2U, testedSize, rndGenInUse); 306 const auto place = uniform(0U,baseSize, rndGenInUse); 307 308 VEBnode vN = VEBnode(4096); 309 vN.ptrArr[place]._val = value; 310 assert(vN.ptrArr[place]._val == value); 311 assert(vN.cluster[place - 1]._val == value); // the right place: it is the (place - 1) location of clusters 312 vN.cluster[place]._val = 73; 313 } 314 315 /* 316 An unnamed union defines the essential behavior of the node. It is either a real node with a min and a max, or 317 it is a ulong which is handled as a bit array. 318 */ 319 union 320 { 321 /* 322 as the underlying type size_t is chosen. It is the intended choice, as this delivers the maximum storage 323 size without emulation of types, which size is not native to an architecture. 324 As the value behaves different depending on the node being a leaf. A special consideration should be done, 325 how the unset state has to be stored. 326 In case of being a leaf, the value is interpreated as bit vector, storing a "zero" correspond to setting the 327 least bit. So the unset state corresponds to value being zero. 328 In case of being an intermediate node, the both stored values min and max correspond to a range. In this 329 case the state of being unset is modeled by a minimum set to a value greater then the maximum on 330 initialization. 331 For the case a function is queried and the answer correnspond to a state being not set a Nullable.null is 332 returned from the very method. The null value in the Nullable type doesn't need to enlarge it, as on every 333 architecture all values beyond 2^(8 * size_t.sizeof / 2) - 1 stays unused It is not possible to construct 334 a tree large enough to contain these values. Due this fact the null-value for the Nullable is chosen to 335 2^(8 * size_t.sizeof / 2) 336 */ 337 private size_t _val; 338 mixin(bitfields!( 339 size_t, "_min", baseSize/2, // the default value of the min is greater then the max. 340 size_t, "_max", baseSize/2 341 )); 342 } 343 344 /* 345 It is not allowed to construct a node without providing the current universe size as it has to be known on 346 creation whether the children nodes have to be constructed. So, the default case is forbidden, as the children 347 may not appended beyond the knowledge of the constructor. 348 */ 349 @disable this(); 350 /** 351 Node constructor. A universe size provided, if the size is below the cutoff there is nothing to be done, as the 352 underlying value created and set to zero by default. 353 If otherwise create an array of children. This array has to be (according to Cormen) of size of higher square 354 root of the current universe size + 1. The extra place is reserved for the summary. 355 For each just created child call its constructor. 356 For the summary with the universe size of the higher square root of the current universe size. 357 For each other child with the universe size of the lower square root of the currennt universe size. 358 Then, assign the fully initialized children array to the pointer in the current node, doing approprate steps to 359 show, that this node is an intermediate node, not containing any values yet. 360 The knowledge of the current universe size is lost at this moment. As this keeps every build up node smaller 361 (and there could be a lot of them). This is why, the VEBtree class continues to hold the global universe size, 362 which is passed on every call to the root node. In this way this, extern saved value has the role of being 363 outsourced array size for each (!) node in the tree, as its size is reconstructed during the access to them. 364 */ 365 nothrow this(size_t uS) 366 { 367 if(uS > baseSize) 368 { 369 VEBnode[] tmp; 370 tmp.reserve(hSR(uS) + 1); // reserve enough place for the summary and the children cluster 371 tmp ~= VEBnode(hSR(uS)); // add the summary with its universe of higher squaure root of the current universe 372 for(size_t i = 1; i <= hSR(uS); ++i) 373 tmp ~= VEBnode(lSR(uS)); // add higher square root children with lower square root universe each. 374 ptrArr = tmp.ptr; // save the pointer to the array, loosing the information about its length. 375 nullify; // set the value to the sentinel value to represent the empty state. 376 } 377 // else nothing todo. 378 } 379 unittest 380 { 381 auto currentSeed = unpredictableSeed(); 382 static if(vdebug){write("UT: vN, __ctor. "); writeln("seed: ", currentSeed);} 383 rndGenInUse.seed(currentSeed); //initialize the random generator 384 385 const auto uS = uniform!"[]"(2U, testedSize, rndGenInUse); 386 387 const VEBnode vN = VEBnode(uS); 388 if(uS <= baseSize) 389 { 390 assert(vN.isLeaf); 391 assert(vN._val == 0); 392 assert(vN.ptrArr == null); 393 } 394 else 395 { 396 assert(!vN.isLeaf); 397 assert(vN._val == 1); 398 assert(vN.ptrArr != null); 399 } 400 } 401 402 /** convinience method to check, if the node belongs to the lowest level in the tree */ 403 @nogc nothrow @property bool isLeaf() const { return ptrArr == null; } 404 405 /** method to check whether the current node holds a value */ 406 @nogc nothrow @property bool isNull() const { return isLeaf ? (_val == 0) : (_min > _max); } 407 408 /** method executing the appropriate steps to nullify the current node */ 409 @nogc nothrow @property void nullify() { _val = isLeaf ? 0 : 1; } 410 411 /** 412 method returning either the lower part of the stored value (intermediate node) or the lowest bit set (bit vector 413 mode. If the node does not contain any value (min > max or value == 0) Nullable.null is returned. 414 */ 415 @nogc nothrow @property Nullable!(size_t, maxSizeBound) min() const 416 { 417 // define the result as a nullable 418 Nullable!(size_t, maxSizeBound) retVal; 419 /* 420 we have only a chance to get a value, if a value is present. 421 if it is a leaf, handle the val as a bit array and find the first bit set from the right. 422 if it is not a leaf, get the minimum. 423 */ 424 if(!isNull) retVal = isLeaf ? bsf(_val) : _min; 425 // return the result, even if it was not set to a value. 426 return retVal; 427 } 428 429 /** 430 setter for the min, setting either the lowest bit or the min part of the value. 431 */ 432 @nogc nothrow @property void min(size_t value) 433 { 434 if(isLeaf) 435 { 436 assert(min > value); 437 insert(value); 438 } 439 else 440 { 441 // the passed value should not exceed the allowed size of a size/2 442 assert(value < maxSizeBound); 443 _min = value; 444 } 445 } 446 447 /** 448 method returning either the higher part of the stored value (intermediate node) or the highest bit set (bit 449 vector mode. If the node does not contain any value (min > max or value == 0) Nullable.null is returned. 450 */ 451 @nogc nothrow @property Nullable!(size_t, maxSizeBound) max() const 452 { 453 // define the result as a nullable 454 Nullable!(size_t, maxSizeBound) retVal; 455 /* 456 we have only a chance to get a value, if a value is present. 457 if it is a leaf, handle the val as a bit array and find the first bit set from the left. 458 if it is not a leaf, get the max. 459 */ 460 if(!isNull) retVal = isLeaf ? bsr(_val) : _max; 461 // return the result, even if it was not set to a value. 462 return retVal; 463 } 464 465 /** 466 setter for the max, setting either the highest bit or the max part of the value. 467 */ 468 @nogc nothrow @property void max(size_t value) 469 { 470 if(isLeaf) 471 { 472 assert(max < value); 473 insert(value); 474 } 475 else 476 { 477 // the passed value should not exceed the allowed size of a size/2 478 assert(value < maxSizeBound); 479 _max = value; 480 } 481 } 482 483 /** 484 member method for the case universe size < base size. Overloads by passing only one parameter, which is 485 the bit number of interest. Returns whether the appropriate bit inside the bitvector is set. 486 */ 487 @nogc nothrow bool opIn_r(size_t bitnum) const 488 in 489 { 490 // method inside the node defined on leafs only. 491 assert(isLeaf); 492 // when a bitnum is passed to the leaf, then, it is an index to the bitarray and has to be in proper range 493 assert(bitnum < baseSize); 494 } 495 body { return bt(&_val, bitnum) != 0; } 496 // 497 unittest 498 { 499 auto currentSeed = unpredictableSeed(); // 83_843; 898_797_859; 500 static if(vdebug){write("UT: vN, opIn_r. "); writeln("seed: ", currentSeed);} 501 rndGenInUse.seed(currentSeed); //initialize the random generator 502 503 const auto value = uniform(0U,size_t.max, rndGenInUse); 504 const auto bitNum = uniform(0U,baseSize, rndGenInUse); 505 VEBnode vN = VEBnode(baseSize); 506 vN._val = value; 507 508 if((vN._val & size_t(1) << bitNum) != 0 ) assert(bitNum in vN); 509 if((vN._val & size_t(1) << bitNum) == 0 ) assert(!(bitNum in vN)); 510 } 511 512 /** 513 member method. this method is called from class with a universe size given. It performs recursion calls until 514 the universe size is reduced to the base size. Then the overloaded member method is called. 515 */ 516 @nogc nothrow bool member(size_t value, size_t uS) const 517 { 518 if(uS <= baseSize) return (value in this); // do not use other functionality any more, if descended so far. 519 520 if(this.isNull) return false; // if an empty intermediate node is found, nothing is stored below it. 521 if(value == this.min || value == this.max) return true; // case of a single valued range. 522 523 /* 524 else we have to descend, using the recursive indexing: 525 1. take the high(value, uS)-th child and 526 2. ask it about the reduced low(value, uS) value 527 3. use the lSR(uS) universe size of the childe node. 528 */ 529 return cluster[high(value, uS)].member(low(value, uS), lSR(uS)); 530 } 531 532 /** 533 insert method. given a leaf, sets the bit and returns, whether the bit was unset. Overloads by passing only one 534 parameter, which is the bit number of interest. 535 */ 536 @nogc nothrow bool insert(size_t bitnum) 537 in 538 { 539 // method inside the node defined on leafs only. 540 assert(isLeaf); 541 // when a bitnum is passed to the leaf, then, it is an index to the bitarray and has to be in proper range 542 assert(bitnum < baseSize); 543 } 544 body { return bts(&_val, bitnum) == 0; } 545 546 /** 547 insert method. this method is called from class with a universe size given. It performs recursion calls untill 548 the universe size is reduced to the base size. Then the overloaded insert method is called. 549 */ 550 @nogc nothrow bool insert(size_t value, size_t uS) 551 { 552 import std.algorithm.comparison : min, max; 553 554 if(uS <= baseSize) return this.insert(value); // if descended so far, do not use other functionality any more. 555 556 if(this.isNull) // if the current node does not contain anything put the value inside. 557 { 558 this.min = value; // the setters of min handle the value appropriately and do not need the universe size 559 this.max = value; // being explicitely provided, as they can check the isLeaf property. 560 assert(!this.isNull); 561 return true; 562 } 563 564 assert(!this.isNull); 565 assert(!this.min.isNull); 566 assert(!this.max.isNull); 567 568 if(value == this.min || value == this.max) return false; // return, if value is already here. 569 570 if(this.min == this.max) // if the node contains a single value only, expand the node to a range and leave. 571 { 572 this.min = min(this.min, value); 573 this.max = max(this.max, value); 574 return true; 575 } 576 /* 577 if none of the cases above was true (all of them are break conditions) we have to compare the given value 578 with the values present and adapt the range limits. This replaces the value we want to insert. 579 */ 580 // a swap can not be used here, as min is itself a (property) method 581 if(value < this.min) {const auto tmp = value; value = this.min.get; this.min = tmp; } 582 // a swap can not be used here, as max is itself a (property) method 583 if(value > this.max) {const auto tmp = value; value = this.max.get; this.max = tmp; } 584 585 // calculate the index of the children cluster by high(value, uS) we want to descent to. 586 auto nextTreeIndex = high(value, uS); 587 588 // if the child is happened to be null we have to update the summary and insert value of high(value, uS) into it 589 if(cluster[nextTreeIndex].isNull) summary.insert(high(value, uS), hSR(uS)); 590 591 // in any case we pass the lowered value low(value, uS) to the child. 592 auto res = cluster[nextTreeIndex].insert(low(value, uS), lSR(uS)); 593 return res; 594 } 595 596 /** 597 remove method. given a leaf, remove the bit and returns, whether the bit was set. Overloads by passing only one 598 parameter, which is the bit number of interest. 599 */ 600 @nogc nothrow bool remove(size_t bitnum) 601 in 602 { 603 assert(isLeaf); 604 assert(bitnum < baseSize); 605 } 606 body { return btr(&_val, bitnum) != 0; } 607 608 /** 609 remove method. this method is called from class with a universe size given. It performs recursion calls untill 610 the universe size is reduced to the base size. Then the overloaded remove method is called. 611 */ 612 @nogc nothrow bool remove(size_t value, size_t uS) 613 { 614 if(uS <= baseSize) return this.remove(value); // if descended so far, do not use other functionality any more. 615 if(this.isNull) return false; // if the current node is null, there is nothing to remove. 616 if(this.min == this.max) // if the current node contains only a single value 617 { 618 if(this.min != value) return false; // do nothing if the given value is not the stored one 619 this.nullify; // set this node to the sentinel-null if it does. 620 return true; 621 } 622 if(value == this.min) // if we met the minimum of a node 623 { 624 auto treeOffset = summary.min; // calculate an offset from the summary to continue with 625 if(treeOffset.isNull) // if the offset is invalid, then there is no further hierarchy and we are going to 626 { 627 this.min = this.max; // store a single value in this node. 628 return true; 629 } 630 auto min = cluster[treeOffset].min; // otherwise we get the minimum from the offset child 631 cluster[treeOffset].remove(min, lSR(uS)); // remove it from the child 632 633 // if it happens to become null during the remove process, we also remove the offset entry from the summary 634 if(cluster[treeOffset].isNull) summary.remove(treeOffset, hSR(uS)); 635 636 //anyway, the new min of the current node become the restored value of the calculated offset. 637 this.min = index(uS, treeOffset, min); 638 639 return true; 640 } 641 if(value == this.max) // if we met the maximum of a node 642 { 643 auto treeOffset = summary.max; // calculate an offset from the summary to contiue with 644 if(treeOffset.isNull) // if the offset is invalid, then there is no further hierarchy and we are going to 645 { 646 this.max = this.min; // store a single value in this node. 647 return true; 648 } 649 650 auto max = cluster[treeOffset].max; // otherwise we get maximum from the offset child 651 cluster[treeOffset].remove(max, lSR(uS)); // remove it from the child 652 653 // if it happens to become null during the remove process, we also remove the offset enty from the summary 654 if(cluster[treeOffset].isNull) summary.remove(treeOffset, hSR(uS)); 655 656 // anyway, the new max of the current node become the restored value of the calculated offset. 657 this.max = index(uS, treeOffset, max); 658 return true; 659 } 660 661 // if no condition was met we have to descend deeper. We get the offset by reducing the value to high(value, uS) 662 auto treeOffset = high(value, uS); 663 // and remove low(value, uS) from the offset child. 664 const bool retVal = cluster[treeOffset].remove(low(value, uS), lSR(uS)); 665 // if the cluster become null during the remove process we have to update the summary node. 666 if(cluster[treeOffset].isNull) summary.remove(treeOffset, hSR(uS)); 667 668 return retVal; 669 } 670 671 /** 672 predecessor method. given a leaf, returns the previous set bit if exists, otherwise Nullable.null. Overloads by 673 passing only one parameter, which is the bit number of interest. 674 */ 675 @nogc nothrow Nullable!(size_t, maxSizeBound) predecessor(size_t bitNum) const 676 in 677 { 678 assert(isLeaf); 679 assert(bitNum < baseSize); 680 } 681 body 682 { 683 Nullable!(size_t, maxSizeBound) retVal; 684 685 if(!isNull && (bitNum != 0)) 686 { 687 // create a mask, which hides all higher bits of the stored value down to the given bit number, then apply 688 // bit search from the highest bit. 689 auto maskedVal = _val & ((size_t(1) << bitNum) - 1); 690 if(maskedVal != 0) retVal = bsr(maskedVal); 691 } 692 return retVal; 693 } 694 // 695 unittest 696 { 697 const size_t currentSeed = unpredictableSeed(); 698 static if(vdebug){write("UT: vN, predecessor. "); writeln("seed: ", currentSeed);} 699 rndGenInUse.seed(currentSeed); //initialize the random generator 700 const size_t v = uniform(2U, testedSize, rndGenInUse); //set universe size to some integer. 701 const size_t x = uniform(1U, baseSize, rndGenInUse); 702 VEBnode vN = VEBnode(baseSize); 703 vN._val = v; 704 705 bool found; 706 707 for(size_t index = x - 1; index >= 0; --index) 708 { 709 if (v & (size_t(1) << index)) 710 { 711 found = true; 712 assert(!vN.isNull); 713 assert(vN.predecessor(x) == index); 714 break; 715 } 716 if(!index) break; 717 } 718 719 if(!found) assert(vN.predecessor(x).isNull); 720 } 721 722 /** 723 predecessor method. this method is called from class with a universe size given. It performs recursion calls 724 until the universe size is reduced to the base size. Then the overloaded predecessor method is called. 725 */ 726 @nogc nothrow Nullable!(size_t, maxSizeBound) predecessor(size_t value, size_t uS) const 727 { 728 Nullable!(size_t, maxSizeBound) retVal; 729 730 if(uS <= baseSize) return predecessor(value); // if descended so far, do not use other functionality any more. 731 if(this.isNull) return retVal; // if this node is empty, no predecessor can be found here or deeper in the tree 732 if(value > this.max) return this.max; // if given value is greater then the stored max, the predecessor is max 733 if(value <= this.min) return retVal; // if given value is less then the min, no predecessor exists. 734 735 /* 736 if none of the break conditions was met we have to descend further into the tree. 737 */ 738 const auto childIndex = high(value, uS); // calculate the child index by high(value, uS) 739 const auto minlow = cluster[childIndex].min; // look into the child for its minimum 740 // if the minimum exists and the lowered given value is greater then the child's minimum 741 if(!minlow.isNull && low(value, uS) > minlow) 742 { 743 // calculate an offset to continue with by asking the child on predecessor of the lowered value. 744 auto offset = cluster[childIndex].predecessor(low(value, uS), lSR(uS)); 745 // the result is given by reconstruction of the answer. 746 retVal = index(uS, childIndex, offset); 747 } 748 else // otherwise we can not use the minimum of the child 749 { 750 // ask the summary for the predecessor cluster. 751 auto predcluster = summary.predecessor(childIndex, hSR(uS)); 752 // if the predecessor cluster is null return the current min, as this is the last remaining value 753 if(predcluster.isNull) return this.min; 754 // if the predecessor cluster exists, the offset is given by its maximum 755 // and the result by the reconstruction of the offset. 756 retVal = index(uS, predcluster, cluster[predcluster].max); 757 } 758 return retVal; 759 } 760 761 /** 762 successor method. given a leaf, returns the next set bit if exists, otherwise Nullable.null. Overloads by 763 passing only one parameter, which is the bit number of interest. 764 */ 765 @nogc nothrow Nullable!(size_t, maxSizeBound) successor(size_t bitNum) const 766 in 767 { 768 assert(isLeaf); 769 assert(bitNum < baseSize); 770 } 771 body 772 { 773 Nullable!(size_t, maxSizeBound) retVal; 774 775 if(!isNull && (bitNum + 1 < baseSize)) 776 { 777 // create a mask, which hides all lower bits of the stored value up to the given bit number, then apply 778 // bit search from the lowest bit. 779 auto maskedVal = _val & ~((size_t(1) << (bitNum + 1)) - 1); 780 if(maskedVal != 0) retVal = bsf(maskedVal); 781 } 782 return retVal; 783 } 784 // 785 unittest 786 { 787 const size_t currentSeed = unpredictableSeed(); 788 static if(vdebug){write("UT: vN, successor. "); writeln("seed: ", currentSeed);} 789 rndGenInUse.seed(currentSeed); //initialize the random generator 790 const size_t v = uniform(0U, size_t.max, rndGenInUse); //set universe size to some integer. 791 const size_t x = uniform(0U, baseSize, rndGenInUse); 792 VEBnode vN = VEBnode(baseSize); 793 vN._val = v; 794 795 bool found; 796 797 for (size_t index = x + 1; index < baseSize; ++index) 798 { 799 if (v & (size_t(1) << index)) 800 { 801 found = true; 802 assert(vN.successor(x) == index); 803 break; 804 } 805 } 806 if(!found) assert(vN.successor(x).isNull); 807 } 808 809 /** 810 successor method. this method is called from class with a universe size given. It performs recursion calls until 811 the universe size is reduced to the base size. Then the overloaded successor method is called. 812 */ 813 @nogc nothrow Nullable!(size_t, maxSizeBound) successor(size_t value, size_t uS) const 814 { 815 Nullable!(size_t, maxSizeBound) retVal; 816 if(uS <= baseSize) return successor(value); // if descended so far, do not use other functionality any more. 817 if(this.isNull) return retVal; // if this node is empty, no successor can be found here or deeper in the tree 818 if(value < this.min) return this.min; // if given value is less then the min, return the min as successor 819 if(value >= this.max) return retVal; // if given value is greater then the max, no predecessor exists 820 821 /* 822 if none of the break conditions was met, we have to descent further into the tree. 823 */ 824 const auto childIndex = high(value, uS); // calculate the child index by high(value, uS) 825 const auto maxlow = cluster[childIndex].max; // look into the child for its maximum 826 // if the maximum exists and the lowered given value is less then the child's maximum 827 if(!maxlow.isNull && low(value, uS) < maxlow) 828 { 829 // calculate an offset to continue with by asking the child on predecessor of the lowered value 830 auto offset = cluster[childIndex].successor(low(value, uS), lSR(uS)); 831 // the result is given by reconstruction of the answer 832 retVal = index(uS, childIndex, offset); 833 } 834 else // otherwise we can not use the maximum of the child 835 { 836 // ask the summary for the successor cluster. 837 auto succcluster = summary.successor(childIndex, hSR(uS)); 838 // if the successor cluster is null 839 if(succcluster.isNull) return this.max; // return the current max, as this is the last remaining value 840 // if the successor cluster exists, the offset is given by its minimum 841 // and the result by the reconstruction of the offset. 842 retVal = index(uS, succcluster, cluster[succcluster].min); 843 } 844 return retVal; 845 } 846 } 847 848 /// 849 unittest 850 { 851 VEBnode vN = VEBnode(baseSize); 852 static assert(vN.sizeof < baseSize/2); 853 assert(vN.isNull); 854 assert(vN.sizeof == 2 * size_t.sizeof); 855 assert(vN.isLeaf); 856 assert(vN.isNull); 857 vN._val = 3; 858 assert(vN._min == 3); 859 assert(1 in vN); 860 assert(!(2 in vN)); 861 assert(vN.isLeaf); 862 assert(vN.ptrArr == null); 863 vN.nullify; 864 assert(vN.isNull); 865 assert(vN._val == 0); 866 } 867 868 /** 869 This class represents the VEB tree itself. It saves the provided at the initialization step wished size. The 870 generated tree has the capacity of the next higher power of 2. Beyond this, it stores the root element, through 871 which all accesses to the tree are managed. The tree implements also the interface for being a bidirectional range. 872 */ 873 class VEBtree 874 { 875 // the root element of the tree. 876 private VEBnode* root; 877 // this member is updated on every insertion and deletion to give the current element count on O(1) 878 private size_t _elementCount; 879 /// this member stores the initialization size, as it would be lost forever after initialization, otherwise 880 immutable size_t expectedSize; 881 /// this value tracks the total range of the tree to be able to move on it independent from its copies 882 private auto _range = iota(0UL); 883 884 /// default constructor of a VEB tree is disabled. 885 @disable this(); 886 887 private this(VEBnode* root_, size_t expectedSize_, size_t elementCount_, typeof(iota(0UL)) range_) @nogc 888 { 889 root = root_; 890 expectedSize = expectedSize_; 891 _elementCount = elementCount_; 892 _range = range_; 893 } 894 895 /** 896 to construct a VEB tree the wished size has to be provided. However, the size should be greater then one and 897 should not excess the maximum allowed size for the current architecture. 898 */ 899 nothrow this(size_t value) 900 in 901 { 902 assert(value > 1); 903 assert(value < maxSizeBound); 904 } 905 body 906 { 907 // set the expected size to the passed value 908 expectedSize = value; 909 // delegate the creation of the nodes with the apropriate power of two of the needed universe size 910 root = new VEBnode(nextPow2(expectedSize - 1)); 911 912 assert(this.empty); 913 } 914 /// 915 unittest 916 { 917 auto currentSeed = unpredictableSeed(); 918 static if(vdebug){write("UT: vT, __ctor. "); writeln("seed: ", currentSeed);} 919 rndGenInUse.seed(currentSeed); //initialize the random generator 920 921 auto uS = uniform(1U << size_t(1),testedSize, rndGenInUse); 922 const VEBtree vT = new VEBtree(uS); 923 assert(vT.empty); 924 if((uS & (uS - 1)) == 0) 925 assert(vT.capacity == uS); 926 else 927 assert(vT.capacity == (size_t(1) << (bsr(uS) + 1))); 928 929 assert(vT.expectedSize == uS); 930 931 const auto uS1 = uniform(1U << size_t(1),testedSize, rndGenInUse); 932 const auto uSsmall = uniform(1U << size_t(1),baseSize, rndGenInUse); 933 VEBtree vT1 = new VEBtree(uS1); 934 const VEBtree vTsmall = new VEBtree(uSsmall); 935 936 assert(vTsmall.root._val == 0); 937 assert(vTsmall.root.ptrArr == null); 938 939 if(uS1 > CHAR_BIT * size_t.sizeof) 940 { 941 assert(vT1.root._val == 1); 942 assert(vT1.root.ptrArr != null); 943 944 //check every child for consistency. 945 // the size of a node is higher square root & the summary node. 946 947 // capacity is tested elsewhere. 948 // hSR is also tested elsewhere 949 const auto childsAmount_l1 = hSR(vT1.capacity) + 1; 950 const auto childsAmount_l2 = hSR(lSR(vT1.capacity)) > baseSize ? hSR(lSR(vT1.capacity)) + 1 : 0; 951 952 // the tree is just created, assert all children are nullified 953 for(size_t i; i < childsAmount_l1; ++i) 954 { 955 assert(vT1.root.ptrArr[i].isNull); 956 if(childsAmount_l1 > baseSize + 1) 957 { 958 for(size_t j; j < childsAmount_l2; ++j) 959 { 960 assert(vT1.root.ptrArr[i].ptrArr[j].isNull); 961 } 962 } 963 } 964 } 965 } 966 967 /// another possibility is to construct a VEB tree is by providing an array. 968 nothrow this(R)(R range) if(isIterable!R) 969 in 970 { 971 // check, whether the range is not too long. I. e. expressable with an uint. 972 assert(range.length < maxSizeBound); 973 } 974 body 975 { 976 import std.algorithm.comparison : max; 977 // first, we have to determine the size of the tree. 978 // it is either derived from the length of the given tree or its greatest element 979 size_t limit; 980 foreach(size_t i; range) limit = max(limit,i); 981 // assert no element has exceeded the allowed range of baseSize/2 982 assert(limit < maxSizeBound); 983 // if the array is longer, then the greatest element, but the length passes, substitute the limit 984 limit = max(limit, range.length); 985 // call the constructor with the limit 986 this(limit + 1); 987 // put every element from the range into the tree 988 foreach(size_t i; range) insert(i); 989 } 990 991 /** 992 this method returns the capacity of the tree. It is equal to the next power of 2 regarding the size used at 993 initialization. 994 */ 995 @nogc nothrow size_t capacity() const { return nextPow2(expectedSize - 1); } 996 997 /** 998 this method is used to add an element to the tree. duplicate values will be ignored. the class provides an 999 intermediate layer between the caller and the contained root and enrichs the input by the stored size. 1000 */ 1001 auto insert(size_t value) @nogc nothrow 1002 { 1003 if(value >= capacity) return false; 1004 const bool retVal = root.insert(value, capacity); 1005 if(retVal) 1006 { 1007 ++_elementCount; 1008 1009 if(length == 1) _range = iota(value, value + 1UL); 1010 else 1011 { 1012 if(value < _range.front) _range = iota(value, _range.back + 1UL); 1013 if(value > _range.back) _range = iota(_range.front, value + 1UL); 1014 } 1015 1016 } 1017 return retVal; 1018 } 1019 /// 1020 unittest 1021 { 1022 auto currentSeed = unpredictableSeed(); 1023 static if(vdebug){write("UT: vT, insert. "); writeln("seed: ", currentSeed);} 1024 rndGenInUse.seed(currentSeed); //initialize the random generator 1025 1026 auto uS = uniform(allowedArraySize,testedSize, rndGenInUse); 1027 VEBtree vT = new VEBtree(uS); 1028 1029 size_t n; 1030 uint[allowedArraySize] insertedVals; 1031 while(n < allowedArraySize) 1032 { 1033 auto valToInsert = uniform(0U, uS, rndGenInUse); 1034 const bool inserted = vT.insert(valToInsert); 1035 if(inserted) 1036 { 1037 insertedVals[n] = valToInsert; 1038 n++; 1039 } 1040 } 1041 assert(vT._elementCount == insertedVals.length); 1042 1043 sort(insertedVals[]); 1044 assert(uniq(insertedVals[]).array.length == insertedVals.length); 1045 } 1046 1047 /// this method overrides the insert method to directly use arrays 1048 @nogc nothrow void insert(R)(R arr) if(isIterable!R){ foreach(size_t i; arr) insert(i); } 1049 1050 /// this method is used to remove elements from the tree. not existing values will be ignored. 1051 bool remove(size_t value) @nogc nothrow 1052 { 1053 const bool retVal = root.remove(value, capacity); 1054 if(retVal) 1055 { 1056 --_elementCount; 1057 if(length == 0) _range = iota(0UL); 1058 else 1059 { 1060 assert(!_range.empty); 1061 if(value == _range.front) popFront(); 1062 if(value == _range.back) popBack(); 1063 } 1064 } 1065 return retVal; 1066 } 1067 /// 1068 unittest 1069 { 1070 auto currentSeed = unpredictableSeed(); 1071 static if(vdebug){write("UT: vT, remove. "); writeln("seed: ", currentSeed);} 1072 rndGenInUse.seed(currentSeed); //initialize the random generator 1073 1074 auto uS = uniform(allowedArraySize,testedSize, rndGenInUse); 1075 VEBtree vT = new VEBtree(uS); 1076 1077 size_t n; 1078 uint[allowedArraySize] insertedVals; 1079 1080 while(n < allowedArraySize) 1081 { 1082 auto valToInsert = uniform(0U, uS, rndGenInUse); 1083 if(vT.insert(valToInsert)) 1084 { 1085 insertedVals[n] = valToInsert; 1086 n++; 1087 } 1088 } 1089 1090 assert(vT._elementCount == insertedVals.length); 1091 1092 foreach(size_t i; insertedVals) assert(vT.remove(i)); 1093 assert(vT.length == 0); 1094 } 1095 1096 /// this method is used to determine, whether an element is currently present in the tree 1097 @nogc nothrow bool opIn_r(size_t value) const 1098 { 1099 if(value >= capacity) return false; 1100 return root.member(value, capacity); 1101 } 1102 /// 1103 unittest 1104 { 1105 auto currentSeed = unpredictableSeed(); 1106 static if(vdebug){write("UT: vT, member. "); writeln("seed: ", currentSeed);} 1107 rndGenInUse.seed(currentSeed); //initialize the random generator 1108 1109 auto uS = uniform(allowedArraySize,testedSize, rndGenInUse); 1110 VEBtree vT = new VEBtree(uS); 1111 1112 size_t n; 1113 uint[allowedArraySize] insertedVals; 1114 while(n < allowedArraySize) 1115 { 1116 auto valToInsert = uniform(0U, uS, rndGenInUse); 1117 if(vT.insert(valToInsert)) 1118 { 1119 insertedVals[n] = valToInsert; 1120 n++; 1121 } 1122 } 1123 1124 sort(insertedVals[]); 1125 auto sortedInserted = assumeSorted(insertedVals[]); 1126 for(size_t i; i < testedSize; ++i) 1127 { 1128 if(sortedInserted.contains(i)) 1129 { 1130 assert(i in vT); 1131 } 1132 else 1133 { 1134 assert(!(i in vT), "i: " ~ to!string(i)); 1135 } 1136 } 1137 } 1138 1139 /// this method is used to determine the minimum of the tree 1140 @nogc nothrow @property Nullable!(size_t, maxSizeBound) min() const { return root.min; } 1141 1142 /// this method is used to determine the maximum of the tree 1143 @nogc nothrow @property Nullable!(size_t, maxSizeBound) max() const { return root.max; } 1144 1145 /// this method retrieves the successor of the given input. 1146 Nullable!(size_t, maxSizeBound) successor(alias boundaries = "()")(size_t value) const 1147 { 1148 if(value >= capacity) return Nullable!(size_t, maxSizeBound)(); 1149 1150 // otherwise get the successor 1151 auto currentSuccessor = root.successor(value, capacity); 1152 1153 // in case of closed boundaries 1154 static if(boundaries[1] == ']') // if there is a successor (between the value and the upper bound) 1155 { 1156 if(!currentSuccessor.isNull) return currentSuccessor; // return the successor 1157 else return Nullable!(size_t, maxSizeBound)(capacity); // otherwise return the bound (capacity) 1158 } 1159 else return currentSuccessor; // in case of open boundaries return the successor (null or a value) 1160 } 1161 unittest 1162 { 1163 auto currentSeed = unpredictableSeed(); 1164 static if(vdebug){write("UT: vT, successor. "); writeln("seed: ", currentSeed);} 1165 rndGenInUse.seed(currentSeed); //initialize the random generator 1166 1167 auto uS = uniform(allowedArraySize, testedSize, rndGenInUse); 1168 VEBtree vT = new VEBtree(uS); 1169 1170 // testing the boundaries now: 1171 auto randomElement = uniform(allowedArraySize, uS); 1172 assert(vT.successor(randomElement).isNull); 1173 assert(vT.successor!"[]"(randomElement) == vT.capacity); 1174 1175 size_t n; 1176 uint[allowedArraySize] insertedVals; 1177 while(n < allowedArraySize) 1178 { 1179 auto valToInsert = uniform(0U, uS, rndGenInUse); 1180 if(vT.insert(valToInsert)) 1181 { 1182 insertedVals[n] = valToInsert; 1183 n++; 1184 } 1185 } 1186 1187 auto sortedInserted = assumeSorted(sort(insertedVals[])); 1188 1189 assert(vT.max == sortedInserted.back); 1190 assert(vT.min == sortedInserted.front); 1191 size_t j; 1192 1193 for(size_t i; i <= testedSize; ++i) 1194 { 1195 const auto el = vT.successor(i); 1196 1197 if(el.isNull) 1198 { 1199 assert(i == vT.max); 1200 assert(sortedInserted.contains(i)); 1201 break; // quit the loop; 1202 } 1203 else 1204 { 1205 if(sortedInserted.contains(i)) ++j; 1206 assert(el == sortedInserted[j]); 1207 } 1208 } 1209 } 1210 1211 /// this method retrieves the predecessor of the given input. 1212 @nogc nothrow Nullable!(size_t, maxSizeBound) predecessor(alias boundaries = "()")(size_t value) const 1213 { 1214 auto currentPredecessor = root.predecessor(value, capacity); 1215 static if(boundaries[0] == '[') 1216 { 1217 if(!currentPredecessor.isNull) 1218 return currentPredecessor; 1219 else 1220 return Nullable!(size_t, maxSizeBound)(0); 1221 } 1222 else return currentPredecessor; 1223 } 1224 /// 1225 unittest 1226 { 1227 auto currentSeed = unpredictableSeed(); 1228 static if(vdebug){write("UT: vT, predecessor. "); writeln("seed: ", currentSeed);} 1229 rndGenInUse.seed(currentSeed); //initialize the random generator 1230 1231 auto uS = uniform(allowedArraySize,testedSize, rndGenInUse); 1232 VEBtree vT = new VEBtree(uS); 1233 1234 // testing the boundaries now: 1235 auto randomElement = uniform(allowedArraySize, uS); 1236 assert(vT.predecessor(randomElement).isNull); 1237 assert(vT.predecessor!"[]"(randomElement) == 0); 1238 1239 size_t n; 1240 uint[allowedArraySize] insertedVals; 1241 while(n < allowedArraySize) 1242 { 1243 auto valToInsert = uniform(0U, uS, rndGenInUse); 1244 if(vT.insert(valToInsert)) 1245 { 1246 insertedVals[n] = valToInsert; 1247 n++; 1248 } 1249 } 1250 1251 auto sortedInserted = assumeSorted(sort(insertedVals[])); 1252 1253 assert(vT.max == sortedInserted.back); 1254 assert(vT.min == sortedInserted.front); 1255 size_t j = allowedArraySize - 1; 1256 1257 size_t i = testedSize + 1; 1258 do 1259 { 1260 --i; 1261 const auto el = vT.predecessor(i); 1262 if(el.isNull) 1263 { 1264 assert(i == vT.min); 1265 assert(sortedInserted.contains(i)); 1266 break; // quit the loop; 1267 1268 } 1269 else 1270 { 1271 if(sortedInserted.contains(i)) --j; 1272 assert(el == sortedInserted[j]); 1273 } 1274 1275 }while(i > 0); 1276 } 1277 1278 /// this method is used to determine, whether the tree is currently containing an element. 1279 @nogc nothrow @property bool empty() const { return _range.empty; } 1280 1281 /// this method returns the minimium. 1282 @nogc nothrow @property size_t front() const { return _range.front; } 1283 1284 /// this method removes the minimum element 1285 void popFront() @nogc nothrow 1286 { 1287 auto s = successor(_range.front); 1288 if(s.isNull) _range = iota(maxSizeBound, 0UL); 1289 else _range = iota(s.get, _range.back + 1UL); 1290 } 1291 1292 /// bidirectional range also needs 1293 @nogc nothrow @property size_t back() { return _range.back; } 1294 1295 /// this method removes the maximum element 1296 @nogc nothrow void popBack() 1297 { 1298 if(!empty) 1299 { 1300 assert(!_range.empty); 1301 auto p = predecessor(_range.back); 1302 if(p.isNull) _range = iota(maxSizeBound, 0UL); 1303 else _range = iota(_range.front, p.get + 1UL); 1304 } 1305 } 1306 1307 /// Returns whether the element is in the tree 1308 auto opIndex(size_t index) const @nogc nothrow 1309 { 1310 if(index in this) return index; 1311 else return maxSizeBound; 1312 } 1313 1314 /// This method returns the amount of elements currently present in the tree. 1315 @nogc nothrow @property size_t length() const { return _elementCount; } 1316 1317 /** 1318 forward range also needs save. This is a draft version of the save function, it uses the opslice of the struct 1319 to construct a new one via an array 1320 */ 1321 @property VEBtree save() { return new VEBtree(root, expectedSize, _elementCount, _range.save); } 1322 1323 /** 1324 find the end of the slice by finding the successor in the bit tree. if no successor found return the limit. 1325 The limit is assumed to be smaller then the number given on creation. 1326 However, if a number larger then the expected size was inserted, the hard limit is the capacity of the tree. 1327 */ 1328 private size_t aMax(size_t i) const @nogc 1329 { 1330 immutable auto succ = successor(i); 1331 1332 if(succ.isNull) 1333 { 1334 if(max < expectedSize) 1335 { 1336 return expectedSize; 1337 } 1338 else 1339 { 1340 return capacity; 1341 } 1342 } 1343 else 1344 { 1345 return succ.get; 1346 } 1347 } 1348 1349 /** 1350 find the beginning of the slice by finding the predecessor in the bit tree. If no predecessor is found, assume zero 1351 as the first index. https://www.cs.utexas.edu/users/EWD/transcriptions/EWD08xx/EWD831.html 1352 */ 1353 private size_t aMin(size_t i) const @nogc 1354 { 1355 if(opIn_r(i)) return i; 1356 immutable auto pred = predecessor(i); 1357 if(pred.isNull) return 0; 1358 else return pred.get; 1359 } 1360 1361 /** 1362 prototype of an exporting function. For a proper save function, the undelying data structure has to be a bit array. 1363 */ 1364 @property auto dump() const 1365 { 1366 struct Result(T) 1367 { 1368 private T _input; 1369 private size_t _current; 1370 1371 this(T)(T input) 1372 { 1373 _input = input; 1374 } 1375 1376 void popFront() 1377 { 1378 if(this.empty) 1379 { 1380 _current = 0; 1381 } 1382 else 1383 { 1384 _current = _input.aMax(_current); 1385 } 1386 } 1387 1388 @property size_t front() const 1389 { 1390 assert(!empty, "Attempting to fetch the front of an empty polymers structure"); 1391 auto m = _input.aMax(_current); 1392 assert(m > _current); 1393 return m - _current; 1394 } 1395 1396 @property bool empty() const 1397 { 1398 const next = _input.aMax(_current); 1399 1400 if(next < _input.expectedSize) 1401 { 1402 return false; 1403 } 1404 else if(next == _input.expectedSize) 1405 { 1406 if(_input.expectedSize in _input) 1407 { 1408 return false; 1409 } 1410 else 1411 { 1412 return true; 1413 } 1414 } 1415 else 1416 { 1417 if(next == _input.capacity) 1418 { 1419 return true; 1420 } 1421 else 1422 { 1423 assert(next < _input.capacity); 1424 return false; 1425 } 1426 } 1427 } 1428 } 1429 1430 return Result!(typeof(this))(this); 1431 } 1432 /// 1433 unittest 1434 { 1435 static if(vdebug){writeln("UT: vT, dump. "); /*writeln("seed: ", currentSeed);*/} 1436 1437 auto p = new VEBtree(100); 1438 assert(p.empty); 1439 p.insert(5); 1440 p.insert(100); 1441 assert(!p.empty); 1442 assert(p.successor(0) == 5); 1443 assert(p.successor(4) == 5); 1444 assert(p.successor(5) == 100); 1445 auto s = p.dump; 1446 static assert(isInputRange!(typeof(s))); 1447 1448 assert(s.front == 5); 1449 1450 s.popFront; 1451 1452 assert(!s.empty); 1453 assert(s.front == 95); 1454 s.popFront; 1455 assert(s.empty); 1456 } 1457 1458 /** 1459 TODO: for a proper save function, more work has to be done: 1460 Each node has to be modeled by a bitarray entry. 1461 The bitarray has to be defined inside a tree object. 1462 The needed overhead has to be stored inside the same bitarray. 1463 Therefore, the bitarray has to be allocated recursively AND contiguosly. 1464 Then, the save (and copy) routine can be formulated as bitcopy of the underlying bitarray, 1465 alowing to reconstruct the tree behaviour and content from the layout of the bitarray itself. 1466 */ 1467 1468 /// dollar operator overload. 1469 @nogc nothrow @property size_t opDollar() 1470 { 1471 auto cMax = this.max; 1472 if(cMax.isNull) return capacity; 1473 else return cMax + 1; 1474 } 1475 1476 /** 1477 The single slice operation (slice-all) which is supported by the tree. Returns all elements in a new array. 1478 A convinience method. Uses exportTree with the boundaries set to the default ("()") 1479 */ 1480 size_t[] opSlice() const { return exportTree(); } 1481 1482 /// Makes a deep copy of the current object. 1483 VEBtree dup() const 1484 { 1485 auto retVal = new VEBtree(expectedSize); 1486 1487 if(this.length == 0) return retVal; 1488 1489 auto curr = this.min; 1490 do 1491 { 1492 retVal.insert(curr.get); 1493 curr = successor(curr.get); 1494 }while(!curr.isNull); 1495 1496 return retVal; 1497 } 1498 1499 /** 1500 this method serves as export of the tree to an array. () and [] is possible as template parameter to export the 1501 boundaries, even if they are not present. 1502 */ 1503 auto exportTree(alias boundaries = "()")() const 1504 { 1505 size_t[] retArray; 1506 auto exportLength = this.length; 1507 1508 static if(boundaries[0] == '[') 1509 if(!(0 in this)) // if zero is not in the tree and we want to include the left boundary, the export grows 1510 ++exportLength; 1511 static if(boundaries[1] == ']') 1512 ++exportLength; // we want to include the capacity as value, so the export grows 1513 1514 if(exportLength == 0) return retArray; 1515 else retArray.length = exportLength; 1516 1517 static if(boundaries[0] == '[') retArray[0] = 0; 1518 else if(!this.empty) retArray[0] = this.min; 1519 1520 static if(boundaries[1] == ']') retArray[$-1] = capacity; 1521 else if(!this.empty) retArray[$-1] = this.max; 1522 1523 for(size_t i = 1; i < retArray.length; ++i) 1524 { 1525 const auto succ = successor!boundaries(retArray[i-1]); 1526 if(succ.isNull || succ == capacity) break; 1527 retArray[i] = succ; 1528 } 1529 1530 return retArray; 1531 } 1532 // (optional) todo: implement some kind of cool output as a graphViz pic, similiar to the graphs in Cormen. 1533 } 1534 1535 /// 1536 unittest 1537 { 1538 VEBtree vT = new VEBtree(100); 1539 assert(vT.empty); 1540 const auto result = vT.insert(2); 1541 assert(result); 1542 assert(!vT.empty); 1543 assert(2 in vT); 1544 1545 auto vT2 = vT.save(); 1546 auto const vT3 = vT.dup(); 1547 assert(2 in vT2); 1548 const result2 = vT2.insert(3); 1549 assert(result2); 1550 assert(3 in vT2); 1551 assert(3 in vT); 1552 assert(!(3 in vT3)); 1553 assert(vT2.length == 2); 1554 1555 } 1556 1557 /// 1558 unittest 1559 { 1560 auto currentSeed = unpredictableSeed(); 1561 static if(vdebug){write("UT: vT, exportTree. "); writeln("seed: ", currentSeed);} 1562 rndGenInUse.seed(currentSeed); //initialize the random generator 1563 1564 const auto M = uniform(2U,testedSize, rndGenInUse); //set universe size to some integer. 1565 VEBtree vT = new VEBtree(M); //create the tree 1566 vT.fill(1000, rndGenInUse); 1567 1568 //assert(vT.exportTree == vT[]); 1569 assert(vT.exportTree!"[)".front == 0); 1570 assert(vT.exportTree!"(]".back == vT.capacity); 1571 } 1572 /// 1573 unittest 1574 { 1575 assert(!__traits(compiles, VEBtree())); 1576 VEBtree vT = new VEBtree(1000); 1577 assert(vT.capacity == 1024); 1578 assert(vT.min.isNull); 1579 assert(vT.insert(2)); 1580 vT.insert(5); 1581 assert(!(8 in vT)); 1582 assert(vT.insert(88)); 1583 assert(88 in vT); 1584 assert(vT.predecessor(4) == 2); 1585 assert(!(8 in vT)); 1586 assert(vT.insert(8)); 1587 assert(8 in vT); 1588 assert(vT.predecessor(75) == 8); 1589 assert(vT.predecessor(90) == 88); 1590 assert(vT.predecessor(7) == 5); 1591 assert(vT.predecessor(4) == 2); 1592 assert(vT.predecessor(2).isNull); 1593 assert(vT.predecessor!"[]"(2) == 0); 1594 assert(vT.successor(6) == 8); 1595 assert(vT.successor(5) == 8); 1596 assert(vT.successor(4) == 5); 1597 assert(vT.successor(1) == 2); 1598 assert(vT.successor(75) == 88); 1599 assert(vT.successor(90).isNull); 1600 assert(vT.successor!"[]"(90) == vT.capacity); 1601 assert(!(1029 in vT)); 1602 assert(vT.successor(1025).isNull); 1603 assert(vT.successor!"[]"(1025).isNull); 1604 1605 auto vT2 = new VEBtree(500); 1606 assert(vT2.empty); 1607 vT2.insert(50); 1608 vT2.insert(500); 1609 assert(vT2.max == 500); 1610 assert(vT2.min == 50); 1611 assert(vT2.successor(40) == 50); 1612 assert(vT2.successor(50) == 500); 1613 1614 vT2 = new VEBtree(500); 1615 assert(vT2.empty); 1616 vT2.insert(50); 1617 vT2.insert(500); 1618 assert(vT2.max == 500); 1619 assert(vT2.min == 50); 1620 assert(vT2.successor(40) == 50); 1621 assert(vT2.successor(50) == 500); 1622 1623 /* about 20 seconds in debug mode. 1624 auto vT3 = new VEBtree(uint.max); 1625 assert(vT3.insert(5)); 1626 assert(vT3.member(5)); 1627 assert(vT3.capacity == cast(ulong)uint.max + 1); 1628 //*/ 1629 1630 assert(!(1029 in vT)); 1631 assert(!(865 in vT)); 1632 assert(vT.insert(865)); 1633 assert(865 in vT); 1634 assert(!vT.insert(865)); 1635 assert(865 in vT); 1636 assert(!(866 in vT)); 1637 assert(!vT.remove(866)); 1638 assert(865 in vT); 1639 assert(vT.remove(865)); 1640 assert(!(865 in vT)); 1641 } 1642 1643 /// 1644 unittest 1645 { 1646 auto currentSeed = unpredictableSeed(); 1647 static if(vdebug){write("UT: rand, succ. "); writeln("seed: ", currentSeed);} 1648 rndGenInUse.seed(currentSeed); //initialize the random generator 1649 1650 const auto M = uniform(2U,testedSize, rndGenInUse); //set universe size to some integer. 1651 VEBtree vT = new VEBtree(M); //create the tree 1652 assert(vT.capacity == nextPow2(M-1)); 1653 const auto m = vT.fill(40, rndGenInUse); //(try to) fill the tree with thousend values 1654 size_t n; 1655 Nullable!(size_t, maxSizeBound) i = vT.max; 1656 1657 // discover the thousend (or little less) values with the predecessor method 1658 while(!i.isNull) 1659 { 1660 ++n; 1661 i = vT.predecessor(i); 1662 if(n > m) break; 1663 } 1664 1665 size_t o; 1666 i = vT.min; 1667 while(!i.isNull) 1668 { 1669 ++o; 1670 i = vT.successor(i.get); 1671 if(o - 1 > m) break; 1672 } 1673 1674 // assert, that all members are discovered, iff when no predecessors are left 1675 assert(n == m); 1676 assert(o == m); 1677 } 1678 1679 /// 1680 unittest 1681 { 1682 auto currentSeed = unpredictableSeed(); 1683 static if(vdebug){write("UT: rand, pred "); writeln("seed: ", currentSeed);} 1684 rndGenInUse.seed(currentSeed); //initialize the random generator 1685 const auto M = uniform(2U, testedSize, rndGenInUse); // set universe size to some integer. 1686 VEBtree vT = new VEBtree(M); 1687 vT.fill(1000, rndGenInUse); //fill the tree with some values 1688 Nullable!(size_t, maxSizeBound) i = vT.max; 1689 1690 // remove all members beginning from the maximum 1691 bool result; 1692 while(!i.isNull) 1693 { 1694 result = vT.remove(i); 1695 assert(result); 1696 const auto j = vT.predecessor(i); 1697 if(!j.isNull) 1698 assert(j != i); 1699 i = j; 1700 } 1701 1702 // assert, that all members are removed, iff when no predecessors are left. 1703 assert(vT.empty); 1704 } 1705 1706 /// 1707 unittest 1708 { 1709 auto currentSeed = unpredictableSeed(); 1710 static if(vdebug){write("UT: rand, remove "); writeln("seed: ", currentSeed);} 1711 rndGenInUse.seed(currentSeed); //initialize the random generator 1712 const auto M = uniform(2U, testedSize, rndGenInUse); // set universe size to some integer. 1713 VEBtree vT = new VEBtree(M); 1714 vT.fill(1000, rndGenInUse); //fill the tree with some values 1715 Nullable!(size_t, maxSizeBound) i = vT.min; 1716 1717 // remove all members beginning from the minimum 1718 bool result; 1719 while(!i.isNull) 1720 { 1721 result = vT.remove(i); 1722 assert(result); 1723 const auto j = vT.successor(i); 1724 if(!j.isNull) 1725 assert(j != i); 1726 i = j; 1727 } 1728 1729 // assert, that all members are removed, iff when no successors are left. 1730 assert(vT.empty); 1731 } 1732 1733 /// 1734 unittest 1735 { 1736 const uint M = testedSize; 1737 VEBtree vT = new VEBtree(M); 1738 vT.insert(0x000f); 1739 assert(vT.predecessor(0x000f).isNull); 1740 vT.insert(0x00f0); 1741 assert(vT.predecessor(0x00f0) == 0x000f); 1742 vT.insert(0x0f00); 1743 assert(vT.predecessor(0x0f00) == 0x00f0); 1744 vT.insert(0xf000); 1745 assert(vT.predecessor(0xf000) == 0x0f00); 1746 1747 auto result = vT.remove(0xf000); 1748 assert(result); 1749 assert(vT.predecessor(0xf000) == 0x0f00); 1750 result = vT.remove(0x0f00); 1751 assert(result); 1752 assert(vT.predecessor(0x0f00) == 0x00f0); 1753 result = vT.remove(0x00f0); 1754 assert(result); 1755 assert(vT.predecessor(0x00f0) == 0x000f); 1756 result = vT.remove(0x000f); 1757 assert(result); 1758 assert(vT.predecessor(0x000f).isNull); 1759 } 1760 1761 /// 1762 unittest 1763 { 1764 const uint M = testedSize; 1765 VEBtree vT = new VEBtree(M); 1766 vT.insert(0xf000); 1767 assert(0xf000 in vT); 1768 vT.insert(0x0f00); 1769 assert(0x0f00 in vT); 1770 vT.insert(0x00f0); 1771 assert(0x00f0 in vT); 1772 vT.insert(0x000f); 1773 assert(0x000f in vT); 1774 1775 auto result = vT.remove(0xf000); 1776 assert(result); 1777 assert(!(0xf000 in vT)); 1778 result = vT.remove(0x0f00); 1779 assert(result); 1780 assert(!(0x0f00 in vT)); 1781 result = vT.remove(0x00f0); 1782 assert(result); 1783 assert(!(0x00f0 in vT)); 1784 result = vT.remove(0x000f); 1785 assert(result); 1786 assert(!(0x000f in vT)); 1787 } 1788 1789 /// 1790 unittest 1791 { 1792 //stress test 1793 auto currentSeed = unpredictableSeed(); 1794 static if(vdebug){write("UT: rand, stress "); writeln("seed: ", currentSeed);} 1795 rndGenInUse.seed(currentSeed); //initialize the random generator 1796 // do not use more then "1 << 15", as for the red-black tree the insertion duration is almost 4 (!) minutes. 1797 // last test says: see below. 1798 const auto M = uniform(2U, testedSize, rndGenInUse); // set universe size to some integer. 1799 VEBtree vT = new VEBtree(M); 1800 size_t[] arr; 1801 const auto howMuchFilled = vT.fill(arr, rndGenInUse); 1802 1803 assert(arr.length == howMuchFilled); 1804 1805 VEBtree vT2 = new VEBtree(M); 1806 1807 assert(vT2.capacity == vT.capacity); 1808 1809 auto rbt = redBlackTree!size_t(0); 1810 rbt.clear; 1811 1812 void fill1() 1813 { 1814 foreach(size_t i; arr) 1815 { 1816 vT2.insert(i); 1817 } 1818 1819 foreach(size_t i; arr) 1820 { 1821 vT2.remove(i); 1822 } 1823 assert(vT2.empty); 1824 1825 } 1826 1827 void fill2() 1828 { 1829 foreach(size_t i; arr) 1830 { 1831 rbt.insert(i); 1832 } 1833 } 1834 1835 /* 1836 this part is for speed test 1837 */ 1838 /* 1839 compiled with ldc2 vebtree.d -O -main -unittest 1840 results of stress tests: 1841 size of tree: 16777216 1842 howMuchFilled: 16252928 1843 VEB: 2 secs, 382 ms, 588 μs, and 8 hnsecs 1844 */ 1845 /* 1846 import std.stdio; 1847 1848 writeln("size of tree: ", vT2.capacity); 1849 writeln("howMuchFilled: ", howMuchFilled); 1850 //auto r = benchmark!(fill1, fill2)(1); 1851 auto r = benchmark!(fill1)(1); 1852 1853 auto f0Result = to!Duration(r[0]); 1854 //auto f1Result = to!Duration(r[1]); 1855 writeln("VEB: ", f0Result); //10ms 1856 //writeln("rbt: ", f1Result); //40sec 1857 //assert(f0Result < f1Result); 1858 //*/ 1859 } 1860 1861 /// 1862 unittest 1863 { 1864 const uint currentSeed = unpredictableSeed(); 1865 static if(vdebug){write("UT: rand, member "); writeln("seed: ", currentSeed);} 1866 rndGenInUse.seed(currentSeed); //initialize the random generator 1867 const uint M = uniform(2U, testedSize, rndGenInUse); // set universe size to some integer. 1868 uint[] sourceArr; 1869 sourceArr.length = M; 1870 // generate a random array as the source for the tree 1871 for(uint i; i < M; i++) sourceArr[i] = uniform(0U, M, rndGenInUse); 1872 // constructor to test 1873 VEBtree vT = new VEBtree(sourceArr); 1874 // make the array values unique. 1875 auto uniqueArr = sort(sourceArr).uniq; 1876 // check, that all values are filled 1877 bool result; 1878 foreach(uint i; uniqueArr) 1879 { 1880 assert(i in vT); 1881 result = vT.remove(i); 1882 assert(result); 1883 } 1884 // check, that no other elements are present. 1885 assert(vT.empty); 1886 } 1887 1888 /// 1889 unittest 1890 { 1891 auto currentSeed = unpredictableSeed(); 1892 static if(vdebug){write("UT: rand, opSlice "); writeln("seed: ", currentSeed);} 1893 rndGenInUse.seed(currentSeed); //initialize the random generator 1894 // do not use more then "1 << 15", as for the red-black tree the insertion duration is almost 4 (!) minutes. 1895 const auto M = uniform(2U, testedSize, rndGenInUse); // set universe size to some integer. 1896 VEBtree vT = new VEBtree(M); 1897 1898 size_t[] arr; 1899 vT.fill(arr, rndGenInUse, 16); 1900 //assert(setSymmetricDifference(vT[], sort(arr)).empty); 1901 } 1902 1903 /// 1904 unittest 1905 { 1906 assert(isBidirectionalRange!VEBtree); 1907 assert(isRandomAccessRange!VEBtree); 1908 } 1909 1910 /// 1911 unittest 1912 { 1913 VEBtree vT = new VEBtree(14); 1914 auto result = vT.insert(2); 1915 assert(result); 1916 result = vT.insert(5); 1917 assert(result); 1918 result = vT.insert(10); 1919 assert(result); 1920 //assert(vT[] == [2, 5, 10]); 1921 } 1922 1923 /// 1924 unittest 1925 { 1926 /* 1927 //another stress test 1928 auto currentSeed = unpredictableSeed(); 1929 static if(vdebug){write("UT: stress test 2 "); writeln("seed: ", currentSeed);} 1930 rndGenInUse.seed(currentSeed); //initialize the random generator 1931 1932 void fill16(){ VEBtree vT = new VEBtree(1 << 16); } 1933 void fill17(){ VEBtree vT = new VEBtree(1 << 17); } 1934 void fill18(){ VEBtree vT = new VEBtree(1 << 18); } 1935 void fill19(){ VEBtree vT = new VEBtree(1 << 19); } 1936 void fill20(){ VEBtree vT = new VEBtree(1 << 20); } 1937 void fill21(){ VEBtree vT = new VEBtree(1 << 21); } 1938 void fill22(){ VEBtree vT = new VEBtree(1 << 22); } 1939 void fill23(){ VEBtree vT = new VEBtree(1 << 23); } 1940 void fill24(){ VEBtree vT = new VEBtree(1 << 24); } 1941 void fill25(){ VEBtree vT = new VEBtree(1 << 25); } 1942 void fill26(){ VEBtree vT = new VEBtree(1 << 26); } 1943 void fill27(){ VEBtree vT = new VEBtree(1 << 27); } 1944 void fill28(){ VEBtree vT = new VEBtree(1 << 28); } 1945 void fill29(){ VEBtree vT = new VEBtree(1 << 29); } 1946 void fill30(){ VEBtree vT = new VEBtree(1 << 30); } 1947 1948 import std.stdio; 1949 auto r = benchmark!(fill16, fill17, fill18, fill19, fill20, fill21, fill22, fill23, fill24, fill25, fill26, fill27, 1950 fill28, fill29, fill30)(1); 1951 //auto r = benchmark!(fill1)(1); 1952 auto f16Result = to!Duration(r[0]); 1953 auto f17Result = to!Duration(r[1]); 1954 auto f18Result = to!Duration(r[2]); 1955 auto f19Result = to!Duration(r[3]); 1956 auto f20Result = to!Duration(r[4]); 1957 auto f21Result = to!Duration(r[5]); 1958 auto f22Result = to!Duration(r[6]); 1959 auto f23Result = to!Duration(r[7]); 1960 auto f24Result = to!Duration(r[8]); 1961 auto f25Result = to!Duration(r[9]); 1962 auto f26Result = to!Duration(r[10]); 1963 auto f27Result = to!Duration(r[11]); 1964 auto f28Result = to!Duration(r[12]); 1965 auto f29Result = to!Duration(r[13]); 1966 auto f30Result = to!Duration(r[14]); 1967 1968 writeln("VEB with M of ", 1 << 16, ": ", f16Result); 1969 writeln("VEB with M of ", 1 << 17, ": ", f17Result); 1970 writeln("VEB with M of ", 1 << 18, ": ", f18Result); 1971 writeln("VEB with M of ", 1 << 19, ": ", f19Result); 1972 writeln("VEB with M of ", 1 << 20, ": ", f20Result); 1973 writeln("VEB with M of ", 1 << 21, ": ", f21Result); 1974 writeln("VEB with M of ", 1 << 22, ": ", f22Result); 1975 writeln("VEB with M of ", 1 << 23, ": ", f23Result); 1976 writeln("VEB with M of ", 1 << 24, ": ", f24Result); 1977 writeln("VEB with M of ", 1 << 25, ": ", f25Result); 1978 writeln("VEB with M of ", 1 << 26, ": ", f26Result); 1979 writeln("VEB with M of ", 1 << 27, ": ", f27Result); 1980 writeln("VEB with M of ", 1 << 28, ": ", f28Result); 1981 writeln("VEB with M of ", 1 << 29, ": ", f29Result); 1982 writeln("VEB with M of ", 1 << 30, ": ", f30Result); 1983 //*/ 1984 1985 } 1986 1987 /// 1988 unittest 1989 { 1990 // This unittest is for check of adding of big numbers 1991 /* in debug mode about 1 min. 1992 const uint[] arr = [1, 2, 8, 2_147_483_647]; 1993 new VEBtree(arr); 1994 //*/ 1995 } 1996 1997 /// 1998 unittest 1999 { 2000 import std.algorithm : sort, uniq; 2001 //stress test 2002 auto currentSeed = unpredictableSeed(); 2003 static if(vdebug){write("UT: rand, ranges "); writeln("seed: ", currentSeed);} 2004 rndGenInUse.seed(currentSeed); //initialize the random generator 2005 // do not use more then "1 << 15", as for the red-black tree the insertion duration is almost 4 (!) minutes. 2006 // last test says: see below. 2007 const auto M = uniform(2U, testedSize, rndGenInUse); // set universe size to some integer. 2008 auto const vT = new VEBtree(M); 2009 /*testing the range methods*/ 2010 assert(vT.empty); 2011 2012 uint[] sourceArr; 2013 sourceArr.length = uniform(2U, M, rndGenInUse); 2014 for(uint i; i < sourceArr.length; i++) 2015 sourceArr[i] = uniform(0U, M, rndGenInUse); 2016 2017 sort(sourceArr); 2018 const auto uniqueArr = sourceArr.uniq.array; 2019 2020 // constructor to test 2021 2022 auto vTnew = new VEBtree(sourceArr); 2023 assert(!vTnew.empty); 2024 assert(vTnew.length == uniqueArr.length); 2025 const auto vT2 = vTnew.save; 2026 assert(vTnew.array == uniqueArr); 2027 assert(!vTnew.empty); 2028 assert(!vT2.empty); 2029 assert(vT2[] == uniqueArr); 2030 2031 /* Works. Duration in debug mode: about 35 seconds. 2032 auto vTT = new VEBtree(maxSizeBound - 1); 2033 assert(vTT.insert(42)); 2034 //*/ 2035 }