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