From c35b3d92f09d5943b1f5abf62e82810e5ddc6429 Mon Sep 17 00:00:00 2001 From: Matthew Hammer Date: Thu, 23 Jun 2022 16:16:59 -0600 Subject: [PATCH 01/10] BTree. --- src/BTree.mo | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 src/BTree.mo diff --git a/src/BTree.mo b/src/BTree.mo new file mode 100644 index 00000000..21a024a5 --- /dev/null +++ b/src/BTree.mo @@ -0,0 +1,87 @@ +/// Imperative sequences as B-Trees. + +import A "Array"; +import I "Iter"; +import List "List"; +import Option "Option"; +import Order "Order"; +import P "Prelude"; +import Prim "mo:⛔"; + +module { + + /// Constants we use to shape the tree. + /// See https://en.wikipedia.org/wiki/B-tree#Definition + module Constants { + let MAX_CHILDREN = 32; + + let MIN_CHILDREN = 16; + }; + + public type Index = { + keys : [K]; + trees : [Tree]; + // well-formedness invariants (see Check sub-module): + // 1. keys.size() + 1 = subtrees.size() + // 2. for all k in keysOf(subrees[i]), + // keys[i] <= k <= keys[i + 1] + }; + + public type Data = { + key : K; + val : V; + }; + + public type Tree = { + #index : Index; + #data : Data; + }; + + /// Check that a B-Tree instance observes invariants of B-Trees. + /// Invariants ensure performance is what we expect. + /// For testing and debugging. + public module Check { + + public type Compare = { + compare : (K, K) -> Order.Order + }; + + type CompareOp = { + compare : (?K, ?K) -> Order.Order + }; + + func compareOp(c : Compare) : CompareOp = { + compare = func (k1 : ?K, k2 : ?K) : Order.Order { + switch (k1, k2) { + case (null, null) { assert false; loop { } }; + case (null, _) #less; + case (_, null) #greater; + case (?k1, ?k2) c.compare(k1, k2) + } + } + }; + + public func check(c : Compare, t : Tree) { + rec(null, compareOp(c), t, null) + }; + + func rec(lower : ?K, c : CompareOp, t : Tree, upper : ?K) { + switch t { + case (#data(d)) { + assert (c.compare(lower, ?d.key) != #greater); + assert (c.compare(?d.key, upper) != #greater); + }; + case (#index(i)) { index(lower, c, i, upper) }; + } + }; + + func index(lower : ?K, c : CompareOp, i : Index, upper : ?K) { + assert (i.keys.size() + 1 == i.trees.size()); + for (j in I.range(0, i.keys.size() + 1)) { + let lower = if (j == 0) { null } else { ?(i.keys[j - 1]) }; + let upper = if (j == i.keys.size()) { null } else { ?(i.keys[j - 1]) }; + rec(lower, c, i.trees[j], upper) + } + }; + }; +} From b95320f5bd37399c7dae6d81ab339c098ebba336 Mon Sep 17 00:00:00 2001 From: Matthew Hammer Date: Thu, 23 Jun 2022 16:39:08 -0600 Subject: [PATCH 02/10] remove already-stale invariants comment. prefer Check module. --- src/BTree.mo | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/BTree.mo b/src/BTree.mo index 21a024a5..91b0d567 100644 --- a/src/BTree.mo +++ b/src/BTree.mo @@ -21,10 +21,6 @@ module { public type Index = { keys : [K]; trees : [Tree]; - // well-formedness invariants (see Check sub-module): - // 1. keys.size() + 1 = subtrees.size() - // 2. for all k in keysOf(subrees[i]), - // keys[i] <= k <= keys[i + 1] }; public type Data = { From d81a1c3b8ae7cf6981a92a66b6d761fe2404b8c5 Mon Sep 17 00:00:00 2001 From: Matthew Hammer Date: Thu, 23 Jun 2022 18:05:48 -0600 Subject: [PATCH 03/10] revised definition. wrote find. checks need revision. --- src/BTree.mo | 69 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/src/BTree.mo b/src/BTree.mo index 91b0d567..4dee68ad 100644 --- a/src/BTree.mo +++ b/src/BTree.mo @@ -13,19 +13,20 @@ module { /// Constants we use to shape the tree. /// See https://en.wikipedia.org/wiki/B-tree#Definition module Constants { - let MAX_CHILDREN = 32; - - let MIN_CHILDREN = 16; + let MAX_CHILDREN = 4; }; - public type Index = { - keys : [K]; - trees : [Tree]; + public type Compare = { + compare : (K, K) -> Order.Order }; public type Data = { - key : K; - val : V; + data : [(K, V)]; + }; + + public type Index = { + data : Data; + trees : [Tree]; }; public type Tree = { @@ -33,15 +34,34 @@ module { #data : Data; }; + func find_data(d : Data, find_k : K, c : Compare) : ?V { + for ((k, v) in d.data.vals()) { + if (c.compare(k, find_k) == #equal) { return ?v }; + }; + return null + }; + + func find(t : Tree, k : K, c : Compare) : ?V { + switch t { + case (#data(d)) { return find_data(d, k, c) }; + case (#index(i)) { + for (j in I.range(0, i.data.data.size())) { + switch (c.compare(k, i.data.data[j].0)) { + case (#equal) { return ?i.data.data[j].1 }; + case (#less) { return find(i.trees[j], k, c) }; + case _ { } + } + }; + find(i.trees[i.data.data.size()], k, c) + }; + }; + }; + /// Check that a B-Tree instance observes invariants of B-Trees. /// Invariants ensure performance is what we expect. /// For testing and debugging. public module Check { - public type Compare = { - compare : (K, K) -> Order.Order - }; - type CompareOp = { compare : (?K, ?K) -> Order.Order }; @@ -49,7 +69,7 @@ module { func compareOp(c : Compare) : CompareOp = { compare = func (k1 : ?K, k2 : ?K) : Order.Order { switch (k1, k2) { - case (null, null) { assert false; loop { } }; + case (null, null) { assert false; loop {} }; case (null, _) #less; case (_, null) #greater; case (?k1, ?k2) c.compare(k1, k2) @@ -57,27 +77,38 @@ module { } }; + /* public func check(c : Compare, t : Tree) { rec(null, compareOp(c), t, null) }; func rec(lower : ?K, c : CompareOp, t : Tree, upper : ?K) { switch t { - case (#data(d)) { - assert (c.compare(lower, ?d.key) != #greater); - assert (c.compare(?d.key, upper) != #greater); - }; + case (#data(d)) { data(lower, c, i, upper) }; case (#index(i)) { index(lower, c, i, upper) }; } }; + func data(lower : ?K, c : CompareOp, d : Data, upper : ?K) { + let var prev_k = null; + for ((k, _) in d.data.vals()) { + assert (c.compare(prev_k, ?k) != #greater); + assert (c.compare(lower, ?k) != #greater); + assert (c.compare(?k, upper) != #greater); + prev_k := ?k; + } + }; + func index(lower : ?K, c : CompareOp, i : Index, upper : ?K) { assert (i.keys.size() + 1 == i.trees.size()); + data(lower, c, i.data, upper); for (j in I.range(0, i.keys.size() + 1)) { - let lower = if (j == 0) { null } else { ?(i.keys[j - 1]) }; - let upper = if (j == i.keys.size()) { null } else { ?(i.keys[j - 1]) }; + let lower = if (j == 0) { lower } else { ?(i.keys[j - 1]) }; + let upper = if (j == i.keys.size()) { upper } else { ?(i.keys[j - 1]) }; rec(lower, c, i.trees[j], upper) } }; + */ }; + } From 142a0d0affa2af717aab4cc0eee45ac57fe04c63 Mon Sep 17 00:00:00 2001 From: Matthew Hammer Date: Thu, 23 Jun 2022 19:19:51 -0600 Subject: [PATCH 04/10] simplify type defs. --- src/BTree.mo | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/BTree.mo b/src/BTree.mo index 4dee68ad..eba1e4ee 100644 --- a/src/BTree.mo +++ b/src/BTree.mo @@ -20,9 +20,7 @@ module { compare : (K, K) -> Order.Order }; - public type Data = { - data : [(K, V)]; - }; + public type Data = [(K, V)]; public type Index = { data : Data; @@ -34,8 +32,8 @@ module { #data : Data; }; - func find_data(d : Data, find_k : K, c : Compare) : ?V { - for ((k, v) in d.data.vals()) { + func find_data(data : Data, find_k : K, c : Compare) : ?V { + for ((k, v) in data.vals()) { if (c.compare(k, find_k) == #equal) { return ?v }; }; return null @@ -45,14 +43,14 @@ module { switch t { case (#data(d)) { return find_data(d, k, c) }; case (#index(i)) { - for (j in I.range(0, i.data.data.size())) { - switch (c.compare(k, i.data.data[j].0)) { - case (#equal) { return ?i.data.data[j].1 }; + for (j in I.range(0, i.data.size())) { + switch (c.compare(k, i.data[j].0)) { + case (#equal) { return ?i.data[j].1 }; case (#less) { return find(i.trees[j], k, c) }; case _ { } } }; - find(i.trees[i.data.data.size()], k, c) + find(i.trees[i.data.size()], k, c) }; }; }; From ffd99ec3d4ca63d8d01f0c51e95c2aa7afa11ba3 Mon Sep 17 00:00:00 2001 From: Matthew Hammer Date: Thu, 23 Jun 2022 19:29:43 -0600 Subject: [PATCH 05/10] adjust Check module. --- src/BTree.mo | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/BTree.mo b/src/BTree.mo index eba1e4ee..2381c1ee 100644 --- a/src/BTree.mo +++ b/src/BTree.mo @@ -75,21 +75,20 @@ module { } }; - /* public func check(c : Compare, t : Tree) { rec(null, compareOp(c), t, null) }; func rec(lower : ?K, c : CompareOp, t : Tree, upper : ?K) { switch t { - case (#data(d)) { data(lower, c, i, upper) }; + case (#data(d)) { data(lower, c, d, upper) }; case (#index(i)) { index(lower, c, i, upper) }; } }; func data(lower : ?K, c : CompareOp, d : Data, upper : ?K) { - let var prev_k = null; - for ((k, _) in d.data.vals()) { + var prev_k : ?K = null; + for ((k, _) in d.vals()) { assert (c.compare(prev_k, ?k) != #greater); assert (c.compare(lower, ?k) != #greater); assert (c.compare(?k, upper) != #greater); @@ -98,15 +97,14 @@ module { }; func index(lower : ?K, c : CompareOp, i : Index, upper : ?K) { - assert (i.keys.size() + 1 == i.trees.size()); + assert (i.data.size() + 1 == i.trees.size()); data(lower, c, i.data, upper); - for (j in I.range(0, i.keys.size() + 1)) { - let lower = if (j == 0) { lower } else { ?(i.keys[j - 1]) }; - let upper = if (j == i.keys.size()) { upper } else { ?(i.keys[j - 1]) }; - rec(lower, c, i.trees[j], upper) + for (j in I.range(0, i.trees.size())) { + let lower_ = if (j == 0) { lower } else { ?(i.data[j - 1].0) }; + let upper_ = if (j == i.data.size()) { upper } else { ?(i.data[j]).0 }; + rec(lower_, c, i.trees[j], upper_) } }; - */ }; } From 61334ec147b1086ab482516da835969e97d36683 Mon Sep 17 00:00:00 2001 From: Matthew Hammer Date: Fri, 15 Jul 2022 19:33:52 -0600 Subject: [PATCH 06/10] fix issues and unit test. --- src/BTree.mo | 91 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 60 insertions(+), 31 deletions(-) diff --git a/src/BTree.mo b/src/BTree.mo index 2381c1ee..8333b7d5 100644 --- a/src/BTree.mo +++ b/src/BTree.mo @@ -6,6 +6,7 @@ import List "List"; import Option "Option"; import Order "Order"; import P "Prelude"; +import Debug "Debug"; import Prim "mo:⛔"; module { @@ -17,19 +18,20 @@ module { }; public type Compare = { + show : K -> Text; compare : (K, K) -> Order.Order }; public type Data = [(K, V)]; - public type Index = { + public type Internal = { data : Data; trees : [Tree]; }; public type Tree = { - #index : Index; - #data : Data; + #internal : Internal; + #leaf : Data; }; func find_data(data : Data, find_k : K, c : Compare) : ?V { @@ -41,8 +43,8 @@ module { func find(t : Tree, k : K, c : Compare) : ?V { switch t { - case (#data(d)) { return find_data(d, k, c) }; - case (#index(i)) { + case (#leaf(d)) { return find_data(d, k, c) }; + case (#internal(i)) { for (j in I.range(0, i.data.size())) { switch (c.compare(k, i.data[j].0)) { case (#equal) { return ?i.data[j].1 }; @@ -58,50 +60,77 @@ module { /// Check that a B-Tree instance observes invariants of B-Trees. /// Invariants ensure performance is what we expect. /// For testing and debugging. + /// + /// Future refactoring --- Eventually, we can return Result or + /// Option so that both valid and invalid inputs can be inspected in + /// test cases. Doing assertions directly here is easier, for now. public module Check { - type CompareOp = { - compare : (?K, ?K) -> Order.Order + type Inf = {#infmax; #infmin; #finite : K}; + + type InfCompare = { + compare : (Inf, Inf) -> Order.Order; + show : Inf -> Text }; - func compareOp(c : Compare) : CompareOp = { - compare = func (k1 : ?K, k2 : ?K) : Order.Order { + func infCompare(c : Compare) : InfCompare = { + show = func (k : Inf) : Text { + switch k { + case (#infmax) "#infmax"; + case (#infmin) "#infmin"; + case (#finite k) "#finite(" # c.show k # ")"; + } + }; + compare = func (k1 : Inf, k2 : Inf) : Order.Order { switch (k1, k2) { - case (null, null) { assert false; loop {} }; - case (null, _) #less; - case (_, null) #greater; - case (?k1, ?k2) c.compare(k1, k2) + case (#infmin, _) #less; + case (_, #infmin) { /* nonsense case. */ assert false; loop { } }; + case (_, #infmax) #less; + case (#infmax, _) { /* nonsense case. */ assert false; loop { } }; + case (#finite(k1), #finite(k2)) c.compare(k1, k2); } } }; - public func check(c : Compare, t : Tree) { - rec(null, compareOp(c), t, null) + public func root(compare : Compare, t : Tree) { + switch t { + case (#leaf _) { rec(#infmin, infCompare(compare), t, #infmax) }; + case (#internal i) { + if (i.data.size() == 0) { assert i.trees.size() == 0; return }; + if (i.trees.size() < 2) { assert false }; + rec(#infmin, infCompare(compare), t, #infmax) + }; + } }; - func rec(lower : ?K, c : CompareOp, t : Tree, upper : ?K) { + func rec(lower : Inf, c : InfCompare, t : Tree, upper : Inf) { switch t { - case (#data(d)) { data(lower, c, d, upper) }; - case (#index(i)) { index(lower, c, i, upper) }; + case (#leaf(d)) { data(lower, c, d, upper) }; + case (#internal(i)) { internal(lower, c, i, upper) }; } }; - func data(lower : ?K, c : CompareOp, d : Data, upper : ?K) { - var prev_k : ?K = null; + func data(lower : Inf, c : InfCompare, d : Data, upper : Inf) { + var prev_k : Inf = #infmin; for ((k, _) in d.vals()) { - assert (c.compare(prev_k, ?k) != #greater); - assert (c.compare(lower, ?k) != #greater); - assert (c.compare(?k, upper) != #greater); - prev_k := ?k; - } + if false { + Debug.print (c.show (#finite k)); + }; + assert (c.compare(prev_k, #finite k) == #less); + assert (c.compare(lower, #finite k) == #less); + assert (c.compare(#finite k, upper) == #less); + prev_k := #finite k; + }; }; - func index(lower : ?K, c : CompareOp, i : Index, upper : ?K) { - assert (i.data.size() + 1 == i.trees.size()); - data(lower, c, i.data, upper); - for (j in I.range(0, i.trees.size())) { - let lower_ = if (j == 0) { lower } else { ?(i.data[j - 1].0) }; - let upper_ = if (j == i.data.size()) { upper } else { ?(i.data[j]).0 }; + func internal(lower : Inf, c : InfCompare, i : Internal, upper : Inf) { + // counts make sense when there is one tree between each pair of + // consecutive key-value pairs; no key-value pairs on the end. + assert (i.trees.size() == i.data.size() + 1); + for (j in I.range(0, i.trees.size() - 1)) { + let lower_ = if (j == 0) { lower } else { #finite(i.data[j - 1].0) }; + let upper_ = if (j + 1 == i.trees.size()) { upper } else { #finite((i.data[j]).0) + }; rec(lower_, c, i.trees[j], upper_) } }; From 0d065060dda383ddb9ea8721b256e49b2f017fdb Mon Sep 17 00:00:00 2001 From: Matthew Hammer Date: Fri, 15 Jul 2022 20:04:50 -0600 Subject: [PATCH 07/10] missing file: BTreeTest.mo --- test/BTreeTest.mo | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 test/BTreeTest.mo diff --git a/test/BTreeTest.mo b/test/BTreeTest.mo new file mode 100644 index 00000000..8f054599 --- /dev/null +++ b/test/BTreeTest.mo @@ -0,0 +1,37 @@ +import Debug "mo:base/Debug"; +import BT "mo:base/BTree"; +import Nat "mo:base/Nat"; +import Text "mo:base/Text"; + +Debug.print "BTree tests: Begin."; + +func check(t : BT.Tree){ BT.Check.root({compare=Text.compare; show=func (t:Text) : Text { t }}, t) }; + +Debug.print "empty1."; +let empty1 = #internal({data=[]; trees=[]}); +check(empty1); + +Debug.print "empty2."; +let empty2 = #leaf([]); +check(empty2); + +Debug.print "leaf of one."; +let leaf_of_one = #leaf([("oak", 1)]); +check(leaf_of_one); + +Debug.print "leaf of two."; +let leaf_of_two = #leaf([("ash", 1), ("oak", 2)]); +check(leaf_of_one); + +Debug.print "leaf of three. A-C"; +let leaf_of_three_a_c = #leaf([("apple", 1), ("ash", 4), ("crab apple", 3)]); +check(leaf_of_three_a_c); + +Debug.print "leaf of three. S-W"; +let leaf_of_three_s_w = #leaf([("salix", 1), ("sallows", 4), ("willow", 3)]); +check(leaf_of_three_s_w); + +Debug.print "binary internal."; +check(#internal({data=[("pine", 42)]; trees=[leaf_of_three_a_c, leaf_of_three_s_w]})); + +Debug.print "BTree tests: End."; From 273fb6f548c2ed261b96db1ef4706cf7d605bd58 Mon Sep 17 00:00:00 2001 From: Matthew Hammer Date: Sat, 16 Jul 2022 08:28:40 -0600 Subject: [PATCH 08/10] simpler public API; unit test for find. --- src/BTree.mo | 10 +++++----- test/BTreeTest.mo | 17 +++++++++++++++-- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/BTree.mo b/src/BTree.mo index 8333b7d5..be117b5f 100644 --- a/src/BTree.mo +++ b/src/BTree.mo @@ -34,19 +34,19 @@ module { #leaf : Data; }; - func find_data(data : Data, find_k : K, c : Compare) : ?V { + func find_data(data : Data, find_k : K, c : (K, K) -> Order.Order) : ?V { for ((k, v) in data.vals()) { - if (c.compare(k, find_k) == #equal) { return ?v }; + if (c(k, find_k) == #equal) { return ?v }; }; return null }; - func find(t : Tree, k : K, c : Compare) : ?V { - switch t { + public func find(t : Tree, k : K, c : (K, K) -> Order.Order) : ?V { + switch t { case (#leaf(d)) { return find_data(d, k, c) }; case (#internal(i)) { for (j in I.range(0, i.data.size())) { - switch (c.compare(k, i.data[j].0)) { + switch (c(k, i.data[j].0)) { case (#equal) { return ?i.data[j].1 }; case (#less) { return find(i.trees[j], k, c) }; case _ { } diff --git a/test/BTreeTest.mo b/test/BTreeTest.mo index 8f054599..2a1fb1b0 100644 --- a/test/BTreeTest.mo +++ b/test/BTreeTest.mo @@ -3,6 +3,10 @@ import BT "mo:base/BTree"; import Nat "mo:base/Nat"; import Text "mo:base/Text"; +import Suite "mo:matchers/Suite"; +import M "mo:matchers/Matchers"; +import T "mo:matchers/Testable"; + Debug.print "BTree tests: Begin."; func check(t : BT.Tree){ BT.Check.root({compare=Text.compare; show=func (t:Text) : Text { t }}, t) }; @@ -21,7 +25,7 @@ check(leaf_of_one); Debug.print "leaf of two."; let leaf_of_two = #leaf([("ash", 1), ("oak", 2)]); -check(leaf_of_one); +check(leaf_of_two); Debug.print "leaf of three. A-C"; let leaf_of_three_a_c = #leaf([("apple", 1), ("ash", 4), ("crab apple", 3)]); @@ -32,6 +36,15 @@ let leaf_of_three_s_w = #leaf([("salix", 1), ("sallows", 4), ("willow", 3)]); check(leaf_of_three_s_w); Debug.print "binary internal."; -check(#internal({data=[("pine", 42)]; trees=[leaf_of_three_a_c, leaf_of_three_s_w]})); +let binary_internal = #internal({data=[("pine", 42)]; trees=[leaf_of_three_a_c, leaf_of_three_s_w]}); +check(binary_internal); + +let _ = Suite.suite("find", [ + Suite.test("pine", + BT.find(binary_internal, "pine", Text.compare), + M.equals(T.optional(T.natTestable, ?42)) + ) +]); + Debug.print "BTree tests: End."; From 9db84ca5838aebd52fa63eeac581a0b8d7599b4b Mon Sep 17 00:00:00 2001 From: Matthew Hammer Date: Tue, 19 Jul 2022 14:56:26 -0600 Subject: [PATCH 09/10] internal compiler error. const ... captures ..., not found in static environment --- src/BTree.mo | 28 +++++++++++++++++-- test/BTreeTest.mo | 68 ++++++++++++++++++++++++++++++----------------- 2 files changed, 70 insertions(+), 26 deletions(-) diff --git a/src/BTree.mo b/src/BTree.mo index be117b5f..b086b040 100644 --- a/src/BTree.mo +++ b/src/BTree.mo @@ -3,6 +3,7 @@ import A "Array"; import I "Iter"; import List "List"; +import Text "Text"; import Option "Option"; import Order "Order"; import P "Prelude"; @@ -45,7 +46,7 @@ module { switch t { case (#leaf(d)) { return find_data(d, k, c) }; case (#internal(i)) { - for (j in I.range(0, i.data.size())) { + for (j in I.range(0, i.data.size() - 1)) { switch (c(k, i.data[j].0)) { case (#equal) { return ?i.data[j].1 }; case (#less) { return find(i.trees[j], k, c) }; @@ -57,6 +58,29 @@ module { }; }; + public module Insert { + + + + }; + + // Assert that the given B-Tree instance observes all relevant invariants. + // Used for unit tests. Show function helps debug failing tests. + // + // Note: These checks-as-assertions can be refactored into value-producing checks, + // if that seems useful. Then, they can be individual matchers tests. Again, if useful. + public func assertIsValid( + t : Tree, + compare : (K, K) -> Order.Order, + show : K -> Text) + { + Check.root({compare; show}, t) + }; + + public func assertIsValidTextKeys(t : Tree){ + Check.root({compare=Text.compare; show=func (t:Text) : Text { t }}, t) + }; + /// Check that a B-Tree instance observes invariants of B-Trees. /// Invariants ensure performance is what we expect. /// For testing and debugging. @@ -64,7 +88,7 @@ module { /// Future refactoring --- Eventually, we can return Result or /// Option so that both valid and invalid inputs can be inspected in /// test cases. Doing assertions directly here is easier, for now. - public module Check { + module Check { type Inf = {#infmax; #infmin; #finite : K}; diff --git a/test/BTreeTest.mo b/test/BTreeTest.mo index 2a1fb1b0..58c9f9a7 100644 --- a/test/BTreeTest.mo +++ b/test/BTreeTest.mo @@ -9,42 +9,62 @@ import T "mo:matchers/Testable"; Debug.print "BTree tests: Begin."; -func check(t : BT.Tree){ BT.Check.root({compare=Text.compare; show=func (t:Text) : Text { t }}, t) }; - -Debug.print "empty1."; let empty1 = #internal({data=[]; trees=[]}); -check(empty1); - -Debug.print "empty2."; let empty2 = #leaf([]); -check(empty2); - -Debug.print "leaf of one."; let leaf_of_one = #leaf([("oak", 1)]); -check(leaf_of_one); - -Debug.print "leaf of two."; let leaf_of_two = #leaf([("ash", 1), ("oak", 2)]); -check(leaf_of_two); - -Debug.print "leaf of three. A-C"; let leaf_of_three_a_c = #leaf([("apple", 1), ("ash", 4), ("crab apple", 3)]); -check(leaf_of_three_a_c); +let leaf_of_three_s_w = #leaf([("salix", 11), ("sallows", 44), ("willow", 33)]); +let binary_internal = #internal( + { + data=[("pine", 42)]; + trees=[leaf_of_three_a_c, leaf_of_three_s_w] + }); + +let _ = Suite.suite( + "constructions and checks.", + [ // These checks-as-assertions can be refactored into value-producing checks, + // if that seems useful. Then, they can be individual matchers tests. Again, if useful. + Suite.test("assertions.", try { + Debug.print "empty1."; + BT.assertIsValidTextKeys(empty1); -Debug.print "leaf of three. S-W"; -let leaf_of_three_s_w = #leaf([("salix", 1), ("sallows", 4), ("willow", 3)]); -check(leaf_of_three_s_w); + Debug.print "empty2."; + BT.assertIsValidTextKeys(empty2); -Debug.print "binary internal."; -let binary_internal = #internal({data=[("pine", 42)]; trees=[leaf_of_three_a_c, leaf_of_three_s_w]}); -check(binary_internal); + Debug.print "leaf of one."; + BT.assertIsValidTextKeys(leaf_of_one); + + Debug.print "leaf of two."; + BT.assertIsValidTextKeys(leaf_of_two); + + Debug.print "leaf of three. A-C"; + BT.assertIsValidTextKeys(leaf_of_three_a_c); + + Debug.print "leaf of three. S-W"; + BT.assertIsValidTextKeys(leaf_of_three_s_w); + + Debug.print "binary internal."; + BT.assertIsValidTextKeys(binary_internal); + + true + } catch _ { false }, + M.equals(T.bool(true)) + )]); let _ = Suite.suite("find", [ Suite.test("pine", BT.find(binary_internal, "pine", Text.compare), M.equals(T.optional(T.natTestable, ?42)) - ) + ), + Suite.test("apple", + BT.find(binary_internal, "apple", Text.compare), + M.equals(T.optional(T.natTestable, ?1)) + ), + Suite.test("willow", + BT.find(binary_internal, "willow", Text.compare), + M.equals(T.optional(T.natTestable, ?33)) + ), ]); - Debug.print "BTree tests: End."; From 4393a8138b05a9559e6b87d985a593a207f183e4 Mon Sep 17 00:00:00 2001 From: Matthew Hammer Date: Tue, 19 Jul 2022 15:00:26 -0600 Subject: [PATCH 10/10] workaround compiler issue. --- src/BTree.mo | 2 +- test/BTreeTest.mo | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/BTree.mo b/src/BTree.mo index b086b040..eaef074a 100644 --- a/src/BTree.mo +++ b/src/BTree.mo @@ -80,7 +80,7 @@ module { public func assertIsValidTextKeys(t : Tree){ Check.root({compare=Text.compare; show=func (t:Text) : Text { t }}, t) }; - + /// Check that a B-Tree instance observes invariants of B-Trees. /// Invariants ensure performance is what we expect. /// For testing and debugging. diff --git a/test/BTreeTest.mo b/test/BTreeTest.mo index 58c9f9a7..41974dbc 100644 --- a/test/BTreeTest.mo +++ b/test/BTreeTest.mo @@ -21,11 +21,13 @@ let binary_internal = #internal( trees=[leaf_of_three_a_c, leaf_of_three_s_w] }); +/* let _ = Suite.suite( "constructions and checks.", [ // These checks-as-assertions can be refactored into value-producing checks, // if that seems useful. Then, they can be individual matchers tests. Again, if useful. Suite.test("assertions.", try { +*/ Debug.print "empty1."; BT.assertIsValidTextKeys(empty1); @@ -47,10 +49,12 @@ let _ = Suite.suite( Debug.print "binary internal."; BT.assertIsValidTextKeys(binary_internal); +/* true } catch _ { false }, M.equals(T.bool(true)) )]); +*/ let _ = Suite.suite("find", [ Suite.test("pine",