-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathBtreeBan.java
More file actions
1816 lines (1580 loc) · 110 KB
/
BtreeBan.java
File metadata and controls
1816 lines (1580 loc) · 110 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//------------------------------------------------------------------------------
// BtreeBam with memory in one block
// Philip R Brenan at appaapps dot com, Appa Apps Ltd Inc., 2025
//------------------------------------------------------------------------------
package com.AppaApps.Silicon; // Btree in a block on the surface of a silicon chip.
import java.util.*;
class BtreeBan extends Test // Manipulate a btree using only a basic array machine.
{final LayoutBam L; // The btree laid out in arrays
final int
maxKeysPerLeaf, // Maximum number of leafs in a key
maxKeysPerBranch, // Maximum number of keys in a branch
splitLeafSize, // The number of key, data pairs to split out of a leaf
splitBranchSize, // The number of key, next pairs to split out of a branch
numberOfNodes; // The number of nodes in the btree
final static int
maxDepth = 9, // Maximum depth of any realistic tree
gutter = 2, // Gutter between printed items
linesPerNode = 4, // Number of lines needed to print a node
linesPerTree = linesPerNode * maxDepth; // Number of lines needed to print a tree
static boolean debug = false; // Debugging when enabled
//D1 Construction // Create a BTree from nodes which can be branches or leaves. The data associated with the BTree is stored only in the leaves opposite the keys
BtreeBan(int MaxKeysPerLeaf, int MaxKeysPerBranch, int NumberOfNodes) // Define a BTree with the specified dimensions
{zz();
maxKeysPerLeaf = MaxKeysPerLeaf;
maxKeysPerBranch = MaxKeysPerBranch;
splitLeafSize = maxKeysPerLeaf >> 1;
splitBranchSize = maxKeysPerBranch >> 1;
numberOfNodes = NumberOfNodes;
L = layoutBtree();
L.zero("free"); L.zero("isLeaf"); L.zero("current_size"); // Clear all control information
L.zero("keys"); L.zero("data"); // Clear all data
for (int i = numberOfNodes-1; i > 1; --i) L.clear(i, "free", ""+(i-1)); // Initialize free chain by putting all the nodes on the free chain except the root (which is permanently allocated at position 0) with the low nodes first to be allocated.
L.clear(1, "freeChainHead"); // The first freed node
L.clear(0, "root"); // The root
L.clear(1, "isLeaf", "root"); // The root starts as a leaf
}
LayoutBam layoutBtree() // Layout the btree
{final int k = max(maxKeysPerBranch+1, maxKeysPerLeaf);
final int d = max(maxKeysPerBranch+1, maxKeysPerLeaf);
return new LayoutBam()
{void load() {
array("allocate"); // Result of calling allocate
array("balance_index"); // The index of the child to be balanced
array("balance_parent"); // The parent of the branch which wants to balance a child
array("branchSize"); // Get branch size
array("child"); // The child node during a descent through the tree
array("current_size", numberOfNodes); // Current size of stuck
array("data", numberOfNodes, d); // Data
array("delete_Key"); // The key to delete
array("f_data"); // Data associated with key
array("f_found"); // Whether the key was found
array("findAndDelete_Key"); // The key to find and delete
array("findAndInsert_Data"); // The data to insert
array("findAndInsert_Key"); // The key to insert
array("f_index"); // The index in the leaf or branch of the greater than or equal key
array("find_Key"); // Key to find in a tree
array("find_result_leaf"); // The node index of the leaf containing the key found by fine
array("f_inserted"); // Inserted if true
array("f_key"); // Matching found key
array("f_leaf"); // Node number of leaf found
array("freeChainHead"); // The head of the free chain
array("free", numberOfNodes); // Used to place the node on the free chain else zero if in use
array("f_success"); // Inserted or updated if true
array("hasLeavesForChildren"); // Whether the node has leaves for children
array("isFullRoot"); // Whether the root is full
array("isFull"); // Whether the node is full or not
array("isALeaf"); // Test whether a node is a leaf
array("isLeaf", numberOfNodes); // Whether each node is a leaf or a branch
array("isLow"); // Test whether the node is low on children
array("keys", numberOfNodes, k); // Keys
array("leafSize"); // The result of leaf size
array("merge_Key"); // The Key along whose path we should merge
array("mergeLeftSibling_bs");
array("mergeLeftSibling_index"); // The index of the child that wants to steal from the child in its parent
array("mergeLeftSibling_l");
array("mergeLeftSibling_left");
array("mergeLeftSibling_nl");
array("mergeLeftSibling_nr");
array("mergeLeftSibling_parent"); // The parent of the branch which wants to merge with its left sibling
array("mergeLeftSibling_r");
array("mergeLeftSibling_size");
array("mergeLeftSibling_t");
array("mergeLeftSibling"); // Whether the merge with the left sibling was successful
array("mergeRightSibling_bs");
array("mergeRightSibling_index"); // The index of the child that wants to steal from the child in its parent
array("mergeRightSibling_l");
array("mergeRightSibling_left");
array("mergeRightSibling_nl");
array("mergeRightSibling_nr");
array("mergeRightSibling_parent"); // The parent of the branch which wants to merge with its left sibling
array("mergeRightSibling_ld"); // Last child of left node
array("mergeRightSibling_pk"); // One up from split point in parent
array("mergeRightSibling_r");
array("mergeRightSibling_size");
array("mergeRightSibling_t");
array("mergeRightSibling"); // Whether the merge with the left sibling was successful
array("mergeRoot_l"); // Left child of root
array("mergeRoot_nl"); // Number in left child
array("mergeRoot_nP"); // Number in root
array("mergeRoot_nr"); // Number in right child
array("mergeRoot_pkn"); // First key in root
array("mergeRoot_r"); // Right child of node
array("parent"); // The parent node during a descent through the tree
array("put_Data"); // The data to put into the tree
array("put_Key"); // The key to put into the tree
array("root"); // Always zero indicating the location of the root which never changes
array("rootIsLeaf"); // Whether the root is a
array("s_data"); // The input data for a stuck or the resulting data found in a stuck
array("setBranch"); // Set the specified node as a branch
array("setLeaf"); // Set the specified node as a leaf
array("s_found"); // Whether a matching key was found when searching a stuck
array("s_index"); // The input index of a key,data pair in a stuck or the output index of a located key, data pair
array("s_key"); // The input key for searching or adding to a stuck and the output of a search greater than
array("splitBranch_index"); // The index in the parent of the branch to be split
array("splitBranch_l"); // New left leaf
array("splitBranch_node"); // The branch to be split
array("splitBranch_parent"); // The parent of the leag=f to be split
array("splitBranch_rk"); // Right key
array("splitBranchRoot_l"); // New left branch
array("splitBranchRoot_plk"); // Parent left key
array("splitBranchRoot_r"); // New right branch
array("splitLeaf_F"); // First of right
array("splitLeaf_fl"); // Mid point
array("splitLeaf_index"); // The index in the parent of the leaf to be split
array("splitLeaf_L"); // Last of left
array("splitLeaf_l"); // New left split out leaf
array("splitLeaf_node"); // The leaf to be split
array("splitLeaf_parent"); // The parent of the leag=f to be split
array("splitLeafRoot_first"); // First of right leaf
array("splitLeafRoot_i"); // Build left leaf from parent
array("splitLeafRoot_kv"); // Mid key
array("splitLeafRoot_last"); // Last of left leaf
array("splitLeafRoot_l"); // New left leaf
array("splitLeafRoot_r"); // New right leaf
array("stealFromLeft_bd"); // Right half of mid key
array("stealFromLeft_index"); // The index of the child that wants to steal from its left sibling in its parent
array("stealFromLeft_left"); // The index of the left child being stolen from
array("stealFromLeft_l"); // New left node
array("stealFromLeft_nl2"); // Two less than the number on the left
array("stealFromLeft_nl"); // Size of left node
array("stealFromLeft_nr"); // Size of right node
array("stealFromLeft_parent"); // The parent of the branch which wants to steal from the left to give to the right
array("stealFromLeft_r"); // Existing right node
array("stealFromLeft_td"); // Left half of mid key
array("stealFromLeft"); // Whether the steal from the left was successful
array("stealFromRight_bd"); //
array("stealFromRight_fk"); // Right child key
array("stealFromRight_index"); // The index of the child that wants to steal from the right sibling in its parent
array("stealFromRight_lk"); // Left child key
array("stealFromRight_l"); // Left child index
array("stealFromRight_nl"); // Number in left child
array("stealFromRight_nP"); // Parent branch size
array("stealFromRight_nr"); // Number in right child
array("stealFromRight_parent"); // The parent of the branch which wants to steal from the right child
array("stealFromRight_right"); // Index of sibling on right
array("stealFromRight_r"); // Right child index
array("stealFromRight_td"); //
array("stealFromRight"); // Whether the steal from the right was successful
array("stuck"); // The index of the stuck to be operated on
array("find_loop"); // Iterator for the find loop
array("put_loop"); // Iterator for the put loop
array("delete_loop"); // Iterator for the delete loop
array("merge_loop"); // Iterator for the merge loop
}
};
}
//D1 Nodes
//D2 Allocate and free // Allocate nodes for use in the tree and free them for reuse
void allocate(String a) // Allocate a node
{final String fc = "freeChainHead";
L.move(a, fc);
int f = L.get(a); // Last freed node
if (f == 0) stop("No more memory available"); // No more free nodes available
L.move(fc, "free", a); // Second to last freed node becomes head of the free chain
L.zero("free", a); L.zero("isLeaf", a); L.zero("current_size", a); // Clear all control information
L.zero("keys", a); L.zero("data", a);
}
void free(String f) // Free a new node to make it available for reuse
{final int n = L.get(f);
if (n == 0) stop("Cannot free root"); // The root is never freed
L.ones("free", f); L.ones("isLeaf", f); L.ones("current_size", f); // Invalidate all control information
L.ones("keys", f); L.ones("data", f);
L.move("free", "freeChainHead", f); // Chain this node in front of the last freed node
L.move("freeChainHead", f); // Freed node becomes head of the free chain
}
void allocLeaf (String node) {allocate(node); L.move("setLeaf", node); setLeaf();} // Allocate leaf
void allocBranch(String node) {allocate(node); L.move("setBranch", node); setBranch();} // Allocate branch
//D2 Basics // Basic operations on nodes
void getKey (String n) {L.move(n, "s_key" );} // Move current key to target
void getData (String n) {L.move(n, "s_data" );} // Move current data to target
void getStuck(String n) {L.move(n, "stuck" );} // Move current stuck to target
void getIndex(String n) {L.move(n, "s_index");} // Move current index to target
void setKey (int n) {L.set(n, "s_key");}
void setData (int n) {L.set(n, "s_data");}
void setStuck(int n) {L.set(n, "stuck");}
void setIndex(int n) {L.set(n, "s_index");}
void setKey (String n) {L.move("s_key" , n);} // Set current key
void setData (String n) {L.move("s_data", n);} // Set current data
void setStuck(String n) {L.move("stuck", n);} // Set current stuck
void setIndex(String n) {L.move("s_index", n);} // Set current index
int getFound () {return L.get("f_found");} // Whether the key was found
int getFSuccess () {return L.get("f_success");} // Inserted or updated if true
void putFSuccess (int n) {L.set(n, "f_success");} // Inserted or updated if true
void putFInserted(int n) {L.set(n, "f_inserted");} // Inserted if true
void isLeaf () {L.move("isALeaf", "isLeaf", "isALeaf");} // A leaf if the target is not zero
void rootIsLeaf () {L.move("rootIsLeaf", "isLeaf", "root");} // The root is a leaf if the target is not zero
void setLeaf () {L.clear(1, "isLeaf", "setLeaf");} // Set as leaf
void setBranch () {L.clear(0, "isLeaf", "setBranch");} // Set as branch
void setLeafRoot () {L.clear(1, "isLeaf", "root");} // Set root as leaf
void setBranchRoot() {L.clear(0, "isLeaf", "root");} // Set root as branch
void leafSize () {L.move("leafSize", "current_size", "leafSize" );} // Number of children in leaf
void branchSize () // Number of children in body of branch
{L.move("branchSize", "current_size", "branchSize");
L.addImmediate(-1, "branchSize");
}
void isFull() // The node is full
{L.move("isALeaf", "isFull");
isLeaf();
if (L.get("isALeaf") > 0) isFullLeaf(); else isFullBranch();
}
void isFullLeaf() // The leaf is full
{L.move("leafSize", "isFull");
leafSize();
L.set(L.get("leafSize") >= maxKeysPerLeaf ? 1 : 0, "isFull");
}
void isFullBranch() // The branch is full
{L.move("branchSize", "isFull");
branchSize();
L.set(L.get("branchSize") >= maxKeysPerBranch ? 1 : 0, "isFull");
}
void isFullRoot() // The root is full
{L.zero("isFull");
isFull();
L.set(L.get("isFull"), "isFullRoot");
}
void isLow() // The branch is low on children making it impossible to merge two sibling children
{L.move("branchSize", "isLow");
branchSize();
L.set(L.get("branchSize") < 2 ? 1 : 0, "isLow");
}
void hasLeavesForChildren() // Whether the branch has leaves for children
{L.move("isALeaf", "data", "hasLeavesForChildren", "0");
isLeaf();
L.set(L.get("isALeaf") > 0 ? 1 : 0, "hasLeavesForChildren");
}
//D2 Nodes // Operations on nodes
//D2 Split // Split nodes in half to increase the number of nodes in the tree
void splitLeafRoot() // Split a leaf which happens to be a full root into two half full leaves while transforming the root leaf into a branch
{final String $l = "splitLeafRoot_l"; allocLeaf($l); // Allocate new left leaf
final String $r = "splitLeafRoot_r"; allocLeaf($r); // Allocate new right leaf
final String $p = "root"; // Parent
final String $first = "splitLeafRoot_first";
final String $last = "splitLeafRoot_last";
final String $kv = "splitLeafRoot_kv";
for (int i = 0; i < splitLeafSize; i++) // Build left leaf from parent
{setStuck($p); stuck_shift();
setStuck($l); stuck_push();
}
for (int i = 0; i < splitLeafSize; i++) // Build right leaf from parent
{setStuck($p); stuck_shift();
setStuck($r); stuck_push();
}
stuck_firstElement();
getKey($first); // First of right leaf
L.move("stuck", $l); stuck_lastElement(); // Last of left leaf
getKey($last); // Last of left leaf
L.set((L.get($last) + L.get($first)) / 2, $kv); // Mid key
setBranchRoot();
setStuck($p); stuck_clear(); // Clear the root
setKey($kv); setData($l); stuck_push(); // Insert left leaf into root
setKey($p); setData($r); stuck_push(); // Insert right into root. This will be the top node and so ignored by search ... except last.
}
void splitBranchRoot() // Split a branch which happens to be a full root into two half full branches while retaining the current branch as the root
{final String $l = "splitBranchRoot_l"; allocBranch($l); // New left branch
final String $r = "splitBranchRoot_r"; allocBranch($r); // New right branch
final String $p = "root"; // Root
final String $plk = "splitBranchRoot_plk";
for (int i = 0; i < splitBranchSize; i++) // Build left child from parent
{setStuck($p); stuck_shift();
setStuck($l); stuck_push();
}
setStuck($p); stuck_shift();
getKey($plk);
setStuck($l); setKey($p); stuck_push(); // Left top
for(int i = 0; i < splitBranchSize; i++) // Build right child from parent
{setStuck($p); stuck_shift();
setStuck($r); stuck_push();
}
setStuck($p); stuck_shift();
setStuck($r); setKey($p); stuck_push(); // Right top
setStuck($p); // Clear root
stuck_clear();
setKey($plk); setData($l); stuck_push();
setKey($p); setData($r); stuck_push();
}
void splitLeaf() // Split a leaf which is not the root
{final String $p = "splitLeaf_parent"; // Parent
final String $l = "splitLeaf_l"; allocLeaf($l); // New split out leaf
final String $r = "splitLeaf_node"; // Existing leaf on right
final String $in = "splitLeaf_index"; // Index of the child to be split in its parent
final String $fl = "splitLeaf_fl";
final String $F = "splitLeaf_F";
final String $L = "splitLeaf_L";
for (int i = 0; i < splitLeafSize; i++) // Build left leaf
{setStuck($r); stuck_shift();
setStuck($l); stuck_push();
}
setStuck($r); stuck_firstElement(); getKey($F);
setStuck($l); stuck_lastElement (); getKey($L);
L.set((L.get($F) + L.get($L)) / 2, $fl);
setStuck($p); setKey($fl); setData($l); setIndex($in);
stuck_insertElementAt(); // Insert new key, next pair in parent
}
void splitBranch() // Split a branch which is not the root by splitting right to left
{final String $p = "splitBranch_parent";
final String $l = "splitBranch_l"; allocBranch($l);
final String $r = "splitBranch_node";
final String $in = "splitBranch_index";
final String $rk = "splitBranch_rk";
for (int i = 0; i < splitBranchSize; i++) // Build left branch from right
{setStuck($r); stuck_shift();
setStuck($l); stuck_push();
}
setStuck($r); stuck_shift(); // Build right branch
getKey($rk);
setStuck($l); setKey("root"); stuck_push(); // Becomes top and so is ignored by search ... except last
setStuck($p); setKey($rk); setData($l); setIndex($in);
stuck_insertElementAt();
}
void stealFromLeft() // Steal from the left sibling of the indicated child if possible to give to the right - Dennis Moore, Dennis Moore, Dennis Moore.
{final String $sfl = "stealFromLeft";
final String $P = "stealFromLeft_parent";
final String $index = "stealFromLeft_index";
final String $nl2 = "stealFromLeft_nl2";
final String $left = "stealFromLeft_left";
final String $l = "stealFromLeft_l";
final String $r = "stealFromLeft_r";
final String $nl = "stealFromLeft_nl";
final String $nr = "stealFromLeft_nr";
final String $td = "stealFromLeft_td";
final String $bd = "stealFromLeft_bd";
L.clear(0, $sfl); // Assume at the start that we cannot steal from the lrft
finished: // Return
{setStuck($P);
if (L.get($index) == 0) break finished; // Nothing to the left to steal from
L.set(L.get($index)-1, $left); // Index of left child
setIndex($left); stuck_elementAt(); getData($l);
setIndex($index); stuck_elementAt(); getData($r);
L.move("hasLeavesForChildren", $P);
hasLeavesForChildren();
leaves:
{if (L.get("hasLeavesForChildren") == 0) break leaves; // Children are leaves
L.move("leafSize", $l); leafSize(); L.move($nl, "leafSize");
L.move("leafSize", $r); leafSize(); L.move($nr, "leafSize");
if (L.get($nr) >= maxKeysPerLeaf) break finished; // Steal not possible because there is no where to put the steal
if (L.get($nl) <= 1) break finished; // Steal not allowed because it would leave the leaf sibling empty
setStuck($l); stuck_lastElement();
setStuck($r); stuck_unshift(); // Increase right
setStuck($l); stuck_pop(); // Reduce left
L.set(L.get($nl)-2, $nl2); // Index of last key on left
setIndex($nl2); stuck_elementAt(); // Last key on left
}
branches: // Children are branches
{if (L.get("hasLeavesForChildren") > 0) break branches;
L.move("branchSize", $l); branchSize(); L.move($nl, "branchSize");
L.move("branchSize", $r); branchSize(); L.move($nr, "branchSize");
if (L.get($nr) >= maxKeysPerBranch) break finished; // Steal not possible because there is no where to put the steal
if (L.get($nl) <= 1) break finished; // Steal not allowed because it would leave the left sibling empty
setStuck($l); stuck_lastElement(); getData($td); // Increase right with left top
setStuck($P); setIndex($index); stuck_elementAt();
setStuck($r); setData($td); setIndex("root"); stuck_insertElementAt(); // Increase right with left top
setStuck($l); stuck_pop(); // Remove left top
setStuck($r); stuck_firstElement(); getData($bd); // Increase right with left top
setStuck($P); setIndex($left); stuck_elementAt();
setStuck($r); setData($bd); setIndex("root"); stuck_setElementAt(); // Reduce key of parent of right
setStuck($l); stuck_lastElement(); // Last left key
}
setStuck($P); setData($l); setIndex($left); stuck_setElementAt(); // Reduce key of parent of left
L.clear(1, $sfl);
}
}
void stealFromRight() // Steal from the right sibling of the indicated child if possible
{final String $sfr = "stealFromRight"; // Whether the steal from the right was successful
final String $P = "stealFromRight_parent"; // Parent node
final String $index = "stealFromRight_index"; // Index of child stealing from the right sibling
final String $parent = "stealFromRight_parent"; // The parent of the branch which wants to steal from the right child
final String $nP = "stealFromRight_nP"; // Parent branch size
final String $right = "stealFromRight_right"; // Index of sibling on right
final String $l = "stealFromRight_l"; // Left child index
final String $lk = "stealFromRight_lk"; // Left child key
final String $fk = "stealFromRight_fk"; // Right child key
final String $r = "stealFromRight_r"; // Right child index
final String $nl = "stealFromRight_nl"; // Number in left child
final String $nr = "stealFromRight_nr"; // Number in right child
final String $td = "stealFromRight_td"; //
final String $bd = "stealFromRight_bd"; //
L.clear(0, $sfr); // Assume at the start that we cannot steal from the right
L.move("branchSize", $P); branchSize(); L.move($nP, "branchSize");
finished:
{if (L.get($index) >= L.get($nP)) break finished;
setStuck($P);
setIndex($index); stuck_elementAt(); getData($l); getKey($lk);
L.set($right, L.get($index)+1);
setIndex($right); stuck_elementAt(); getData($r);
L.move("hasLeavesForChildren", $P);
hasLeavesForChildren();
leaves: // Children are leaves
{if (L.get("hasLeavesForChildren") == 0) break leaves;
L.move("leafSize", $l); leafSize(); L.move($nl, "leafSize");
L.move("leafSize", $r); leafSize(); L.move($nr, "leafSize");
if (L.get($nl) >= maxKeysPerLeaf) break finished; // Steal not possible because there is no where to put the steal
if (L.get($nr) <= 1) break finished; // Steal not allowed because it would leave the right sibling empty
}
branches: // Children are branches
{if (L.get("hasLeavesForChildren") > 0) break branches;
L.move("branchSize", $l); branchSize(); L.move($nl, "branchSize");
L.move("branchSize", $r); branchSize(); L.move($nr, "branchSize");
if (L.get($nl) >= maxKeysPerBranch) break finished; // Steal not possible because there is no where to put the steal
if (L.get($nr) <= 1) break finished; // Steal not allowed because it would leave the right sibling empty
setStuck($l);
stuck_lastElement(); // Last element of left child
setKey($lk); setIndex($nl); stuck_setElementAt(); // Left top becomes real
}
setStuck($r); stuck_firstElement(); getKey($fk); // First element of right child
setStuck($l); stuck_push(); // Increase left
setStuck($P); setKey($fk); setData($l);
setIndex($index); stuck_setElementAt(); // Swap key of parent
setStuck($r); stuck_shift(); // Reduce right
L.clear(1, $sfr);
}
}
//D2 Merge // Merge two nodes together and free the resulting free node
void mergeRoot() // Merge into the root
{final String $nP = "mergeRoot_nP";
final String $l = "mergeRoot_l";
final String $r = "mergeRoot_r";
final String $nl = "mergeRoot_nl";
final String $nr = "mergeRoot_nr";
final String $pkn = "mergeRoot_pkn";
finished:
{rootIsLeaf();
if (L.get("rootIsLeaf") > 0) break finished;
L.clear(0, "branchSize"); branchSize(); L.move($nP, "branchSize");
if (L.get($nP) > 1) break finished;
setStuck(0);
stuck_firstElement(); getData($l);
stuck_lastElement (); getData($r);
L.move("hasLeavesForChildren", "root");
hasLeavesForChildren();
leaves:
{if (L.get("hasLeavesForChildren") == 0) break leaves; // Children are leaves
L.move("leafSize", $l); leafSize(); L.move($nl, "leafSize");
L.move("leafSize", $r); leafSize(); L.move($nr, "leafSize");
if (L.get($nl) + L.get($nr) > maxKeysPerLeaf) break finished;
setStuck("root"); stuck_clear();
for (int i = 0; i < maxKeysPerLeaf; ++i)
{if (i >= L.get($nl)) break;
setStuck($l); stuck_shift();
setStuck("root"); stuck_push();
}
for (int i = 0; i < maxKeysPerLeaf; ++i)
{if (i >= L.get($nr)) break;
setStuck($r); stuck_shift();
setStuck("root"); stuck_push();
}
L.move("setLeaf", "root"); setLeaf();
free($l);
free($r);
}
branches: // Children are branches
{if (L.get("hasLeavesForChildren") > 0) break branches;
L.move("branchSize", $l); branchSize(); L.move($nl, "branchSize");
L.move("branchSize", $r); branchSize(); L.move($nr, "branchSize");
if (L.get($nl) + 1 + L.get($nr) > maxKeysPerBranch) break finished;
setStuck("root"); stuck_firstElement(); getKey($pkn);
stuck_clear();
for (int i = 0; i < maxKeysPerBranch; ++i)
{if (i >= L.get($nl)) break;
setStuck($l); stuck_shift();
setStuck("root"); stuck_push();
}
setStuck($l); stuck_lastElement();
setStuck("root"); setKey($pkn); stuck_push();
for (int i = 0; i < maxKeysPerBranch; ++i)
{if (i >= L.get($nr)) break;
setStuck($r); stuck_shift();
setStuck("root"); stuck_push();
}
setStuck($r); stuck_lastElement(); // Top next
setStuck("root"); setKey("root"); stuck_push(); // Top so ignored by search ... except last
free($l);
free($r);
}
}
}
void mergeLeftSibling() // Merge the left sibling
{final String $mls = "mergeLeftSibling";
final String $P = "mergeLeftSibling_parent";
final String $index = "mergeLeftSibling_index";
final String $bs = "mergeLeftSibling_bs";
final String $left = "mergeLeftSibling_left";
final String $l = "mergeLeftSibling_l";
final String $r = "mergeLeftSibling_r";
final String $nl = "mergeLeftSibling_nl";
final String $nr = "mergeLeftSibling_nr";
final String $size = "mergeLeftSibling_size";
final String $t = "mergeLeftSibling_t";
L.clear(0, $mls); // Assume at the start that we will not be able to merge with the left sibling
finished:
{if (L.get($index) == 0) break finished;
L.move("branchSize", $P); branchSize(); L.move($bs, "branchSize");
if (L.get($index) >= L.get($bs)) break finished;
setStuck($P);
L.set($left, L.get($index)-1);
setIndex($left); stuck_elementAt(); getData($l);
setIndex($index); stuck_elementAt(); getData($r);
L.move("hasLeavesForChildren", $P);
hasLeavesForChildren();
leaves: // Children are leaves
{if (L.get("hasLeavesForChildren") == 0) break leaves;
L.move("leafSize", $l); leafSize(); L.move($nl, "leafSize");
L.move("leafSize", $r); leafSize(); L.move($nr, "leafSize");
if (L.get($nl) + L.get($nr) >= maxKeysPerLeaf) break finished; // Combined body would be too big
stuck_size($size, $l); // Number of entries to remove
for (int i = 0; i < maxKeysPerLeaf; i++) // Transfer left to right
{if (i >= L.get($size)) break;
setStuck($l); stuck_pop();
setStuck($r); stuck_unshift();
}
}
branches: // Children are branches
{if (L.get("hasLeavesForChildren") > 0) break branches;
L.move("branchSize", $l); branchSize(); L.move($nl, "branchSize");
L.move("branchSize", $r); branchSize(); L.move($nr, "branchSize");
if (L.get($nl) + 1 + L.get($nr) > maxKeysPerBranch) break finished; // Merge not possible because there is not enough room for the combined result
setStuck($P); setIndex($left); stuck_elementAt(); getKey($t); // Top key
setStuck($l); stuck_lastElement(); // Last element of left child
setStuck($r); setKey($t); stuck_unshift(); // Left top to right
setStuck($l); stuck_pop(); // Remove left top
L.move("branchSize", $l); branchSize();
L.set(L.get("branchSize") + 1, $size);
for (int i = 0; i < maxKeysPerBranch; i++) // Transfer left to right
{if (i >= L.get($size)) break;
setStuck($l); stuck_pop();
setStuck($r); stuck_unshift();
}
}
free($l); // Free the empty left node
setStuck($P); setIndex($left); stuck_removeElementAt(); // Reduce P on left
L.clear(1, $mls); // Success
}
}
void mergeRightSibling() // Merge the right sibling
{final String $mrs = "mergeRightSibling";
final String $P = "mergeRightSibling_parent";
final String $index = "mergeRightSibling_index";
final String $bs = "mergeRightSibling_bs";
final String $left = "mergeRightSibling_left";
final String $l = "mergeRightSibling_l";
final String $r = "mergeRightSibling_r";
final String $nl = "mergeRightSibling_nl";
final String $nr = "mergeRightSibling_nr";
final String $size = "mergeRightSibling_size";
final String $t = "mergeRightSibling_t";
final String $pk = "mergeRightSibling_pk";
final String $ld = "mergeRightSibling_ld";
L.clear(0, $mrs); // Assume at the start that it will mnot be possible to merge with the right sibling
finished:
{stuck_size1($bs, $P);
if (L.get($index) >= L.get($bs)) break finished; // No right sibling
setStuck($P);
setIndex(L.get($index)+0); stuck_elementAt(); getData($l);
setIndex(L.get($index)+1); stuck_elementAt(); getData($r);
L.move("hasLeavesForChildren", $P);
hasLeavesForChildren();
leaves: // Children are leaves
{if (L.get("hasLeavesForChildren") == 0) break leaves;
L.move("leafSize", $l); leafSize(); L.move($nl, "leafSize");
L.move("leafSize", $r); leafSize(); L.move($nr, "leafSize");
if (L.get($nl) + L.get($nr) > maxKeysPerLeaf) break finished; // Combined body would be too big for one leaf
stuck_size($size, $r); // Number of entries to remove
for (int i = 0; i < maxKeysPerLeaf; i++) // Transfer right to left
{if (i >= L.get($size)) break; // Transfer right to left
setStuck($r); stuck_shift();
setStuck($l); stuck_push();
}
}
branches: // Children are branches
{if (L.get("hasLeavesForChildren") > 0) break branches;
L.move("branchSize", $l); branchSize(); L.move($nl, "branchSize");
L.move("branchSize", $r); branchSize(); L.move($nr, "branchSize");
if (L.get($nl) + 1 + L.get($nr) > maxKeysPerBranch) break finished; // Merge not possible because there is not enough room in a single branch
setStuck($l); stuck_lastElement(); L.move($ld, "s_data"); // Last element of left child
setStuck($P); setIndex($index); stuck_elementAt(); // Parent dividing element
setStuck($l); setData($ld); setIndex($nl); stuck_setElementAt(); // Re-key left top
L.set(L.get($nr)+1, $size);
for (int i = 0; i < maxKeysPerBranch; i++) // Transfer right to left
{if (i >= L.get($size)) break;
setStuck($r); stuck_shift();
setStuck($l); stuck_push();
}
}
free($r); // Free the empty right node
setStuck($P);
setIndex(L.get($index)+1); stuck_elementAt(); getKey($pk); // One up from dividing point in parent
setIndex(L.get($index)); stuck_elementAt(); // Dividing point in parent
setKey($pk); setIndex($index); stuck_setElementAt(); // Install key of right sibling in this child
setIndex(L.get($index)+1); stuck_removeElementAt(); // Reduce parent on right
L.clear(1, "mergeRightSibling");
}
}
//D2 Balance // Balance the tree by merging and stealing
void balance() // Augment the indexed child so it has at least two children in its body
{final String $parent = "balance_parent";
final String $index = "balance_index";
finished:
{setStuck($parent); setIndex($index); stuck_elementAt();
L.move("isLow", "s_data");
isLow();
if (L.get("isLow") == 0) break finished;
L.move("stealFromLeft_parent", $parent);
L.move("stealFromLeft_index", $index);
stealFromLeft (); if (L.get("stealFromLeft") > 0) break finished;
L.move("stealFromRight_parent", $parent);
L.move("stealFromRight_index", $index);
stealFromRight(); if (L.get("stealFromRight") > 0) break finished;
L.move("mergeLeftSibling_parent", $parent);
L.move("mergeLeftSibling_index", $index);
mergeLeftSibling(); if (L.get("mergeLeftSibling") > 0) break finished;
L.move("mergeRightSibling_parent", $parent);
L.move("mergeRightSibling_index", $index);
mergeRightSibling(); if (L.get("mergeRightSibling") > 0) break finished;
}
}
//D1 Print // Print a BTree horizontally
void padStrings(StringBuilder[]S) // Pad the strings at each level of the tree so we have a vertical face to continue with - a bit like Marc Brunel's tunneling shield
{int L = 0;
for (int i = 0; i < linesPerTree; ++i) L = max(L, S[i].length()); // Maximum advance so far
L += gutter; // Gutter between printed items
for (int i = 0; i < linesPerTree; ++i) // Pad all strings to maximum advance
{S[i].append(" ".repeat(L-S[i].length()));
}
}
void printLeaf(int node, StringBuilder[]S, int level) // Print a leaf
{final StringBuilder s = S[level];
setStuck(node); final int N = L.get("current_size", "stuck");
for (int i = 0; i < N; i++) // Each element in the leaf
{setStuck(node); setIndex(i); stuck_elementAt();
s.append(String.format("%d ", L.get("s_key")));
}
if (s.length() > 0) s.setLength(s.length()-1);
s.append(String.format("=%d ", node));
padStrings(S);
}
void printBranch(int node, StringBuilder[]S, int level) // Print a branch
{setStuck(node);
final int N = L.get("current_size", "stuck")-1;
for (int i = 0; i < N; i++) // Each element in the branch
{setStuck(node); setIndex(i); stuck_elementAt();
final int k = L.get("s_key"), d = L.get("s_data");
L.set(d, "isALeaf");
isLeaf();
if (L.get("isALeaf") > 0) printLeaf(d, S, level+linesPerNode);
else printBranch(d, S, level+linesPerNode);
S[level+0].append(String.format("%d", k));
S[level+1].append(String.format("%d", d));
S[level+2].append(String.format("%d", node));
S[level+3].append(String.format("%d", i));
padStrings(S);
}
setStuck(node); setIndex(N); stuck_elementAt();
final int k = L.get("s_key"), d = L.get("s_data");
L.set(d, "isALeaf");
isLeaf();
if (L.get("isALeaf") > 0) printLeaf(d, S, level+linesPerNode);
else printBranch(d, S, level+linesPerNode);
S[level+0].append("+");
S[level+1].append(String.format("%d", d));
S[level+2].append(String.format("%d", node));
S[level+3].append(String.format("%d", N));
}
String printCollapsed(StringBuilder[]S) // Collapse horizontal representation into a string
{int N = 0;
for (int i = 0; i < linesPerTree; ++i) // Remove trailing blanks
{final StringBuilder s = S[i];
while (s.length() > 0 && Character.isWhitespace(s.charAt(s.length() - 1))) s.setLength(s.length() - 1);
N += s.length()+1;
}
final StringBuilder t = new StringBuilder(); // Concatenate each line
for (int i = 0; i < linesPerTree; ++i) // Concatenate line representing the tree
{final StringBuilder s = S[i];
if (s.length() == 0) continue;
t.append(s+"\n");
}
return ""+t; // Printed tree
}
public String toString() // Dump a tree horizontally
{L.save();
final int N = linesPerTree*linesPerNode; // A big buffer with room for several lines per node
final StringBuilder [] S = new StringBuilder[N]; // A big buffer with room for several lines per node
for (int i = 0; i < N; i++) S[i] = new StringBuilder();
rootIsLeaf();
if (L.get("rootIsLeaf") > 0) printLeaf(0, S, 0); else printBranch(0, S, 0); // Print tree
L.restore();
return printCollapsed(S); // Collapse lines into text
}
void print(String title) // Print the tree
{say("Tree: %s", title);
say(this);
}
//D1 Find // Find the data associated with a key
void find_result // Find result
(String Leaf, String Found, String Index, String Key, String Data)
{L.move("f_leaf", Leaf);
L.move("f_found", Found);
L.move("f_index", Index);
L.move("f_key", Key);
L.move("f_data", Data);
}
void findAndInsert_result(int Success, int Inserted) // Find and insert result
{putFSuccess(Success);
putFInserted(Inserted);
}
void find() // Find the data associated with a key in the tree
{final String $i = "find_loop"; // Step down iterator
final int Key = L.get("find_Key");
rootIsLeaf(); // The root is a leaf
finished:
{root:
{if (L.get("rootIsLeaf") == 0) break root;
setStuck(0); setKey(Key); stuck_search();
find_result("root", "s_found", "s_index", "s_key", "s_data");
break finished;
}
L.move("parent", "root"); // Parent starts at root which is known to be a branch
L.clear(0, $i); // Step down iterator
for (int i = 0;; i++) // Step down through tree
{setStuck("parent"); setKey(Key);
stuck_searchFirstGreaterThanOrEqualExceptLast();
getData("child");
L.move("isALeaf", "child");
isLeaf(); // Found the containing leaf
leaf:
{if (L.get("isALeaf") == 0) break leaf;
setStuck("child"); setKey(Key); stuck_search();
L.move("find_result_leaf", "child");
find_result("find_result_leaf", "s_found", "s_index", "s_key", "s_data");
break finished;
}
L.move("parent", "child"); // Step down to lower branch
L.addImmediate(1, $i); if (L.get($i) > maxDepth) break;
}
stop("Search did not terminate in a leaf");
}
}
void findAndInsert() // Find the leaf that should contain this key and insert or update it is possible
{final int Key = L.get("findAndInsert_Key");
final int Data = L.get("findAndInsert_Data");
L.move("find_Key", "findAndInsert_Key");
find(); // Find the leaf that should contain this key
finished:
{found:
{if (getFound() == 0) break found; // Found the key in the leaf so update it with the new data
setStuck("f_leaf"); setKey(Key); setData(Data); setIndex("f_index");
stuck_setElementAt();
findAndInsert_result(1, 0);
break finished;
}
L.move("isFull", "f_leaf");
isFullLeaf();
notFull:
{if (L.get("isFull") > 0) break notFull; // Leaf is not full so we can insert immediately
setStuck("f_leaf"); setKey(Key);
stuck_searchFirstGreaterThanOrEqual();
setStuck("f_leaf");
setKey(Key); setData(Data);
stuck_insertElementAt();
findAndInsert_result(1, 0);
break finished;
}
findAndInsert_result(0, 0);
}
}
//D1 Insertion // Insert a key, data pair into the tree or update and existing key with a new datum
void put() // Insert a key, data pair into the tree or update and existing key with a new datum
{final String $i = "put_loop"; // Step down iterator
final int Key = L.get("put_Key");
final int Data = L.get("put_Data");
L.set(Key, "findAndInsert_Key");
L.set(Data, "findAndInsert_Data");
findAndInsert(); // Try direct insertion with no modifications to the shape of the tree
finished:
{if (getFSuccess() > 0) break finished; // Inserted or updated successfully
isFullRoot();
fullRoot:
{if (L.get("isFullRoot") == 0) break fullRoot; // Start the insertion at the root, after splitting it if necessary
rootIsLeaf();
rootIsLeaf:
{if (L.get("rootIsLeaf") == 0) break rootIsLeaf;
splitLeafRoot();
}
rootIsBranch:
{if (L.get("rootIsLeaf") > 0) break rootIsBranch;
{splitBranchRoot();
}
}
findAndInsert(); // Splitting the root might have been enough
if (getFSuccess() > 0) break finished; // Inserted or updated successfully
}
L.move("parent", "root"); // Parent starts at root which is known to be a branch
L.clear(0, $i); // Step down iterator
for (int i = 0;; i++) // Step down from branch to branch through the tree until reaching a leaf repacking as we go
{setStuck("parent"); setKey(Key);
stuck_searchFirstGreaterThanOrEqualExceptLast();
getData("child");
L.move("isALeaf", "child");
isLeaf();
isLeaf:
{if (L.get("isALeaf") == 0) break isLeaf; // Reached a leaf
L.move("splitLeaf_node", "child");
L.move("splitLeaf_parent", "parent");
L.move("splitLeaf_index", "s_index");
splitLeaf();
findAndInsert();
L.set(Key, "merge_Key");
merge();
break finished;
}
L.move("isFull", "child");
isFullBranch();
isFullBranch:
{if (L.get("isFull") == 0) break isFullBranch;
L.move("splitBranch_node", "child");
L.move("splitBranch_parent", "parent");
L.move("splitBranch_index", "s_index");
splitBranch(); // Split the child branch in the search path for the key from the parent so the the search path does not contain a full branch above the containing leaf
setStuck("parent"); setKey(Key);
stuck_searchFirstGreaterThanOrEqualExceptLast();
getData("parent");
}
isNotFullBranch:
{if (L.get("isFull") > 0) break isNotFullBranch;
L.move("parent", "child");
}
L.addImmediate(1, $i); if (L.get($i) > maxDepth) break;
}
stop("Fallen off the end of the tree"); // The tree must be missing a leaf
}
}
//D1 Deletion // Delete a key, data pair from the tree
void findAndDelete() // Delete a key from the tree and returns its data if present without modifying the shape of tree
{final int Key = L.get("findAndDelete_Key"); // The key to find and delete
L.set(Key, "find_Key");
find(); // Find the key
finished:
{notFound:
{if (getFound() > 0) break notFound; // No such key
findAndInsert_result(0, 0);
break finished;
}
setStuck("f_leaf"); setIndex("f_index"); // Key, data pairs in the leaf
stuck_removeElementAt(); // Remove the key, data pair from the leaf
L.set(1, "f_found");