Skip to content

Commit

Permalink
Add min() and max() functions
Browse files Browse the repository at this point in the history
  • Loading branch information
ByronBecker committed Feb 5, 2023
1 parent 9a4ac9b commit f4844ff
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 1 deletion.
68 changes: 68 additions & 0 deletions src/BTree.mo
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,22 @@ module {
}
};

/// Returns the minimum key in a BTree with its associated value. If the BTree is empty, returns null
public func min<K, V>(tree: BTree<K, V>): ?(K, V) {
switch(tree.root) {
case (#leaf(leafNode)) { getLeafMin<K, V>(leafNode) };
case (#internal(internalNode)) { getInternalMin<K, V>(internalNode) };
}
};

/// Returns the maximum key in a BTree with its associated value. If the BTree is empty, returns null
public func max<K, V>(tree: BTree<K, V>): ?(K, V) {
switch(tree.root) {
case (#leaf(leafNode)) { getLeafMax<K, V>(leafNode) };
case (#internal(internalNode)) { getInternalMax<K, V>(internalNode) };
}
};

/// Returns an ascending order BTree iterator
public func entries<K, V>(t: BTree<K, V>): Iter.Iter<(K, V)> {
switch(t.root) {
Expand Down Expand Up @@ -291,6 +307,58 @@ module {
/* Internal Library Helper functions*/
/////////////////////////////////////

// gets the max key value pair in the leaf node
func getLeafMin<K, V>({ data }: Leaf<K, V>): ?(K, V) {
if (data.count == 0) null else { data.kvs[0] }
};

// gets the min key value pair in the internal node
func getInternalMin<K, V>(internal: Internal<K, V>): ?(K, V) {
var currentInternal = internal;
var minKV: ?(K, V) = null;
label l loop {
let child = switch(currentInternal.children[0]) {
case (?child) { child };
case null { Debug.trap("UNREACHABLE_ERROR: file a bug report! In BTree.internalmin(), null child error") };
};

switch(child) {
case (#leaf(leafNode)) {
minKV := getLeafMin<K, V>(leafNode);
break l
};
case (#internal(internalNode)) { currentInternal := internalNode; };
}
};
minKV;
};

// gets the max key value pair in the leaf node
func getLeafMax<K, V>({ data }: Leaf<K, V>): ?(K, V) {
if (data.count == 0) null else { data.kvs[data.count - 1] }
};

// gets the max key value pair in the internal node
func getInternalMax<K, V>(internal: Internal<K, V>): ?(K, V) {
var currentInternal = internal;
var maxKV: ?(K, V) = null;
label l loop {
let child = switch(currentInternal.children[currentInternal.data.count]) {
case (?child) { child };
case null { Debug.trap("UNREACHABLE_ERROR: file a bug report! In BTree.internalGetMax(), null child error") };
};

switch(child) {
case (#leaf(leafNode)) {
maxKV := getLeafMax<K, V>(leafNode);
break l
};
case (#internal(internalNode)) { currentInternal := internalNode; };
}
};
maxKV;
};

// Appends all kvs in the leaf to the entriesAccumulator buffer
func appendLeafKVs<K, V>({ data }: Leaf<K, V>, entriesAccumulator: Buffer.Buffer<(K, V)>): () {
var i = 0;
Expand Down
118 changes: 117 additions & 1 deletion test/BTreeTest.mo
Original file line number Diff line number Diff line change
Expand Up @@ -3883,6 +3883,120 @@ let entriesSuite = S.suite("entries", [
]),
]);

let minSuite = S.suite("min", [
S.test("if the tree is empty, returns None",
do {
let t = BT.init<Nat, Nat>(null);
BT.min(t);
},
M.equals(T.optional<(Nat, Nat)>(
T.tuple2Testable(T.natTestable, T.natTestable),
null
))
),
S.test("if the tree root is a partially full leaf",
do {
let t = quickCreateBTreeWithKVPairs(4, Array.tabulate<Nat>(2, func(i) { i+1 }));
BT.min(t)
},
M.equals(T.optional<(Nat, Nat)>(
T.tuple2Testable(T.natTestable, T.natTestable),
?(1, 1),
))
),
S.test("if the tree root is a completely full leaf",
do {
let t = quickCreateBTreeWithKVPairs(4, Array.tabulate<Nat>(3, func(i) { i+1 }));
BT.min(t)
},
M.equals(T.optional<(Nat, Nat)>(
T.tuple2Testable(T.natTestable, T.natTestable),
?(1, 1),
))
),
S.test("if the tree root is an internal node with multiple levels",
do {
let t = quickCreateBTreeWithKVPairs(4, Array.tabulate<Nat>(22, func(i) { i+1 }));
BT.min(t);
},
M.equals(T.optional<(Nat, Nat)>(
T.tuple2Testable(T.natTestable, T.natTestable),
?(1, 1),
))
),
S.test("if the tree root is an internal node with multiple levels and some deletion",
do {
let t = quickCreateBTreeWithKVPairs(4, Array.tabulate<Nat>(22, func(i) { i+1 }));
ignore BT.delete<Nat, Nat>(t, Nat.compare, 1);
ignore BT.delete<Nat, Nat>(t, Nat.compare, 2);
ignore BT.delete<Nat, Nat>(t, Nat.compare, 3);
ignore BT.delete<Nat, Nat>(t, Nat.compare, 4);
BT.min(t);
},
M.equals(T.optional<(Nat, Nat)>(
T.tuple2Testable(T.natTestable, T.natTestable),
?(5, 5),
))
),
]);

let maxSuite = S.suite("max", [
S.test("if the tree is empty, returns None",
do {
let t = BT.init<Nat, Nat>(null);
BT.max(t);
},
M.equals(T.optional<(Nat, Nat)>(
T.tuple2Testable(T.natTestable, T.natTestable),
null
))
),
S.test("if the tree root is a partially full leaf",
do {
let t = quickCreateBTreeWithKVPairs(4, Array.tabulate<Nat>(2, func(i) { i+1 }));
BT.max(t)
},
M.equals(T.optional<(Nat, Nat)>(
T.tuple2Testable(T.natTestable, T.natTestable),
?(2, 2),
))
),
S.test("if the tree root is a completely full leaf",
do {
let t = quickCreateBTreeWithKVPairs(4, Array.tabulate<Nat>(3, func(i) { i+1 }));
BT.max(t)
},
M.equals(T.optional<(Nat, Nat)>(
T.tuple2Testable(T.natTestable, T.natTestable),
?(3, 3),
))
),
S.test("if the tree root is an internal node with multiple levels",
do {
let t = quickCreateBTreeWithKVPairs(4, Array.tabulate<Nat>(22, func(i) { i+1 }));
BT.max(t);
},
M.equals(T.optional<(Nat, Nat)>(
T.tuple2Testable(T.natTestable, T.natTestable),
?(22, 22),
))
),
S.test("if the tree root is an internal node with multiple levels and some deletion",
do {
let t = quickCreateBTreeWithKVPairs(4, Array.tabulate<Nat>(22, func(i) { i+1 }));
ignore BT.delete<Nat, Nat>(t, Nat.compare, 22);
ignore BT.delete<Nat, Nat>(t, Nat.compare, 21);
ignore BT.delete<Nat, Nat>(t, Nat.compare, 20);
ignore BT.delete<Nat, Nat>(t, Nat.compare, 19);
BT.max(t);
},
M.equals(T.optional<(Nat, Nat)>(
T.tuple2Testable(T.natTestable, T.natTestable),
?(18, 18),
))
),
]);

S.run(S.suite("BTree",
[
initSuite,
Expand All @@ -3892,6 +4006,8 @@ S.run(S.suite("BTree",
deleteSuite,
scanLimitSuite,
toArraySuite,
entriesSuite
entriesSuite,
minSuite,
maxSuite,
]
));

0 comments on commit f4844ff

Please sign in to comment.