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