-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathMjaf.java
2052 lines (1927 loc) · 251 KB
/
Mjaf.java
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
//------------------------------------------------------------------------------
// Btree with data stored only in the leaves to simplify deletion.
// Philip R Brenan at appaapps dot com, Appa Apps Ltd Inc., 2024
//------------------------------------------------------------------------------
package com.AppaApps.Silicon; // Design, simulate and layout a binary tree on a silicon chip.
// Shall I compare thee to a summer's day?
// Thou art more lovely and more temperate:
// Rough winds do shake the darling buds of May,
// And summer's lease hath all too short a date;
// Sometime too hot the eye of heaven shines,
// And often is his gold complexion dimm'd;
// For every fair from fair sometime declines,
// By chance or nature's changing course untrimm'd;
// But now thou art for ever fairly made,
// The eye of heaven lights thy face for me,
// Nor shall death brag thou wander'st in his shade,
// When these lines being read bring life to thee!
import java.util.Stack; // Used only for printing trees which is not something that will happen on the chip
class Mjaf extends Memory.Structure // Btree algorithm but with data stored only in the leaves. The branches (interior nodes) have an odd number of keys to facilitate fission, whereas the leaves (exterior nodes) have even number of keys and matching number of data elements because data is not transferred to the parent on fission which simplifies deletions with complicating insertions.
{final int bitsPerKey; // Number of bits in key
final int bitsPerData; // Number of bits in data
final int maxKeysPerLeaf; // The maximum number of keys per leaf. This should be an even number greater than three. The maximum number of keys per branch is one less. The normal Btree algorithm requires an odd number greater than two for both leaves and branches. The difference arises because we only store data in leaves not in leaves and branches as whether classic Btree algorithm.
final int maxKeysPerBranch; // The maximum number of keys per branch.
final int maxNodes; // The maximum number of nodes in the tree
final int splitIdx; // Index of splitting key
final NodeStack nodes = new NodeStack(); // All the branch nodes - this is our memory allocation scheme
Node nodesFreeList = null; // Free nodes list
Memory.Variable keyDataStored;
//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
Node root; // The root node of the Btree
Mjaf(int BitsPerKey, int BitsPerData, int MaxKeysPerLeaf, int size) // Define a Btree with a specified maximum number of keys per leaf.
{super("tree");
final int N = MaxKeysPerLeaf;
bitsPerKey = BitsPerKey;
bitsPerData = BitsPerData;
if (N % 2 == 1) stop("# keys per leaf must be even not odd:", N);
if (N <= 3) stop("# keys per leaf must be greater than three, not:", N);
maxKeysPerLeaf = N;
maxKeysPerBranch = N-1;
splitIdx = maxKeysPerBranch >> 1; // Index of splitting key
for (int i = 0; i < size; i++) nodes.release(new Leaf()); // Pre allocate leaves as they are bigger than branches
maxNodes = size;
keyDataStored = Memory.variable("keyDataStored", BitsPerKey+1); // Field to track number of keys stored in twos complement form hence an extra bit for the sign
addField(keyDataStored);
layout(); // Layout
}
Mjaf() {this(16, 16, 4, 1000);} // Define a Btree with a minimal maximum number of keys per node
static Mjaf mjaf(int Key, int Data, int MaxKeysPerLeaf, int size) // Define a Btree with a specified maximum number of keys per leaf.
{return new Mjaf(Key, Data, MaxKeysPerLeaf, size);
}
int size() {return keyDataStored.toInt();} // Number of entries in the tree
int nodesCreated = 0; // Number of nodes created
boolean emptyTree() {return root == null;} // Test for an empty tree
class Key extends Memory.Variable // Definition of a key
{Key() {super("Key", bitsPerKey); layout();}
}
Key key(int bits)
{final Key k = new Key();
k.set(bits);
return k;
}
Key key(Memory memory)
{final Key k = new Key();
k.set(memory);
return k;
}
class Data extends Memory.Variable // Definition of data associated with a key
{Data() {super("Data", bitsPerData); layout();}
}
Data data(int bits)
{final Data d = new Data();
d.set(bits);
return d;
}
Data data(Memory memory)
{final Data d = new Data();
d.set(memory);
return d;
}
static boolean lessThanOrEqual(Key a, Key b) {return a.compareTo(b) <= 0;} // Define a new Btree of default type with a specified maximum number of keys per node
abstract class Node implements Comparable<Node> // A branch or a leaf: an interior or exterior node. Comparable so we can place them in a tree or set by node number.
{final Stuck keyNames; // Names of the keys in this branch or leaf
final int nodeNumber = ++nodesCreated; // Number of this node
Node next = null; // Linked list of free nodes
Node(int N) {keyNames = new Stuck("Stuck", N, bitsPerKey);} // Create a node
int findIndexOfKey(Key keyToFind) {return keyNames.indexOf(keyToFind);} // Find the one based index of a key in a branch node or zero if not found
Key splitKey() {return key(keyNames.elementAt(splitIdx));} // Splitting key
int size () {return keyNames.stuckSize();} // Number of elements in this leaf
void ok(String expected) {Mjaf.ok(toString(), expected);} // Check node is as expected
abstract void printHorizontally(Stack<StringBuilder>S, int l, boolean d); // Print horizontally
void traverse(Stack<Node>S) {S.push(this);} // Traverse tree placing all its nodes on a stack
public int compareTo(Node B) // Make it possible to orde nodes
{final Integer a = nodeNumber, b = B.nodeNumber;
return a.compareTo(b);
}
} // Node
Stack<Node> traverse() // Traverse tree placing all its nodes on a stack
{final Stack<Node> S = new Stack<>();
if (root != null) root.traverse(S);
return S;
}
class Branch extends Node // A branch node directs the search to the appropriate leaf
{final Stack<Node> nextLevel; // Ultimately this must become a stuck as well
Node topNode;
Branch() // Create a new branch
{super(maxKeysPerBranch);
//nextLevel = new Stuck(maxKeysPerBranch, bitsPerKey); // The number of keys in a branch is one less than the number of keys in a leaf
nextLevel = new Stack<>();
}
void clear() {topNode = null; keyNames.clear(); nextLevel.clear();} // Initialize branch keys and next
//boolean branchIsFull() {return nextLevel.isFull();} // Node should be split
boolean branchIsFull() {return nextLevel.size() >= maxKeysPerBranch;} // Node should be split
void splitRoot() // Split the root
{if (branchIsFull())
{final Key k = splitKey();
final Branch l = splitBranch(), b = branch(this);
b.putBranch(k, l);
root = b;
}
}
Branch splitBranch() // Split a branch into two branches at the indicated key
{final int K = keyNames.stuckSize(), f = splitIdx; // Number of keys currently in node
if (f < K-1) {} else stop("Split", f, "too big for branch of size:", K);
if (f < 1) stop("First", f, "too small");
final Node t = nextLevel.elementAt(f); // Top node
final Branch b = branch(t); // Recycle a branch
for (int i = 0; i < f; i++) // Remove first keys from old node to new node
{final Node n = nextLevel.firstElement(); nextLevel.removeElementAt(0);
b.keyNames .push(keyNames.shift());
b.nextLevel.push(n);
}
keyNames .shift(); // Remove central key which is no longer required
nextLevel.removeElementAt(0);
return b;
}
boolean joinableBranches(Branch a) // Check that we can join two branches
{return size() + a.size() + 1 <= maxKeysPerBranch;
}
void joinBranch(Branch Join, Key joinKeyName) // Append the second branch to the first one adding the specified key
{final int K = size(), J = Join.size(); // Number of keys currently in node
if (K + 1 + J > maxKeysPerBranch) stop("Join of branch has too many keys",
K,"+1+",J, "greater than", maxKeysPerBranch);
keyNames .push(joinKeyName.memory()); // Key to separate two halves
nextLevel.push(topNode); topNode = Join.topNode; // Current top node becomes middle of new node
for (int i = 0; i < J; i++) // Add right hand branch
{final Key k = key(Join.keyNames .elementAt(i));
final Node n = Join.nextLevel.elementAt(i);
keyNames .push(k.memory()); // Push memory associated with key
nextLevel.push(n);
}
nodes.release(Join);
}
Node findFirstGreaterOrEqual(Key keyName) // Find first key which is greater an the search key. The result is 1 based, a result of zero means all the keys were less than or equal than the search key
{final int N = size(); // Number of keys currently in node
for (int i = 0; i < N; i++) // Check each key
{final Key k = key(keyNames.elementAt(i)); // Key
final boolean l = lessThanOrEqual(keyName, k); // Compare current key with search key
if (l) return nextLevel.elementAt(i); // Current key is greater than or equal to the search key
}
return topNode;
}
void putBranch(Key keyName, Node putNode) // Insert a new node into a branch
{final int N = nextLevel.size(); // Number of keys currently in node
if (N >= maxKeysPerLeaf) stop("Too many keys in Branch");
for (int i = 0; i < N; i++) // Check each key
{final Key k = key(keyNames.elementAt(i)); // Key
final boolean l = lessThanOrEqual(keyName, k); // Compare current key with search key
if (l) // Insert new key in order
{keyNames .insertElementAt(keyName.memory(), i);
nextLevel.insertElementAt(putNode, i);
return;
}
}
keyNames .push(keyName.memory()); // Either the leaf is empty or the new key is greater than every existing key
nextLevel.push(putNode);
}
public String toString() // Print branch
{final StringBuilder s = new StringBuilder("Branch(");
final int K = keyNames.stuckSize();
for (int i = 0; i < K; i++) // Keys and next level indices
{s.append(""+keyNames.elementAt(i)+":"+
nextLevel.elementAt(i).nodeNumber+", ");
}
s.append(""+topNode.nodeNumber+")");
return s.toString();
}
void printHorizontally(Stack<StringBuilder>S, int level, boolean debug) // Print branch horizontally
{for (int i = 0; i < size(); i++)
{nextLevel.elementAt(i).printHorizontally(S, level+1, debug);
padStrings(S, level);
S.elementAt(level).append(keyNames.elementAt(i).toInt()+" ");
}
topNode.printHorizontally(S, level+1, debug);
}
void ok(String expected) {Mjaf.ok(toString(), expected);} // Check leaf
void traverse(Stack<Node>S) // Traverse tree placing all its nodes on a stack
{super.traverse(S);
for (int i = 0; i < size(); i++) nextLevel.elementAt(i).traverse(S);
topNode.traverse(S);
}
} // Branch
Branch branch(Node node) {return nodes.recycleBranch(node);} // Create a new branch
class Leaf extends Node // Create a new leaf
{final Stuck dataValues; // Data associated with each key
Leaf() // Data associated with keys in leaf
{super(maxKeysPerLeaf);
dataValues = new Stuck("Stuck", maxKeysPerLeaf, bitsPerData);
}
void clear() {keyNames.clear(); dataValues.clear();} // Clear leaf
boolean leafIsFull() {return dataValues.isFull();} // Leaf is full
void putLeaf(Key keyName, Data dataValue) // Insert a new leaf value
{final int K = keyNames.stuckSize(); // Number of keys currently in node
if (K >= maxKeysPerLeaf) stop("Too many keys in leaf");
for (int i = 0; i < K; i++)
{final Memory m = keyNames.elementAt(i); // Current key
final Key k = new Key(); // Current key
k.set(m);
if (lessThanOrEqual(keyName, k)) // Insert new key in order
{keyNames .insertElementAt(keyName .memory(), i);
dataValues.insertElementAt(dataValue.memory(), i);
keyDataStored.inc(); // Create a new entry in the leaf
return;
}
}
keyNames .push(keyName.memory()); // Either the leaf is empty or the new key is greater than every existing key
dataValues.push(dataValue.memory());
keyDataStored.inc(); // Created a new entry in the leaf
}
Leaf splitLeaf() // Split the leaf into two leafs - the new leaf consists of the indicated first elements, the old leaf retains the rest
{final int K = size(), f = maxKeysPerLeaf/2; // Number of keys currently in node
if (f < K) {} else stop("Split", f, "too big for leaf of size:", K);
if (f < 1) stop("First", f, "too small");
final Leaf l = leaf(); // Create new leaf
for (int i = 0; i < f; i++) // Transfer keys and data
{final Memory k = keyNames .shift(); // Current key as memory
final Memory d = dataValues.shift(); // Current data as memory
l.keyNames .push(k); // Transfer keys
l.dataValues.push(d); // Transfer data
}
return l; // Split out leaf
}
boolean joinableLeaves(Leaf a) // Check that we can join two leaves
{return size() + a.size() <= maxKeysPerLeaf;
}
void joinLeaf(Leaf Join) // Join the specified leaf onto the end of this leaf
{final int K = size(), J = Join.size(); // Number of keys currently in node
if (!joinableLeaves(Join)) stop("Join of leaf has too many keys", K,
"+", J, "greater than", maxKeysPerLeaf);
for (int i = 0; i < J; i++)
{final Key k = key (Join.keyNames .elementAt(i));
final Data d = data(Join.dataValues.elementAt(i));
keyNames .push(k.memory());
dataValues.push(d.memory());
}
nodes.release(Join);
}
public String toString() // Print leaf
{final StringBuilder s = new StringBuilder();
s.append("Leaf(");
final int K = keyNames.stuckSize();
for (int i = 0; i < K; i++)
s.append(""+keyNames.elementAt(i).toInt()+":"+
dataValues.elementAt(i).toInt()+", ");
if (K > 0) s.setLength(s.length()-2);
s.append(")");
return s.toString();
}
public String shortString() // Print a leaf compactly
{final StringBuilder s = new StringBuilder();
final int K = keyNames.stuckSize();
for (int i = 0; i < K; i++) s.append(""+keyNames.elementAt(i).toInt()+",");
if (K > 0) s.setLength(s.length()-1);
return s.toString();
}
void printHorizontally(Stack<StringBuilder>S, int level, boolean debug) // Print leaf horizontally
{padStrings(S, level);
S.elementAt(level).append(debug ? toString() : shortString());
padStrings(S, level);
}
void traverse(Stack<Node>S) // Traverse tree placing all its nodes on a stack
{super.traverse(S);
}
} // Leaf
Leaf leaf() {return nodes.recycleLeaf();} // Create an empty leaf
//D1 Search // Find a key, data pair
Data find(Key keyName) // Find a the data associated with a key
{if (emptyTree()) return null; // Empty tree
Node q = root; // Root of tree
for(int i = 0; i < 999 && q != null; ++i) // Step down through tree up to some reasonable limit
{if (!(q instanceof Branch)) break; // Stepped to a leaf
q = ((Branch)q).findFirstGreaterOrEqual(keyName); // Position of key
}
final int f = q.findIndexOfKey(keyName); // We have arrived at a leaf
return f == -1 ? null : data(((Leaf)q).dataValues.elementAt(f)); // Key not found or data associated with key in leaf
}
boolean findAndInsert(Key keyName, Data dataValue) // Find the leaf for a key and insert the indicated key, data pair into if possible, returning true if the insertion was possible else false.
{if (emptyTree()) // Empty tree so we can insert directly
{root = leaf(); // Create the root as a leaf
((Leaf)root).putLeaf(keyName, dataValue); // Insert key, data pair in the leaf
return true; // Successfully inserted
}
Node q = root; // Root of tree
for(int i = 0; i < 999 && q != null; ++i) // Step down through tree up to some reasonable limit
{if (!(q instanceof Branch)) break; // Stepped to a leaf
q = ((Branch)q).findFirstGreaterOrEqual(keyName); // Position of key
}
final int g = q.findIndexOfKey(keyName); // We have arrived at a leaf
final Leaf l = (Leaf)q;
if (g != -1) l.dataValues.setElementAt(dataValue, g); // Key already present in leaf
else if (l.leafIsFull()) return false; // There's no room in the leaf so return false
else l.putLeaf(keyName, dataValue); // On a leaf that is not full so we can insert directly
return true; // Inserted directly
}
//D1 Insertion // Insert keys and data into the Btree
void put(Key keyName, Data dataValue) // Insert a new key, data pair into the Btree
{if (findAndInsert(keyName, dataValue)) return; // Do a fast insert if possible, thisis increasingly likely in trees with large leaves
if (root instanceof Leaf) // Insert into root as a leaf
{if (!((Leaf)root).leafIsFull()) ((Leaf)root).putLeaf(keyName, dataValue); // Still room in the root while it is is a leaf
else // Insert into root as a leaf which is full
{final Leaf l = ((Leaf)root).splitLeaf(); // New left hand side of root
final Key k = l.splitKey(); // Splitting key
final Branch b = branch(root); // New root with old root to right
b.putBranch(k, l); // Insert left hand node all of whose elements are less than the first element of what was the root
final Leaf f = lessThanOrEqual(keyName, k) ? l : (Leaf)root; // Choose leaf
f.putLeaf(keyName, dataValue); // Place in leaf
root = b; // The root now has just one entry in it - the splitting eky
}
return;
}
else ((Branch)root).splitRoot(); // Split full root which is a branch not a leaf
Branch p = (Branch)root; Node q = p; // The root has already been split so the parent child relationship will be established
for(int i = 0; i < 999; ++i) // Step down through tree to find the required leaf, splitting as we go
{if (!(q instanceof Branch)) break; // Stepped to a branch
if (((Branch)q).branchIsFull()) // Split the branch because it is full and we might need to insert below it requiring a slot in this node
{final Key k = q.splitKey(); // Splitting key
final Branch l = ((Branch)q).splitBranch(); // New lower node
p.putBranch(k, l); // Place splitting key in parent
((Branch)root).splitRoot(); // Root might need to be split to re-establish the invariants at start of loop
if (lessThanOrEqual(keyName, k)) q = l; // Position on lower node if search key is less than splitting key
}
p = (Branch)q; // Step parent down
q = p.findFirstGreaterOrEqual(keyName); // The node does not need splitting
}
final Leaf l = (Leaf)q;
final int g = l.findIndexOfKey(keyName); // Locate index of key
if (g != -1) l.dataValues.setElementAt((Memory)dataValue, g); // Key already present in leaf
else if (l.leafIsFull()) // Split the node because it is full and we might need to insert below it requiring a slot in this node
{final Key k = l.splitKey();
final Leaf e = l.splitLeaf();
p.putBranch(k, e);
if (lessThanOrEqual(keyName, k)) e.putLeaf(keyName, dataValue); // Insert key in the appropriate split leaf
else l.putLeaf(keyName, dataValue);
}
else l.putLeaf(keyName, dataValue); // On a leaf that is not full so we can insert directly
} // put
//D1 Deletion // Delete a key from a Btree
Data delete(Key keyName) // Delete a key from a tree
{if (emptyTree()) return null; // The tree is empty
final Data foundData = find(keyName); // Find the data associated with the key
if (foundData == null) return null; // The key is not present so cannot be deleted
if (root instanceof Leaf) // Delete from root as a leaf
{final Leaf r = (Leaf)root;
final int i = r.findIndexOfKey(keyName); // Only one leaf and the key is known to be in the Btree so it must be in this leaf
r.keyNames .removeElementAt(i);
r.dataValues.removeElementAt(i);
keyDataStored.dec();
return foundData;
}
if (root.size() == 1) // If the root is a branch and only has one key so we might be able to merge its children
{final Branch r = (Branch)root;
final Node A = r.nextLevel.firstElement();
if (A instanceof Leaf)
{final Leaf a = (Leaf)A, b = (Leaf)(r.topNode);
final boolean j = a.joinableLeaves(b); // Can we merge the two leaves
if (j) // Merge the two leaves
{a.joinLeaf(b);
root = a; // New merged root
}
}
else // Merge two branches under root
{final Branch a = (Branch)A, b = (Branch)(r.topNode);
final boolean j = a.joinableBranches(b); // Can we merge the two branches
if (j) // Merge the two branches
{final Key k = key(r.keyNames.firstElement());
a.joinBranch(b, k);
root = a; // New merged root
}
}
}
Node P = root; // We now know that the root is a branch
for (int i = 0; i < 999; ++i) // Step down through tree to find the required leaf, splitting as we go
{if (!(P instanceof Branch)) break; // Stepped to a branch
final Branch p = (Branch)P;
for(int j = 0; j < p.size()-1; ++j) // See if any pair under this node can be merged
{final Node A = p.nextLevel.elementAt(j);
final Node B = p.nextLevel.elementAt(j+1);
if (A instanceof Leaf)
{final Leaf a = (Leaf)A, b = (Leaf)B;
final boolean m = a.joinableLeaves(b); // Can we merge the two leaves
if (m) // Merge the two leaves
{a.joinLeaf(b);
p.keyNames .removeElementAt(j);
p.nextLevel.removeElementAt(j+1);
}
}
else // Merge two branches
{final Branch a = (Branch)A, b = (Branch)B;
final boolean m = a.joinableBranches(b); // Can we merge the two branches
if (m) // Merge the two branches
{final Key k = key(p.keyNames.removeElementAt(j));
a.joinBranch(b, k);
p.nextLevel.removeElementAt(j+1);
}
}
}
if (p.size() > 0) // Check last pair
{final Node A = p.nextLevel.lastElement();
if (A instanceof Leaf)
{final Leaf a = (Leaf)A, b = (Leaf)p.topNode;
final boolean j = a.joinableLeaves(b); // Can we merge the two leaves
if (j) // Merge the two leaves
{a.joinLeaf(b);
p.keyNames .pop();
p.nextLevel.pop();
p.topNode = a;
}
}
else // Merge two branches
{final Branch a = (Branch)A, b = (Branch)p.topNode;
final boolean j = a.joinableBranches(b); // Can we merge the last two branches
if (j) // Merge the last two branches
{final Key k = key(p.keyNames.pop());
a.joinBranch(b, k);
p.nextLevel.pop();
p.topNode = a;
}
}
}
P = p.findFirstGreaterOrEqual(keyName); // Find key position in branch
}
keyDataStored.dec(); // Remove one entry - we are on a leaf andf the entry is known to exist
final int F = P.findIndexOfKey(keyName); // Key is known to be present
P .keyNames .removeElementAt(F);
((Leaf)P).dataValues.removeElementAt(F);
return foundData;
} // delete
//D1 Print // Print a tree
static void padStrings(Stack<StringBuilder> S, int level) // Pad a stack of strings so they all have the same length
{for (int i = S.size(); i <= level; ++i) S.push(new StringBuilder());
int m = 0;
for (StringBuilder s : S) m = m < s.length() ? s.length() : m;
for (StringBuilder s : S)
if (s.length() < m) s.append(" ".repeat(m - s.length()));
}
static String joinStrings(Stack<StringBuilder> S) // Join lines
{final StringBuilder a = new StringBuilder();
for (StringBuilder s : S) a.append(s.toString()+"|\n");
return a.toString();
}
String printHorizontally() // Print a tree horizontally
{final Stack<StringBuilder> S = new Stack<>();
if (emptyTree()) return ""; // Empty tree
S.push(new StringBuilder());
if (root instanceof Leaf) // Root is a leaf
{((Leaf)root).printHorizontally(S, 0, false);
return S.toString()+"\n";
}
final Branch b = (Branch)root; // Root is a branch
final int N = b.size();
for (int i = 0; i < N; i++) // Nodes below root
{b.nextLevel.elementAt(i).printHorizontally(S, 1, false);
S.firstElement().append(" "+b.keyNames.elementAt(i).toInt());
}
b.topNode.printHorizontally(S, 1, false);
return joinStrings(S);
}
//D1 Memory // Preallocated memory for branches and leaves to act as memory for the tree
class NodeStack // Memory for branches and leaves
{Integer size = 0, max = null, min = null; // Statistics
void release(Node n) // Release a branch
{n.next = nodesFreeList; nodesFreeList = n; // Add node to free list
++size;
final int m = size;
if (max == null) max = m; else max = max(max, m);
}
Node recycle() // Recycle a node
{if (nodesFreeList == null) stop("No more memory for branches/leaves");
final int m = size - 1;
if (min == null) min = m; else min = min(min, m);
final Node n = nodesFreeList; nodesFreeList = n.next; // Remove node from free list
--size; n.next = null; // Clear free list entry
return n;
}
Branch recycleBranch(Node node) // Recycle a branch
{recycle();
final Branch b = new Branch(); // In Java we have to create a new object
b.clear(); // Pointless in Java but in assembler we will need to do this
b.topNode = node;
return b;
}
Leaf recycleLeaf() // Recycle a leaf
{recycle();
final Leaf l = new Leaf(); // In Java we have to create a new object
l.clear(); // Pointless in Java but in assembler we will need to do this
return l;
}
public String toString() // Print statistics
{return String.format("Nodes min: %4d, current: %4d, max: %4d", min, size, max);
}
}
//D0 Tests // Test the BTree
static void test_create()
{var m = mjaf(12, 10, 6, 5);
var l = m.leaf();
l.putLeaf(m.key(2), m.data( 4));
l.putLeaf(m.key(4), m.data( 8));
l.putLeaf(m.key(1), m.data( 1));
l.putLeaf(m.key(3), m.data( 6));
l.putLeaf(m.key(5), m.data(10));
l.ok("Leaf(1:1, 2:4, 3:6, 4:8, 5:10)");
ok(l.size(), 5);
ok(l.findIndexOfKey(m.key(3)), 2);
ok(l.findIndexOfKey(m.key(9)), -1);
ok(m.keyDataStored.toInt(), 5);
}
static void test_leaf_split()
{var m = mjaf(12, 12, 6, 5);
var l = m.leaf();
l.putLeaf(m.key(2), m.data(4));
l.putLeaf(m.key(4), m.data(8));
l.putLeaf(m.key(1), m.data(1));
l.putLeaf(m.key(3), m.data(6));
l.putLeaf(m.key(5), m.data(10));
l.ok("Leaf(1:1, 2:4, 3:6, 4:8, 5:10)");
}
static void test_leaf_split_in_half()
{var m = mjaf(12, 12, 6, 5);
var l = m.leaf();
l.putLeaf(m.key(2), m.data(4));
l.putLeaf(m.key(4), m.data(8));
l.putLeaf(m.key(1), m.data(1));
l.putLeaf(m.key(3), m.data(6));
l.putLeaf(m.key(5), m.data(10));
l.putLeaf(m.key(6), m.data(12));
l.ok("Leaf(1:1, 2:4, 3:6, 4:8, 5:10, 6:12)");
var k = l.splitLeaf();
k.ok("Leaf(1:1, 2:4, 3:6)");
l.ok("Leaf(4:8, 5:10, 6:12)");
}
static void test_branch()
{var m = mjaf(12, 12, 6, 8);
var a = m.leaf();
var b = m.leaf();
var c = m.leaf();
var d = m.leaf();
var e = m.leaf();
var f = m.leaf();
Mjaf.Branch B = m.branch(f);
B.putBranch(m.key(1), a); ok(B.branchIsFull(), false);
B.putBranch(m.key(2), b); ok(B.branchIsFull(), false);
B.putBranch(m.key(3), c); ok(B.branchIsFull(), false);
B.putBranch(m.key(4), d); ok(B.branchIsFull(), false);
B.putBranch(m.key(5), e); ok(B.branchIsFull(), true);
//B.ok("Branch(1:1, 2:2, 3:3, 4:4, 5:5, 6)");
ok(B.findIndexOfKey(m.key(3)), 2);
ok(B.splitKey().toInt(), 3);
ok(B.size(), 5);
}
static void test_branch_full()
{var m = mjaf(12, 12, 6, 8);
var a = m.leaf();
var b = m.leaf();
var c = m.leaf();
var d = m.leaf();
var e = m.leaf();
var f = m.leaf();
Mjaf.Branch B = m.branch(f);
B.putBranch(m.key(1), a);
B.putBranch(m.key(2), b);
B.putBranch(m.key(3), c);
B.putBranch(m.key(4), d);
B.putBranch(m.key(5), e);
//B.ok("Branch(1:1, 2:2, 3:3, 4:4, 5:5, 6)");
ok(B.splitKey().toInt(), 3);
ok(B.branchIsFull(), true);
ok(B.size(), 5);
Mjaf.Branch C = B.splitBranch();
//C.ok("Branch(1:1, 2:2, 3)");
//B.ok("Branch(4:4, 5:5, 6)");
}
static void test_pad_strings()
{final Stack<StringBuilder> S = new Stack<>();
padStrings(S, 0); S.lastElement().append(11);
padStrings(S, 1); S.lastElement().append(22);
padStrings(S, 2); S.lastElement().append(33);
padStrings(S, 3); S.lastElement().append(44);
padStrings(S, 3);
final String s = joinStrings(S);
ok(s, """
11 |
22 |
33 |
44|
""");
}
static void test_branch_greater_than_or_equal()
{var m = mjaf(12, 12, 6, 6);
var a = m.leaf();
var b = m.leaf();
var c = m.leaf();
var d = m.leaf();
var e = m.leaf();
var f = m.leaf();
var g = m.leaf();
Mjaf.Branch B = m.branch(g);
B.putBranch(m.key( 2), a);
B.putBranch(m.key( 4), b);
B.putBranch(m.key( 6), c);
B.putBranch(m.key( 8), d);
B.putBranch(m.key(10), e);
B.putBranch(m.key(12), f);
B.ok("Branch(2:1, 4:2, 6:3, 8:4, 10:5, 12:6, 7)");
ok(B.findFirstGreaterOrEqual(m.key(4)), 2);
ok(B.findFirstGreaterOrEqual(m.key(3)), 2);
ok(B.findFirstGreaterOrEqual(m.key(2)), 1);
}
static void test_insert(int N, boolean debug, String expected)
{var m = mjaf(12, 12, 4, N<<1);
for (int i = 0; i < N; i++) m.put(m.key(i), m.data(i<<1));
if (debug) say(m.printHorizontally());
ok(m.printHorizontally(), expected);
if (debug) stop("Debugging stopped");
}
static void test_insert_reverse(int N, boolean debug, String expected)
{var m = mjaf(12, 12, 4, N<<1);
for (int i = N; i >= 0; i--) m.put(m.key(i), m.data(i<<1));
if (debug) say(m.printHorizontally());
ok(m.printHorizontally(), expected);
if (debug) stop("Debugging stopped");
}
static void test_insert()
{if (true) test_insert(9, !true, """
1 3 5 |
0,1 2,3 4,5 6,7,8|
""");
if (true) test_insert(10, !true, """
1 3 5 |
0,1 2,3 4,5 6,7,8,9|
""");
if (true) test_insert(64, !true, """
15 31 |
7 23 39 47 |
3 11 19 27 35 43 51 55 |
1 5 9 13 17 21 25 29 33 37 41 45 49 53 57 59 |
0,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|
""");
}
static void test_insert_reverse()
{if (true) test_insert_reverse(9, !true, """
3 5 7 |
0,1,2,3 4,5 6,7 8,9|
""");
if (true) test_insert_reverse(10, !true, """
6 |
2 4 8 |
0,1,2 3,4 5,6 7,8 9,10|
""");
if (true) test_insert_reverse(64, !true, """
32 48 |
16 24 40 56 |
8 12 20 28 36 44 52 60 |
2 4 6 10 14 18 22 26 30 34 38 42 46 50 54 58 62 |
0,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|
""");
if (true) test_insert_reverse(12, !true, """
8 |
2 4 6 10 |
0,1,2 3,4 5,6 7,8 9,10 11,12|
""");
if (true) test_insert_reverse(13, !true, """
9 |
3 5 7 11 |
0,1,2,3 4,5 6,7 8,9 10,11 12,13|
""");
if (true) test_insert_reverse(14, !true, """
6 10 |
2 4 8 12 |
0,1,2 3,4 5,6 7,8 9,10 11,12 13,14|
""");
if (true) test_insert_reverse(15, !true, """
7 11 |
3 5 9 13 |
0,1,2,3 4,5 6,7 8,9 10,11 12,13 14,15|
""");
if (true) test_insert_reverse(16, !true, """
8 12 |
2 4 6 10 14 |
0,1,2 3,4 5,6 7,8 9,10 11,12 13,14 15,16|
""");
}
static int[]random_array() // Random array
{final int[]r = {27, 442, 545, 317, 511, 578, 391, 993, 858, 586, 472, 906, 658, 704, 882, 246, 261, 501, 354, 903, 854, 279, 526, 686, 987, 403, 401, 989, 650, 576, 436, 560, 806, 554, 422, 298, 425, 912, 503, 611, 135, 447, 344, 338, 39, 804, 976, 186, 234, 106, 667, 494, 690, 480, 288, 151, 773, 769, 260, 809, 438, 237, 516, 29, 376, 72, 946, 103, 961, 55, 358, 232, 229, 90, 155, 657, 681, 43, 907, 564, 377, 615, 612, 157, 922, 272, 490, 679, 830, 839, 437, 826, 577, 937, 884, 13, 96, 273, 1, 188};
return r;
}
static void test_insert_random()
{final int[]r = random_array();
var m = mjaf(12, 12, 4, r.length);
for (int i = 0; i < r.length; ++i) m.put(m.key(r[i]), m.data(i));
//stop(m.printHorizontally());
ok(m.printHorizontally(), """
511 |
186 317 658 |
103 246 403 472 578 704 858 912 |
27 39 72 135 234 261 279 344 358 391 425 442 501 545 560 611 650 686 806 830 903 961 987 |
1,13,27 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237,246 260,261 272,273,279 288,298,317 338,344 354,358 376,377,391 401,403 422,425 436,437,438,442 447,472 480,490,494,501 503,511 516,526,545 554,560 564,576,577,578 586,611 612,615,650 657,658 667,679,681,686 690,704 769,773,804,806 809,826,830 839,854,858 882,884,903 906,907,912 922,937,946,961 976,987 989,993|
""");
if (github_actions) for (int i = 0; i < r.length; ++i) ok(m.find(m.key(r[i])), m.data(i));
ok(m.find(m.key(r.length+1)) == null);
}
static void test_find()
{final int N = 64;
var m = mjaf(12, 12, 4, N<<1);
for (int i = 0; i < N; i++) m.put (m.key(i), m.data(i<<1));
for (int i = 0; i < N; i++) ok(m.find(m.key(i)).toInt(), m.data(i+i).toInt());
}
static void test_find_reverse()
{final int N = 64;
var m = mjaf(12, 12, 4, N<<1);
for (int i = N; i >= 0; i--) m.put (m.key(i), m.data(i<<1));
for (int i = N; i >= 0; i--) ok(m.find(m.key(i)).toInt(), m.data(i+i).toInt());
}
static void test_delete()
{final int[]r = random_array();
var m = mjaf(12, 12, 4, r.length);
for (int i = 0; i < r.length; ++i) m.put(m.key(r[i]), m.data(i));
for (int i = 0; i < r.length; ++i)
{var a = m.delete(m.key(r[i]));
ok(a.toInt(), i);
if (!true) // Write case statement to check deletions
{say(" case", i, "-> /*", r[i], "*/ ok(m.printHorizontally(), \"\"\"");
say(m.printHorizontally());
say("\"\"\");");
}
else switch(i) {
case 0 -> /* 27 */ ok(m.printHorizontally(), """
511 |
317 658 |
103 246 403 472 578 704 858 912 |
27 39 72 135 186 234 261 279 344 358 391 425 442 501 545 560 611 650 686 806 830 903 961 987 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237,246 260,261 272,273,279 288,298,317 338,344 354,358 376,377,391 401,403 422,425 436,437,438,442 447,472 480,490,494,501 503,511 516,526,545 554,560 564,576,577,578 586,611 612,615,650 657,658 667,679,681,686 690,704 769,773,804,806 809,826,830 839,854,858 882,884,903 906,907,912 922,937,946,961 976,987 989,993|
""");
case 1 -> /* 442 */ ok(m.printHorizontally(), """
317 511 658 |
103 246 403 472 578 704 858 912 |
27 39 72 135 186 234 261 279 344 358 391 425 442 501 545 560 611 650 686 806 830 903 961 987 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237,246 260,261 272,273,279 288,298,317 338,344 354,358 376,377,391 401,403 422,425 436,437,438 447,472 480,490,494,501 503,511 516,526,545 554,560 564,576,577,578 586,611 612,615,650 657,658 667,679,681,686 690,704 769,773,804,806 809,826,830 839,854,858 882,884,903 906,907,912 922,937,946,961 976,987 989,993|
""");
case 2 -> /* 545 */ ok(m.printHorizontally(), """
317 511 658 |
103 246 403 472 578 704 858 912 |
27 39 72 135 186 234 261 279 344 358 391 425 442 501 545 560 611 650 686 806 830 903 961 987 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237,246 260,261 272,273,279 288,298,317 338,344 354,358 376,377,391 401,403 422,425 436,437,438 447,472 480,490,494,501 503,511 516,526 554,560 564,576,577,578 586,611 612,615,650 657,658 667,679,681,686 690,704 769,773,804,806 809,826,830 839,854,858 882,884,903 906,907,912 922,937,946,961 976,987 989,993|
""");
case 3 -> /* 317 */ ok(m.printHorizontally(), """
317 511 658 |
103 246 403 472 578 704 858 912 |
27 39 72 135 186 234 261 279 344 358 391 425 442 501 545 560 611 650 686 806 830 903 961 987 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237,246 260,261 272,273,279 288,298 338,344 354,358 376,377,391 401,403 422,425 436,437,438 447,472 480,490,494,501 503,511 516,526 554,560 564,576,577,578 586,611 612,615,650 657,658 667,679,681,686 690,704 769,773,804,806 809,826,830 839,854,858 882,884,903 906,907,912 922,937,946,961 976,987 989,993|
""");
case 4 -> /* 511 */ ok(m.printHorizontally(), """
317 511 658 |
103 246 403 472 578 704 858 912 |
27 39 72 135 186 234 261 279 344 358 391 425 442 501 545 560 611 650 686 806 830 903 961 987 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237,246 260,261 272,273,279 288,298 338,344 354,358 376,377,391 401,403 422,425 436,437,438 447,472 480,490,494,501 503 516,526 554,560 564,576,577,578 586,611 612,615,650 657,658 667,679,681,686 690,704 769,773,804,806 809,826,830 839,854,858 882,884,903 906,907,912 922,937,946,961 976,987 989,993|
""");
case 5 -> /* 578 */ ok(m.printHorizontally(), """
317 511 658 |
103 246 403 472 578 704 858 912 |
27 39 72 135 186 234 261 279 344 358 391 425 442 501 560 611 650 686 806 830 903 961 987 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237,246 260,261 272,273,279 288,298 338,344 354,358 376,377,391 401,403 422,425 436,437,438 447,472 480,490,494,501 503 516,526,554,560 564,576,577 586,611 612,615,650 657,658 667,679,681,686 690,704 769,773,804,806 809,826,830 839,854,858 882,884,903 906,907,912 922,937,946,961 976,987 989,993|
""");
case 6 -> /* 391 */ ok(m.printHorizontally(), """
317 511 658 |
103 246 403 472 578 704 858 912 |
27 39 72 135 186 234 261 279 358 391 425 442 501 560 611 650 686 806 830 903 961 987 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237,246 260,261 272,273,279 288,298 338,344,354,358 376,377 401,403 422,425 436,437,438 447,472 480,490,494,501 503 516,526,554,560 564,576,577 586,611 612,615,650 657,658 667,679,681,686 690,704 769,773,804,806 809,826,830 839,854,858 882,884,903 906,907,912 922,937,946,961 976,987 989,993|
""");
case 7 -> /* 993 */ ok(m.printHorizontally(), """
317 511 658 |
103 246 403 472 578 704 858 912 |
27 39 72 135 186 234 261 279 358 391 425 442 501 560 611 650 686 806 830 903 961 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237,246 260,261 272,273,279 288,298 338,344,354,358 376,377 401,403 422,425 436,437,438 447,472 480,490,494,501 503 516,526,554,560 564,576,577 586,611 612,615,650 657,658 667,679,681,686 690,704 769,773,804,806 809,826,830 839,854,858 882,884,903 906,907,912 922,937,946,961 976,987,989|
""");
case 8 -> /* 858 */ ok(m.printHorizontally(), """
317 511 658 |
103 246 403 472 578 704 858 |
27 39 72 135 186 234 261 279 358 391 425 442 501 560 611 650 686 806 830 903 912 961 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237,246 260,261 272,273,279 288,298 338,344,354,358 376,377 401,403 422,425 436,437,438 447,472 480,490,494,501 503 516,526,554,560 564,576,577 586,611 612,615,650 657,658 667,679,681,686 690,704 769,773,804,806 809,826,830 839,854 882,884,903 906,907,912 922,937,946,961 976,987,989|
""");
case 9 -> /* 586 */ ok(m.printHorizontally(), """
317 511 658 |
103 246 403 472 578 704 858 |
27 39 72 135 186 234 261 279 358 391 425 442 501 560 611 650 686 806 830 903 912 961 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237,246 260,261 272,273,279 288,298 338,344,354,358 376,377 401,403 422,425 436,437,438 447,472 480,490,494,501 503 516,526,554,560 564,576,577 611 612,615,650 657,658 667,679,681,686 690,704 769,773,804,806 809,826,830 839,854 882,884,903 906,907,912 922,937,946,961 976,987,989|
""");
case 10 -> /* 472 */ ok(m.printHorizontally(), """
317 511 658 |
103 246 403 472 578 704 858 |
27 39 72 135 186 234 261 279 358 391 425 442 501 560 611 650 686 806 830 903 912 961 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237,246 260,261 272,273,279 288,298 338,344,354,358 376,377 401,403 422,425 436,437,438 447 480,490,494,501 503 516,526,554,560 564,576,577 611 612,615,650 657,658 667,679,681,686 690,704 769,773,804,806 809,826,830 839,854 882,884,903 906,907,912 922,937,946,961 976,987,989|
""");
case 11 -> /* 906 */ ok(m.printHorizontally(), """
317 511 658 |
103 246 403 472 578 704 858 |
27 39 72 135 186 234 261 279 358 391 425 442 501 560 611 650 686 806 830 903 912 961 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237,246 260,261 272,273,279 288,298 338,344,354,358 376,377 401,403 422,425 436,437,438 447 480,490,494,501 503 516,526,554,560 564,576,577 611 612,615,650 657,658 667,679,681,686 690,704 769,773,804,806 809,826,830 839,854 882,884,903 907,912 922,937,946,961 976,987,989|
""");
case 12 -> /* 658 */ ok(m.printHorizontally(), """
317 511 658 |
103 246 403 472 578 704 858 |
27 39 72 135 186 234 261 279 358 391 425 442 501 560 650 686 806 830 903 912 961 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237,246 260,261 272,273,279 288,298 338,344,354,358 376,377 401,403 422,425 436,437,438 447 480,490,494,501 503 516,526,554,560 564,576,577 611,612,615,650 657 667,679,681,686 690,704 769,773,804,806 809,826,830 839,854 882,884,903 907,912 922,937,946,961 976,987,989|
""");
case 13 -> /* 704 */ ok(m.printHorizontally(), """
317 511 658 |
103 246 403 472 578 704 858 |
27 39 72 135 186 234 261 279 358 391 425 442 501 560 650 686 806 830 903 912 961 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237,246 260,261 272,273,279 288,298 338,344,354,358 376,377 401,403 422,425 436,437,438 447 480,490,494,501 503 516,526,554,560 564,576,577 611,612,615,650 657 667,679,681,686 690 769,773,804,806 809,826,830 839,854 882,884,903 907,912 922,937,946,961 976,987,989|
""");
case 14 -> /* 882 */ ok(m.printHorizontally(), """
317 511 658 |
103 246 403 472 578 704 858 |
27 39 72 135 186 234 261 279 358 391 425 442 501 560 650 686 806 830 903 912 961 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237,246 260,261 272,273,279 288,298 338,344,354,358 376,377 401,403 422,425 436,437,438 447 480,490,494,501 503 516,526,554,560 564,576,577 611,612,615,650 657 667,679,681,686 690 769,773,804,806 809,826,830 839,854 884,903 907,912 922,937,946,961 976,987,989|
""");
case 15 -> /* 246 */ ok(m.printHorizontally(), """
317 511 658 |
103 246 403 472 578 704 858 |
27 39 72 135 186 234 261 279 358 391 425 442 501 560 650 686 806 830 903 912 961 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237 260,261 272,273,279 288,298 338,344,354,358 376,377 401,403 422,425 436,437,438 447 480,490,494,501 503 516,526,554,560 564,576,577 611,612,615,650 657 667,679,681,686 690 769,773,804,806 809,826,830 839,854 884,903 907,912 922,937,946,961 976,987,989|
""");
case 16 -> /* 261 */ ok(m.printHorizontally(), """
317 511 658 |
103 246 403 472 578 704 858 |
27 39 72 135 186 234 261 279 358 391 425 442 501 560 650 686 806 830 903 912 961 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237 260 272,273,279 288,298 338,344,354,358 376,377 401,403 422,425 436,437,438 447 480,490,494,501 503 516,526,554,560 564,576,577 611,612,615,650 657 667,679,681,686 690 769,773,804,806 809,826,830 839,854 884,903 907,912 922,937,946,961 976,987,989|
""");
case 17 -> /* 501 */ ok(m.printHorizontally(), """
317 511 658 |
103 246 403 472 578 704 858 |
27 39 72 135 186 234 261 279 358 391 425 442 501 560 650 686 806 830 903 912 961 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237 260 272,273,279 288,298 338,344,354,358 376,377 401,403 422,425 436,437,438 447 480,490,494 503 516,526,554,560 564,576,577 611,612,615,650 657 667,679,681,686 690 769,773,804,806 809,826,830 839,854 884,903 907,912 922,937,946,961 976,987,989|
""");
case 18 -> /* 354 */ ok(m.printHorizontally(), """
317 511 658 |
103 246 403 472 578 704 858 |
27 39 72 135 186 234 261 279 358 425 442 501 560 650 686 806 830 903 912 961 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237 260 272,273,279 288,298 338,344,358 376,377,401,403 422,425 436,437,438 447 480,490,494 503 516,526,554,560 564,576,577 611,612,615,650 657 667,679,681,686 690 769,773,804,806 809,826,830 839,854 884,903 907,912 922,937,946,961 976,987,989|
""");
case 19 -> /* 903 */ ok(m.printHorizontally(), """
317 511 658 |
103 246 403 472 578 704 858 |
27 39 72 135 186 234 261 279 358 425 442 501 560 650 686 806 830 912 961 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237 260 272,273,279 288,298 338,344,358 376,377,401,403 422,425 436,437,438 447 480,490,494 503 516,526,554,560 564,576,577 611,612,615,650 657 667,679,681,686 690 769,773,804,806 809,826,830 839,854 884,907,912 922,937,946,961 976,987,989|
""");
case 20 -> /* 854 */ ok(m.printHorizontally(), """
317 511 658 |
103 246 403 472 578 704 858 |
27 39 72 135 186 234 261 279 358 425 442 501 560 650 686 806 830 912 961 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237 260 272,273,279 288,298 338,344,358 376,377,401,403 422,425 436,437,438 447 480,490,494 503 516,526,554,560 564,576,577 611,612,615,650 657 667,679,681,686 690 769,773,804,806 809,826,830 839 884,907,912 922,937,946,961 976,987,989|
""");
case 21 -> /* 279 */ ok(m.printHorizontally(), """
317 511 658 |
103 246 403 472 578 704 858 |
27 39 72 135 186 234 279 358 425 442 501 560 650 686 806 830 912 961 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237 260,272,273 288,298 338,344,358 376,377,401,403 422,425 436,437,438 447 480,490,494 503 516,526,554,560 564,576,577 611,612,615,650 657 667,679,681,686 690 769,773,804,806 809,826,830 839 884,907,912 922,937,946,961 976,987,989|
""");
case 22 -> /* 526 */ ok(m.printHorizontally(), """
317 511 658 |
103 246 403 472 704 858 |
27 39 72 135 186 234 279 358 425 442 501 560 578 650 686 806 830 912 961 |
1,13 29,39 43,55,72 90,96,103 106,135 151,155,157,186 188,229,232,234 237 260,272,273 288,298 338,344,358 376,377,401,403 422,425 436,437,438 447 480,490,494 503 516,554,560 564,576,577 611,612,615,650 657 667,679,681,686 690 769,773,804,806 809,826,830 839 884,907,912 922,937,946,961 976,987,989|