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