1 /**
2     Vanilla B-Tree implementation.
3     Note that this is an implementation detail of
4     `dplug.core.map` and not part of of the Dplug API.
5 
6     Copyright: (c) Guillaume Piolat 2024-2025.
7     License: [BSL-1.0](http://www.boost.org/LICENSE_1_0.txt)
8 */
9 module dplug.core.btree;
10 
11 import dplug.core.nogc;
12 
13 import std.functional: binaryFun;
14 
15 //debug = btree;
16 
17 debug(btree)
18 {
19     import core.stdc.stdio;
20 }
21 
22 /**
23     Vanilla [B-Tree](https://en.wikipedia.org/wiki/B-tree).
24 
25     `O(lg(n))` insertion, removal, and search time, much
26     like the builtin associative arrays.
27 
28     `.init` is valid, needs no initialization.
29 
30     Params:
31         K    = Key type, must be comparable with `less`.
32         V    = Value type.
33         less = Must be a strict inequality, orders all `K`.
34         allowDuplicates = Are duplicate keys allowed?
35         duplicateKeyIsUB = Are duplicate keys UB?
36                            When `true`, user MUST guarantee
37                            no duplicates and `insert` is
38                            faster.
39 
40     Warning: keys don't need `opEquals`, but:
41          `!(a < b) && !(b > a)`
42          should imply that:
43          `a == b`
44 */
45 struct BTree(K, V, alias less = "a < b",
46              bool allowDuplicates = false,
47              bool duplicateKeyIsUB = false)
48 {
49 public:
50 nothrow:
51 @nogc:
52 
53     // TODO: map over range of keys
54     // PERF: tune minDegree vs size of K and V, there must
55     // be a sweet spot
56     // PERF: allocate nodes in memory pool, to be built in
57     //       bulk. How does reclaim works in that case, in a
58     //       way that doesn't explode memory?
59     // PERF: first node could be interned in the struct
60     //       itself (interior pointer) however that requires
61     //       first to tweak the branching factor with size
62     //       of item.
63     // PERF: (requires: Node memory pool) find a way to
64     //       deallocate all at once.
65     // PERF: find how to use duplicateKeyIsUB for Map and
66     //       Set.
67 
68     debug(btree) invariant()
69     {
70         checkInvariant();
71     }
72 
73 
74     /**
75         Not copyable.
76     */
77     @disable this(this);
78 
79 
80     /**
81         Constructor.
82         A new B-tree has no allocation and zero items.
83     */
84     this(int dummy)
85     {
86         // It does nothing, because T.init is valid.
87     }
88 
89 
90     /**
91         Destructor. All nodes cleared.
92     */
93     ~this()
94     {
95         if (_root)
96         {
97             _root.reclaimMemory();
98             _root = null;
99         }
100     }
101 
102 
103     /**
104         Number of items in the B-Tree.
105         Returns: Number of items.
106     */
107     size_t length() const
108     {
109         return _count;
110     }
111 
112 
113     /**
114         Is this B-Tree empty?
115         Returns: `true` if zero items.
116     */
117     bool empty() const
118     {
119         return _count == 0;
120     }
121 
122 
123     /**
124         Insert item in tree.
125 
126         Params:
127             key   = Key to insert.
128             value = Value to insert associated with `key`.
129 
130         Returns: `true` if the insertion took place.
131 
132         Warning: Inserting duplicate keys when duplicates
133                  are not supported is Undefined Behaviour.
134                  Use `contains()` to avoid that case.
135     */
136     bool insert(K key, V value)
137     {
138         lazyInitialize();
139 
140         // Runtime check to prevent accidental dupe keys.
141         static if (!allowDuplicates)
142         {
143             static if (duplicateKeyIsUB)
144             {
145                 // Detect dupes, but in -release it will
146                 // be Undefined Behaviour
147                 assert(findKeyValue(_root, key) is null);
148             }
149             else
150             {
151                 // Always detect dupes, this slows down
152                 // insertion by a lot
153                 if (findKeyValue(_root, key) !is null)
154                     return false;
155             }
156         }
157 
158         Node* r = _root;
159 
160         if (r.isFull())
161         {
162             Node* s = allocateNode();
163             _root = s;
164             s.parent = null;
165             s.isLeaf = false;
166             s.numKeys = 0;
167             s.children[0] = r;
168             r.parent = s;
169             splitChild(s, 0, r);
170             insertNonFull(s, key, value);
171         }
172         else
173         {
174             insertNonFull(r, key, value);
175         }
176 
177         _count += 1;
178         return true;
179     }
180 
181 
182     /**
183         Erase an item from the tree.
184 
185         Params:
186             key = Key to remove.
187 
188         Returns: Number of removed items, can be 0 or 1. In
189                  case of dupe keys, remove only one of them.
190     */
191     size_t remove(K key)
192     {
193         // This was surprisingly undocumented on the
194         // Internet or LLM.
195 
196         if (_root is null)
197             return 0;
198 
199         int keyIndex;
200         Node* node = findNode(_root, key, keyIndex);
201         if (node is null)
202             return 0; // not found
203 
204         _count -= 1;
205 
206         // Reference:
207         // https://www.youtube.com/watch?v=0NvlyJDfk1M
208         if (node.isLeaf)
209         {
210             // First, remove key, then eventually rebalance.
211             deleteKeyValueAtIndexAndShift(node, keyIndex);
212             rebalanceAfterDeletion(node);
213         }
214         else
215         {
216             // Exchange key with either highest of the
217             // smaller key in leaf, or largest of the
218             // highest keys.
219             //
220             //              . value .
221             //             /         \
222             //            /           \
223             //           /             \
224             //     left subtree      right subtree
225             //
226             // But I'm not sure why people tout this
227             // solution.
228             // Here we simply always get to the rightmost
229             // leaf node of left subtree. It seems it's
230             // always possible indeed, and it's not faster
231             // to look at the other sub-tree.
232             Node* leaf = node.children[keyIndex];
233             while (!leaf.isLeaf)
234                 leaf = leaf.children[leaf.numKeys];
235             assert(leaf);
236 
237             // Remove key from leaf, put it instead of
238             // target.
239             node.kv[keyIndex] = leaf.kv[leaf.numKeys-1];
240             leaf.numKeys -= 1;
241 
242             // and then rebalance
243             rebalanceAfterDeletion(leaf);
244         }
245         return 1;
246     }
247 
248 
249     /**
250         Iterate over all values in the tree.
251 
252         Returns: Forward range of `V`, over the whole tree.
253     */
254     auto byValue()
255     {
256         return BTreeRange!(RangeType.value)(this);
257     }
258     ///ditto
259     auto byValue() const
260     {
261         return const(BTreeRange!(RangeType.value))(this);
262     }
263 
264     /**
265         Iterate over all keys in the tree.
266 
267         Returns: Forward range of `K`, over the whole tree.
268     */
269     auto byKey()
270     {
271         return BTreeRange!(RangeType.key)(this);
272     }
273     ///ditto
274     auto byKey() const
275     {
276         return const(BTreeRange!(RangeType.key))(this);
277     }
278 
279     /**
280         Iterate over all keys and values simultaneously.
281 
282         Returns: Forward range of a Voldemort struct that
283                  exposes `.key` and `.value` of type `K` and
284                  `V` respectively.
285     */
286     auto byKeyValue()
287     {
288         return BTreeRange!(RangeType.keyValue)(this);
289     }
290     ///ditto
291     auto byKeyValue() const
292     {
293         return const(BTreeRange!(RangeType.keyValue))(this);
294     }
295 
296 
297     /**
298         Search the B-Tree by key.
299 
300         Params:
301             key = Key to search for.
302 
303         Returns:
304             A pointer to the corresponding `V` value.
305             `null` if not found.
306 
307         Note: In case of duplicate keys, it returns one
308               of those in unspecified order.
309     */
310     inout(V)* opBinaryRight(string op)(K key) inout
311         if (op == "in")
312     {
313         if (_root is null)
314             return null;
315         inout(KeyValue)* kv = findKeyValue(_root, key);
316         if (!kv) return null;
317         return &kv.value;
318     }
319 
320     /**
321         Search the B-Tree by key.
322 
323         Params:
324             key = Key to search for.
325 
326         Returns: A reference to the value corresponding to
327                  this key.
328 
329         Note: In case of duplicate keys, it returns one
330               of those in unspecified order.
331     */
332     ref inout(V) opIndex(K key) inout
333     {
334         inout(V)* p = key in this;
335         return *p;
336     }
337 
338     /**
339         Search the B-Tree for a key.
340 
341         Params:
342             key = Key to search for.
343 
344         Returns: `true` if `key`` is contained in the tree.
345     */
346     bool contains(K key) const
347     {
348         if (_root is null)
349             return false;
350         return findKeyValue(_root, key) !is null;
351     }
352 
353 private:
354 
355     // Called "t" or "minimum degree" in litterature, can
356     // never be < 2.
357     // Make it lower (eg: 2) to test tree algorithms.
358     // See <digression> below to see why this is not B-Tree
359     // "order".
360     enum minDegree = 16;
361 
362     // Every node must have >= minKeysPerNode and <=
363     // maxKeysPerNode.
364     // The root node is allowed to have < minKeysPerNode
365     // (but not zero).
366     enum int minKeys     =     minDegree - 1;
367     enum int maxKeys     = 2 * minDegree - 1;
368     enum int minChildren =     minDegree;
369     enum int maxChildren = 2 * minDegree;
370 
371     alias _less = binaryFun!less;
372 
373     Node* _root;
374     size_t _count;
375 
376     void checkInvariant() const
377     {
378         int count = 0;
379         if (_root)
380         {
381             assert(_count > 0);
382             checkNode(_root, null, count);
383         }
384         else
385         {
386             assert(_count == 0); // No items <=> null _root
387         }
388         assert(count == _count);
389     }
390 
391     // null if not found
392     inout(KeyValue)* findKeyValue(inout(Node)* x, K key)
393         inout
394     {
395         int index;
396         inout(Node)* node = findNode(x, key, index);
397         if (node is null)
398             return null;
399         else
400             return &(node.kv[index]);
401     }
402 
403     // Return containing node + index of value in store, or
404     // null if not found.
405     inout(Node)* findNode(inout(Node)* x, K key,
406                           out int index) inout
407     {
408         int i = 0;
409         while (i < x.numKeys && _less(x.kv[i].key, key))
410             i += 1;
411 
412         // Like in Phobos' Red Black tree, this use:
413         // !less(a,b) && !less(b,a) instead of opEquals.
414 
415         if (i < x.numKeys && !_less(key, x.kv[i].key))
416         {
417             index = i;
418             return x;
419         }
420         else
421         {
422             if (x.isLeaf)
423                 return null;
424             else
425                 return findNode(x.children[i], key, index);
426         }
427     }
428 
429     // Create root node if none.
430     void lazyInitialize()
431     {
432         if (_root)
433             return;
434 
435         _root = allocateNode;
436         _root.isLeaf = true;
437         _root.numKeys = 0;
438         _root.parent = null;
439     }
440 
441     void insertNonFull(Node* x, K key, V value)
442     {
443         int i = x.numKeys - 1;
444         if (x.isLeaf)
445         {
446             while (i >= 0 && _less(key, x.kv[i].key))
447             {
448                 x.kv[i+1] = x.kv[i];
449                 i -= 1;
450             }
451             x.kv[i+1] = KeyValue(key, value);
452             x.numKeys++;
453         }
454         else
455         {
456             while (i >= 0 && _less(key, x.kv[i].key))
457             {
458                 i -= 1;
459             }
460             i += 1;
461             Node* c = x.children[i];
462             if (c.isFull)
463             {
464                 splitChild(x, i, c);
465                 if (_less(x.kv[i].key, key))
466                 {
467                     i = i + 1;
468                     c = x.children[i];
469                 }
470             }
471             insertNonFull(c, key, value);
472         }
473     }
474 
475     // x = a parent with at least one slot available
476     // y = a full node to split
477     void splitChild(Node* x, int i, Node* y)
478     {
479         // create new child, that will take half the
480         // keyvalues and children of the full y node
481         Node* z = allocateNode();
482         z.isLeaf = y.isLeaf;
483         z.numKeys = minDegree - 1;
484         z.parent = x;
485 
486         // copy half of values (highest) in new child
487         for (int j = 0; j < minDegree - 1; ++j)
488         {
489             z.kv[j] = y.kv[minDegree + j];
490         }
491 
492         // same for child pointer if any
493         if (!y.isLeaf)
494         {
495             for (int j = 0; j < minDegree; ++j)
496             {
497                 z.children[j] = y.children[minDegree + j];
498                 z.children[j].parent = z;
499             }
500         }
501 
502         // Formerly full child now has room again
503         y.numKeys = minDegree - 1;
504 
505         // And now for the parent node:
506         // * new child is inserted right of its older
507         //   sibling
508         for (int j = x.numKeys; j > i; --j)
509         {
510             x.children[j+1] = x.children[j];
511         }
512         x.children[i+1] = z;
513         for (int j = x.numKeys - 1; j >= i; --j)
514         {
515             x.kv[j+1] = x.kv[j];
516         }
517         // * middle key is choosen as pivot
518         x.kv[i] = y.kv[minDegree-1];
519         x.numKeys += 1;
520     }
521 
522     // Take one node that is exactly below capacity, and
523     // reinstate the invariant by merging nodes and
524     // exchanging with neighbours. Is called on nodes that
525     // might be missing exactly one item.
526     void rebalanceAfterDeletion(Node* node)
527     {
528         if (node.parent is null)  // is this the tree _root?
529         {
530             assert(_root is node);
531 
532             if (_root.numKeys == 0)
533             {
534                 if (_root.isLeaf) // no more items in tree
535                 {
536                     destroyFree(_root);
537                     _root = null;
538                 }
539                 else // tree is reduced by one level
540                 {
541                     Node* oldRoot = _root;
542                     _root = oldRoot.children[0];
543                     _root.parent = null;
544                     oldRoot.numKeys = -1;
545                     // so that it is not destroyed
546                     oldRoot.children[0] = null;
547                     destroyFree(oldRoot); // <- here
548                 }
549             }
550             return;
551         }
552 
553         if (node.numKeys >= minKeys)
554             return; // no balance issue, exit
555 
556         // Else, the node is missing one key
557         assert(node.numKeys == minKeys - 1);
558 
559         Node* parent = node.parent;
560         assert(parent !is null);
561 
562         Node* left;
563         Node* right;
564         int childIndex = -1;
565         for (int n = 0; n < parent.numChildren; ++n)
566         {
567             if (parent.children[n] == node)
568             {
569                 childIndex = n;
570                 break;
571             }
572         }
573 
574         // has left sibling?
575         if (childIndex != 0)
576             left = parent.children[childIndex-1];
577 
578         // has right sibling?
579         if (childIndex + 1 < parent.numChildren)
580             right = parent.children[childIndex+1];
581 
582         assert(left || right); // one of those exists
583 
584         if (left && left.numKeys > minKeys)
585         {
586             // Take largest key from left sibling, it
587             // becomes the new pivot in parent. Old pivot
588             // erase our key (and if non-leaf, gets the left
589             // sibling right subtree + the node one child).
590             assert(left.isLeaf == node.isLeaf);
591             KeyValue largest = left.kv[left.numKeys - 1];
592             Node* rightMost = left.children[left.numKeys];
593             left.numKeys -= 1;
594             KeyValue pivot = node.parent.kv[childIndex-1];
595             node.parent.kv[childIndex-1] = largest;
596 
597             // Pivot enter at position 0.
598             // Need to shift a few kv.
599             for (int n = minKeys - 1; n > 0; --n)
600             {
601                 node.kv[n] = node.kv[n-1];
602             }
603             node.kv[0] = pivot;
604             if (!node.isLeaf)
605             {
606                 for (int n = minKeys; n > 0; --n)
607                 {
608                     node.children[n] = node.children[n-1];
609                 }
610                 node.children[0] = rightMost;
611                 rightMost.parent = node;
612             }
613             node.numKeys = minKeys;
614         }
615         else if (right && right.numKeys > minKeys)
616         {
617             // Take smallest key from right sibling, it
618             // becomes the new pivot in parent. Old pivot
619             // erase our key.
620             assert(right.isLeaf == node.isLeaf);
621             KeyValue smallest = right.kv[0];
622             Node* leftMostChild = right.children[0];
623 
624             // Delete first key (and first child, if any) of
625             // right sibling.
626             if (!node.isLeaf)
627             {
628                 for (int n = 0; n < right.numKeys; ++n)
629                     right.children[n] = right.children[n+1];
630             }
631             deleteKeyValueAtIndexAndShift(right, 0);
632 
633             KeyValue pivot = parent.kv[childIndex];
634             parent.kv[childIndex] = smallest;
635             node.kv[minKeys - 1] = pivot;
636             if (!node.isLeaf)
637             {
638                 leftMostChild.parent = node;
639                 node.children[minKeys] = leftMostChild;
640             }
641             node.numKeys = minKeys;
642         }
643         else
644         {
645             // merge with either left or right
646             if (right)
647             {
648                 mergeChild(parent, childIndex);
649             }
650             else if (left)
651             {
652                 mergeChild(parent, childIndex - 1);
653             }
654             else
655                 assert(0);
656         }
657     }
658 
659     // Merge children nth and nth+1, which must both have
660     // min amount of keys. makes one node with max amount of
661     // keys
662     void mergeChild(Node* parent, int nth)
663     {
664         Node* left = parent.children[nth];
665         Node* right = parent.children[nth+1];
666         KeyValue pivot = parent.kv[nth];
667         assert(left.isLeaf == right.isLeaf);
668 
669         // One key is missing already
670         assert(left.numKeys + right.numKeys == 2*minKeys-1);
671 
672         left.kv[left.numKeys] = pivot;
673 
674         for (int n = 0; n < right.numKeys; ++n)
675         {
676             left.kv[left.numKeys + 1 + n] = right.kv[n];
677         }
678         if (!left.isLeaf)
679         {
680             for (int n = 0; n < right.numKeys+1; ++n)
681             {
682                 left.children[left.numKeys + 1 + n] =
683                     right.children[n];
684                 assert(right.children[n].parent == right);
685                 left.children[left.numKeys + 1 + n].parent =
686                      left;
687             }
688         }
689         left.numKeys = 2 * minKeys;
690 
691         // in parent, shift all by one
692         parent.numKeys -= 1;
693         for (int n = nth; n < parent.numKeys; ++n)
694         {
695             parent.kv[n]         = parent.kv[n+1];
696             parent.children[n+1] = parent.children[n+2];
697         }
698 
699         // in case the parent has too few items
700         rebalanceAfterDeletion(parent);
701 
702         destroyFree(right);
703     }
704 
705     // internal use, delete a kv and shift remaining kv
706     void deleteKeyValueAtIndexAndShift(Node* node,
707                                        int index)
708     {
709         node.numKeys -= 1;
710         for (int n = index; n < node.numKeys; ++n)
711         {
712             node.kv[n] = node.kv[n+1];
713         }
714     }
715 
716     // node invariant
717     void checkNode(const(Node)* node,
718                    const(Node)* parent,
719                    ref int count) const
720     {
721         // Each node of the tree except the root must
722         // contain at least
723         // `minDegree − 1` keys (and hence must have at
724         // least `minDegree` children if it is not a leaf).
725         if (parent !is null)
726         {
727             assert(node.numKeys >= minKeys);
728             assert(node.numKeys <= maxKeys);
729 
730             // parent can't be a leaf
731             assert(!parent.isLeaf);
732         }
733         else
734         {
735             assert(node.numKeys >= 1);
736         }
737 
738         assert(parent is node.parent);
739 
740         count += node.numKeys;
741 
742         if (!node.isLeaf)
743         {
744             // Check child invariants.
745             for (int n = 0; n < node.numChildren(); ++n)
746             {
747                 checkNode(node.children[n], node, count);
748             }
749 
750             // Check internal key ordering
751             for (int n = 0; n + 1 < node.numKeys; ++n)
752             {
753                 const(K) k1 = node.kv[n].key;
754                 const(K) k2 = node.kv[n+1].key;
755                 static if (allowDuplicates)
756                 {
757                     assert(! _less(k2, k1));
758                 }
759                 else
760                 {
761                     assert(_less(k1, k2));
762                 }
763             }
764 
765             // Check key orderings with children. All keys
766             // of child must be inside parent range.
767             for (int n = 0; n < node.numKeys; ++n)
768             {
769                 const(K) k = node.kv[n].key;
770 
771                 // All key of left children must be smaller,
772                 // right must be larger.
773                 const(Node)* left = node.children[n];
774                 const(Node)* right = node.children[n+1];
775 
776                 for (int m = 0; m < left.numKeys; ++m)
777                 {
778                     static if (allowDuplicates)
779                         assert(! _less(k, left.kv[m].key));
780                     else
781                         assert(_less(left.kv[m].key, k));
782                 }
783 
784                 for (int m = 0; m < right.numKeys; ++m)
785                 {
786                     static if (allowDuplicates)
787                         assert(!_less(right.kv[m].key, k));
788                     else
789                         assert(_less(k, right.kv[m].key));
790                 }
791             }
792         }
793     }
794 
795     static struct KeyValue
796     {
797         K key;
798         V value;
799     }
800 
801     debug(btree)
802     public void display()
803     {
804         printf("Tree has %zu items\n", _count);
805         if (_root)
806             _root.display();
807         else
808         {
809             printf("    * no root\n");
810         }
811     }
812 
813     Node* allocateNode()
814     {
815         Node* node = mallocNew!Node();
816         node.treeRef = &this;
817         return node;
818     }
819 
820     void deallocateNode(Node* node)
821     {
822         destroyFree!Node(node);
823     }
824 
825 
826     static struct Node
827     {
828     nothrow @nogc:
829         // Is this a leaf node?
830         bool isLeaf;
831 
832         // This node stores:
833         //   - numKeys keys,
834         //   - and numKeys+1 children.
835         int numKeys;
836 
837         // Keys and values together.
838         KeyValue[maxKeys] kv;
839 
840         // (borrowed) Parent node.
841         // Is null for the root.
842         Node* parent;
843 
844         // (owning) Pointer to child nodes.
845         Node*[maxChildren] children;
846 
847         BTree* treeRef;
848 
849         /// Number of children = numKeys + 1
850         int numChildren() const
851         {
852             assert(!isLeaf); // leaves have no child
853             return numKeys + 1;
854         }
855 
856         bool isFull() const
857         {
858             return numKeys == maxKeys;
859         }
860 
861         void reclaimMemory()
862         {
863             if (isLeaf)
864                 return;
865 
866             for (int c = 0; c < numChildren(); ++c)
867             {
868                 children[c].reclaimMemory();
869             }
870 
871             treeRef.deallocateNode(&this);
872         }
873 
874         debug(btree)
875         void display()
876         {
877             printf("\nNode %p\n", &this);
878             printf("   * parent = %p\n", parent);
879             printf("   * leaf = %s\n", isLeaf ? "yes".ptr:
880                                                 "no".ptr);
881             printf("   * numKeys = %d\n", numKeys);
882 
883             if (numKeys > 0)
884             {
885                 for (int v = 0; v < numKeys; ++v)
886                 {
887                     static if (is(V == string))
888                         printf(" - key %d and value %s\n",
889                                kv[v].key, kv[v].value.ptr);
890                     else
891                         printf(" - key %d\n", kv[v].key);
892                 }
893             }
894             if (!isLeaf)
895             {
896                 for (int v = 0; v < numKeys+1; ++v)
897                 {
898                     printf(" - => child %p\n", children[v]);
899                 }
900             }
901             printf("\n");
902 
903             if (!isLeaf)
904             {
905                 for (int v = 0; v < numKeys+1; ++v)
906                 {
907                     children[v].display;
908                 }
909             }
910         }
911     }
912 
913 
914 public:
915 
916     enum RangeType
917     {
918         key,
919         value,
920         keyValue
921     }
922 
923     /// Btree Range
924     static struct BTreeRange(RangeType type)
925     {
926     nothrow @nogc:
927         this(ref BTree tree)
928         {
929             _current = tree._root;
930             if (_current is null)
931                 return;
932             while(!_current.isLeaf)
933                 _current = _current.children[0];
934         }
935 
936         this(ref const(BTree) tree)
937         {
938             // const_cast here
939             _current = cast(Node*)(tree._root);
940             if (_current is null)
941                 return;
942             while(!_current.isLeaf)
943                 _current = _current.children[0];
944         }
945 
946         bool empty() const
947         {
948             return _current is null;
949         }
950 
951         auto front()
952         {
953             static if (type == RangeType.key)
954                 return _current.kv[_curItem].key;
955             static if (type == RangeType.value)
956                 return _current.kv[_curItem].value;
957             static if (type == RangeType.keyValue)
958                 return _current.kv[_curItem];
959         }
960 
961         void popFront()
962         {
963             // Basically, _current and _curItem points
964             // at a key.
965             //
966             //       3
967             //     /   \
968             //  1-2     4-5
969             //
970             //  Case 1: increment _curItem
971             //  Case 2: increment _curItem, see that
972             //          it's out of bounds.
973             //          Go up the parent chain, find
974             //          position of child.
975             //          Repeat if needed
976             //          Then point at pivot if any, or exit.
977             //  Case 3: See that you're not in a leaf.
978             //          Go down to the leftmost leaf of the
979             //          right sub-tree.
980             //          Start at first item. This one always
981             //          exist.
982             //  Case 4: same as case 1.
983             //  Case 5: same as case 2.
984 
985             // If not a leaf, go inside the next children
986             if (!_current.isLeaf)
987             {
988                 // Case 3
989                 assert(_curItem + 1 < _current.numChildren);
990                 _current = _current.children[_curItem+1];
991                 while (!_current.isLeaf)
992                     _current = _current.children[0];
993                 _curItem = 0;
994             }
995             else
996             {
997                 _curItem += 1;
998                 if (_curItem >= _current.numKeys)
999                 {
1000                     while(true)
1001                     {
1002                         if (_current.parent is null)
1003                         {
1004                             // end of iteration
1005                             _current = null;
1006                             break;
1007                         }
1008                         else
1009                         {
1010                             // Find position of child in
1011                             // parent
1012                             int posParent = -2;
1013                             Node* c = _current;
1014                             Node* parent = _current.parent;
1015 
1016                             // Possibly there is a better
1017                             // way to do it with a stack
1018                             // somewhere. But that would
1019                             // require to know the maximum
1020                             // level.
1021                             // That, or change everything to
1022                             // be B+Tree.
1023                             size_t N = parent.numChildren();
1024                             for (int n = 0; n < N; ++n)
1025                             {
1026                                 if (parent.children[n] == c)
1027                                 {
1028                                     posParent = n;
1029                                     break;
1030                                 }
1031                             }
1032 
1033                             // else tree invalid
1034                             // not sure why I took -2
1035                             assert(posParent != -2);
1036 
1037                             if (posParent < parent.numKeys)
1038                             {
1039                                 // Point at pivot
1040                                 _current = parent;
1041                                 _curItem = posParent;
1042                                 break;
1043                             }
1044                             else
1045                             {
1046                                 // continue search upwards
1047                                 // for a pivot
1048                                 _current = parent;
1049                             }
1050                         }
1051                     }
1052                 }
1053                 else
1054                 {
1055                     // Case 1, nothing to do.
1056                 }
1057             }
1058         }
1059 
1060     private:
1061         // Next item returned by .front
1062         Node* _current;
1063         int _curItem;
1064     }
1065 }
1066 
1067 // <digression>
1068 //
1069 // A short note about B-tree "Knuth order" vs t/minDegree.
1070 //
1071 // t or "minimum degree" ensures an even number of children
1072 // in full nodes.
1073 //
1074 //  .----------------+---------------+-------------------.
1075 //  | minDegree max  | keys per node | children per node |
1076 //  |----------------+---------------+-------------------|
1077 //  |             2  |        1 to 3 |            2 to 4 |
1078 //  |             3  |        2 to 5 |            3 to 6 |
1079 //  |             4  |        3 to 7 |            4 to 8 |
1080 //  +----------------+---------------+-------------------+
1081 //
1082 // However Knuth defines that as an "order" m, which can be
1083 // an _odd_ number.
1084 //
1085 // In this case, the possible B-tree item bounds are:
1086 //
1087 //  .----------------+---------------+-------------------.
1088 //  |      m         | keys per node | children per node |
1089 //  |----------------+---------------+-------------------|
1090 //  |             4  |        1 to 3 |            2 to 4 |
1091 //  |             5  |        2 to 4 |            3 to 5 |
1092 //  |             6  |        2 to 5 |            3 to 6 |
1093 //  |             7  |        3 to 6 |            4 to 7 |
1094 //  |             8  |        3 to 7 |            4 to 8 |
1095 //  +----------------+---------------+-------------------+
1096 //
1097 // So, while things are similar for even m, they are
1098 // different for odd m and some Internet example use m = 5.
1099 // If we disallow non-even m, then it's impossible to
1100 // It makes a difference in deletion, because two minimally
1101 // filled nodes + a pivot can be merged together if m is
1102 // even.
1103 //
1104 // Our implementation does NOT allow for odd m.
1105 //
1106 // </digression>
1107 
1108 unittest // .init testing, basics
1109 {
1110     // It should be possible to use most function of an
1111     // uninitialized BTree.
1112     // All except functions returning a range will work.
1113     BTree!(int, string) m;
1114 
1115     assert(m.length == 0);
1116     assert(m.empty);
1117     assert(!m.contains(7));
1118     m.insert(7, "lol");
1119     assert(m.contains(7));
1120     assert(m[7] == "lol");
1121     m.remove(7);
1122     assert(!m.contains(7));
1123     assert(m.length == 0);
1124     assert(m.empty);
1125     assert(m._root == null);
1126 }
1127 
1128 unittest // add and delete in reverse order
1129 {
1130     enum int N = 10;
1131     for (int G = 0; G < 1; ++G)
1132     {
1133         BTree!(int, string) m;
1134         for (int k = 0; k < N; ++k)
1135         {
1136             m.insert(k, "l");
1137         }
1138 
1139         assert(m.length == N);
1140         for (int k = N-1; k >= 0; --k)
1141         {
1142             assert(1 == m.remove(k));
1143         }
1144         assert(m.length == 0);
1145         assert(m.empty);
1146     }
1147 }
1148 
1149 unittest // dupe keys
1150 {
1151     BTree!(int, int, "a < b", true) m;
1152     enum KEY = 4;
1153 
1154     for (int n = 0; n < 32; ++n)
1155     {
1156         m.insert(KEY, 1 << n);
1157     }
1158 
1159     foreach(k; m.byKey)
1160         assert(k == KEY);
1161 
1162     int r = 0;
1163     for (int n = 0; n < 32; ++n)
1164     {
1165         int* p = KEY in m;
1166         assert(p);
1167         r |= *p;
1168         assert(m.remove(KEY) == 1);
1169     }
1170     assert(r == -1); // all were popped
1171 }
1172 
1173 unittest // "in" without initialization
1174 {
1175     BTree!(int, int) m;
1176     void* p = 1 in m;
1177     assert(p is null);
1178 }
1179 
1180 unittest
1181 {
1182     BTree!(int, int) m;
1183     m.insert(4, 5);
1184     m.insert(4, 9);
1185 }