diff --git a/README.md b/README.md index b6c2d68..71c0878 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,8 @@ than for system testing. - [ ] Investigate applications of Patricia tries - [x] Refactor tests to avoid some of the duplicated code -- [ ] Find out the idiom for stacking __Insert__ and __Find__ tests (avoiding mocks) +- [x] Add code and tests to allow for entries such as "slow", "slower", "slowly" +- [x] Find out the idiom for stacking __Insert__ and __Find__ tests (avoiding mocks) - [ ] Investigate whether byte-based __and__ rune-based options are viable - [ ] Find more examples of tries in use - specifically Rune-based CJKV (Chinese, Japanese, Korean, Vietnamese) - [ ] Find out whether the usual practice is to sort trie entries (the Wikipedia example __is__ sorted) diff --git a/node.go b/node.go index 1614bfd..c19b1c5 100644 --- a/node.go +++ b/node.go @@ -9,6 +9,15 @@ type Node struct { value string children []*Node childCount int + entry bool +} + +// IsEntry may be called to determine if the current node is +// terminal for an entry. Note that this node may or may not +// also be a leaf (in the case of 'slow' and 'slowly', both +// are entries but only 'slowly' is a leaf). +func (n *Node) IsEntry() bool { + return n.entry } // IsLeaf may be called to determine if the current node is a leaf. @@ -16,9 +25,9 @@ func (n *Node) IsLeaf() bool { return n.childCount == 0 } -func (n *Node) makeChildNode(s string) *Node { +func (n *Node) makeChildNode(s string, entry bool) *Node { //fmt.Printf("makingChildNode: %s\n", s) - child := makeNode(s) + child := makeNode(s, entry) n.childCount++ if n.children == nil { n.children = []*Node{&child} @@ -35,7 +44,7 @@ func (n *Node) setChildNode(newNode *Node) bool { return true } -func makeNode(s string) Node { +func makeNode(s string, isEntry bool) Node { //fmt.Printf("makingNode: %s\n", s) - return Node{value: s, childCount: 0} + return Node{value: s, childCount: 0, entry: isEntry} } diff --git a/trie.go b/trie.go index d8f10a0..8b9227e 100644 --- a/trie.go +++ b/trie.go @@ -70,19 +70,20 @@ func (t *Trie) insertRuneNode(parent *Node, n *Node, s string) bool { lenC := len(c.value) if index > 0 { if index == lenC { - return t.insertRuneNode(parent, c, s[index:]) + return t.insertRuneNode(c, c, s[index:]) } if index < lenC { - child := c.makeChildNode(c.value[index:]) + child := c.makeChildNode(c.value[index:], true) child.children = c.children - child.childCount = c.childCount + child.childCount = 0 c.setChildNode(child) //fmt.Printf("c.value: %s\n", c.value) c.value = c.value[:index] //fmt.Printf("c.value: %s\n", c.value) + c.entry = false } //fmt.Printf("making child node: %s\n", s[index:]) - c.makeChildNode(s[index:]) + c.makeChildNode(s[index:], true) t.count++ return true } @@ -90,14 +91,14 @@ func (t *Trie) insertRuneNode(parent *Node, n *Node, s string) bool { // No match in the children so attach to the parent node //fmt.Printf("parented.value2: %s\n", s) - parent.makeChildNode(s) + parent.makeChildNode(s, true) t.count++ return true } func (t *Trie) makeRuneNode(s string) { - rootRune := makeNode(s[:1]) - rootChild := makeNode(s[1:]) + rootRune := makeNode(s[:1], false) + rootChild := makeNode(s[1:], true) rootRune.children = []*Node{&rootChild} rootRune.childCount = 1 t.child = []*Node{&rootRune} diff --git a/trie_test.go b/trie_test.go index 9607d11..8ea3a4b 100644 --- a/trie_test.go +++ b/trie_test.go @@ -27,7 +27,7 @@ func TestIsNotEmpty(t *testing.T) { } } -func TestInsert(t *testing.T) { +func TestInsertR(t *testing.T) { insertTests := []struct { name string @@ -126,6 +126,291 @@ func TestInsert(t *testing.T) { } } +func TestInsertS(t *testing.T) { + + insertTests := []struct { + name string + value string + trie Trie + expectedCount int + inserted bool + }{ + { + name: "insert into empty trie", + value: "slow", + trie: getTrie(0, 's'), + expectedCount: 1, + inserted: true, + }, + { + name: "insert into trie existing element", + value: "slow", + trie: getTrie(1, 's'), + expectedCount: 1, + inserted: false, + }, + { + name: "insert into trie with one element", + value: "slower", + trie: getTrie(1, 's'), + expectedCount: 2, + inserted: true, + }, + { + name: "insert into trie with two elements", + value: "slowly", + trie: getTrie(2, 's'), + expectedCount: 3, + inserted: true, + }, + } + + for _, test := range insertTests { + inserted := test.trie.Insert(test.value) + if inserted != test.inserted { + t.Errorf("test 's' '%s': expected inserted to be %t", test.name, test.inserted) + } + if test.trie.Count() != test.expectedCount { + t.Errorf("test 's' '%s': expected count to be %d, but was %d", test.name, test.expectedCount, test.trie.Count()) + } + } +} + +func TestInsertFindS(t *testing.T) { + + trie := NewTrie() + + insertTests := []struct { + name string + value string + expectedCount int + inserted bool + }{ + { + name: "insert into empty trie", + value: "slow", + expectedCount: 1, + inserted: true, + }, + { + name: "insert into trie existing element", + value: "slow", + expectedCount: 1, + inserted: false, + }, + { + name: "insert into trie with one element", + value: "slower", + expectedCount: 2, + inserted: true, + }, + { + name: "insert into trie with two elements", + value: "slowly", + expectedCount: 3, + inserted: true, + }, + } + + for _, test := range insertTests { + inserted := trie.Insert(test.value) + if inserted != test.inserted { + t.Errorf("test 's' '%s': expected inserted to be %t", test.name, test.inserted) + } + if trie.Count() != test.expectedCount { + t.Errorf("test 's' '%s': expected count to be %d, but was %d", test.name, test.expectedCount, trie.Count()) + } + } + + findTests := []struct { + name string + value string + found bool + isEntry bool + isLeaf bool + }{ + { + name: "find existing element in 's' trie with three elements", + value: "slow", + found: true, + isEntry: true, + isLeaf: false, + }, + { + name: "find existing second element in 's' trie with three elements", + value: "slower", + found: true, + isEntry: true, + isLeaf: true, + }, + { + name: "find existing third element in 's' trie with three elements", + value: "slowly", + found: true, + isEntry: true, + isLeaf: true, + }, + } + + for _, test := range findTests { + found, n := trie.Find(test.value) + if found != test.found { + t.Errorf("test '%s': expected found to be %t", test.name, test.found) + } + if found { + if n.IsEntry() != test.isEntry { + t.Errorf("test '%s': expected isEntry to be %t", test.name, test.isEntry) + } + if n.IsLeaf() != test.isLeaf { + t.Errorf("test '%s': expected isLeaf to be %t", test.name, test.isLeaf) + } + } + } +} + +func TestInsertT(t *testing.T) { + + insertTests := []struct { + name string + value string + trie Trie + expectedCount int + inserted bool + }{ + { + name: "insert into empty trie", + value: "test", + trie: getTrie(0, 't'), + expectedCount: 1, + inserted: true, + }, + { + name: "insert into trie existing element", + value: "test", + trie: getTrie(1, 't'), + expectedCount: 1, + inserted: false, + }, + { + name: "insert into trie with one element", + value: "toaster", + trie: getTrie(1, 't'), + expectedCount: 2, + inserted: true, + }, + { + name: "insert into trie with two elements", + value: "toasting", + trie: getTrie(2, 't'), + expectedCount: 3, + inserted: true, + }, + } + + for _, test := range insertTests { + inserted := test.trie.Insert(test.value) + if inserted != test.inserted { + t.Errorf("test 't' '%s': expected inserted to be %t", test.name, test.inserted) + } + if test.trie.Count() != test.expectedCount { + t.Errorf("test 't' '%s': expected count to be %d, but was %d", test.name, test.expectedCount, test.trie.Count()) + } + } +} + +func TestInsertFindT(t *testing.T) { + + trie := NewTrie() + + insertTests := []struct { + name string + value string + expectedCount int + inserted bool + }{ + { + name: "insert into empty trie", + value: "test", + expectedCount: 1, + inserted: true, + }, + { + name: "insert into trie existing element", + value: "test", + expectedCount: 1, + inserted: false, + }, + { + name: "insert into trie with one element", + value: "toaster", + expectedCount: 2, + inserted: true, + }, + { + name: "insert into trie with two elements", + value: "toasting", + expectedCount: 3, + inserted: true, + }, + } + + for _, test := range insertTests { + inserted := trie.Insert(test.value) + if inserted != test.inserted { + t.Errorf("test 't' '%s': expected inserted to be %t", test.name, test.inserted) + } + if trie.Count() != test.expectedCount { + t.Errorf("test 't' '%s': expected count to be %d, but was %d", test.name, test.expectedCount, trie.Count()) + } + } + + findTests := []struct { + name string + value string + found bool + isEntry bool + isLeaf bool + }{ + { + name: "find existing element in 't' trie with three elements", + value: "test", + found: true, + isEntry: true, + isLeaf: true, + }, + { + name: "find existing second element in 't' trie with three elements", + value: "toaster", + found: true, + isEntry: true, + isLeaf: true, + }, + { + name: "find existing third element in 't' trie with three elements", + value: "toasting", + found: true, + isEntry: true, + isLeaf: true, + }, + } + + for _, test := range findTests { + found, n := trie.Find(test.value) + if found != test.found { + t.Errorf("test '%s': expected found to be %t", test.name, test.found) + } + if found { + if n.IsEntry() != test.isEntry { + t.Errorf("test '%s': expected isEntry to be %t", test.name, test.isEntry) + } + if n.IsLeaf() != test.isLeaf { + t.Errorf("test '%s': expected isLeaf to be %t", test.name, test.isLeaf) + t.Errorf("test '%s': expected isEntry to be %v", test.name, n) + } + } + } +} + func getTrie(nodes int, prefix byte) Trie { emptyTrie := NewTrie() @@ -137,314 +422,508 @@ func getTrie(nodes int, prefix byte) Trie { return Trie{child: []*Node{{ value: "r", children: []*Node{ - {value: "omane", childCount: 0}}, - childCount: 1}}, count: 1} + {value: "omane", childCount: 0, entry: true}}, + childCount: 1, entry: false}}, count: 1} } if nodes == 2 { return Trie{child: []*Node{{ value: "r", children: []*Node{{value: "oman", - children: []*Node{{value: "e", childCount: 0}, - {value: "us", childCount: 0}}, - childCount: 2}}, childCount: 1}}, count: 2} + children: []*Node{{value: "e", childCount: 0, entry: true}, + {value: "us", childCount: 0, entry: true}}, + childCount: 2, entry: false}}, childCount: 1, entry: false}}, count: 2} } if nodes == 3 { return Trie{child: []*Node{{ value: "r", children: []*Node{{value: "om", children: []*Node{{value: "an", - children: []*Node{{value: "e", childCount: 0}, - {value: "us", childCount: 0}}, - childCount: 2}, - {value: "ulus", childCount: 0}}, childCount: 2}}, childCount: 1}}, count: 3} + children: []*Node{{value: "e", childCount: 0, entry: true}, + {value: "us", childCount: 0, entry: true}}, + childCount: 2, entry: false}, + {value: "ulus", childCount: 0, entry: true}}, childCount: 2, entry: false}}, childCount: 1, entry: false}}, count: 3} } if nodes == 4 { return Trie{child: []*Node{{ value: "r", children: []*Node{{value: "om", children: []*Node{{value: "an", - children: []*Node{{value: "e", childCount: 0}, - {value: "us", childCount: 0}}, - childCount: 2}, - {value: "ulus", childCount: 0}}, childCount: 2}, - {value: "ubens", childCount: 0}}, childCount: 1}}, count: 4} + children: []*Node{{value: "e", childCount: 0, entry: true}, + {value: "us", childCount: 0, entry: true}}, + childCount: 2, entry: false}, + {value: "ulus", childCount: 0, entry: true}}, childCount: 2, entry: false}, + {value: "ubens", childCount: 0, entry: true}}, childCount: 1, entry: false}}, count: 4} } if nodes == 5 { return Trie{child: []*Node{{ value: "r", children: []*Node{{value: "om", children: []*Node{{value: "an", - children: []*Node{{value: "e", childCount: 0}, - {value: "us", childCount: 0}}, - childCount: 2}, - {value: "ulus", childCount: 0}}, childCount: 2}, + children: []*Node{{value: "e", childCount: 0, entry: true}, + {value: "us", childCount: 0, entry: true}}, + childCount: 2, entry: false}, + {value: "ulus", childCount: 0, entry: true}}, childCount: 2, entry: false}, {value: "ube", - children: []*Node{{value: "ns", childCount: 0}, - {value: "r", childCount: 0}}, - childCount: 2}}, childCount: 1}}, count: 5} + children: []*Node{{value: "ns", childCount: 0, entry: true}, + {value: "r", childCount: 0, entry: true}}, + childCount: 2, entry: false}}, childCount: 1, entry: false}}, count: 5} } if nodes == 6 { return Trie{child: []*Node{{ value: "r", children: []*Node{{value: "om", children: []*Node{{value: "an", - children: []*Node{{value: "e", childCount: 0}, - {value: "us", childCount: 0}}, - childCount: 2}, - {value: "ulus", childCount: 0}}, childCount: 2}, + children: []*Node{{value: "e", childCount: 0, entry: true}, + {value: "us", childCount: 0, entry: true}}, + childCount: 2, entry: false}, + {value: "ulus", childCount: 0, entry: true}}, childCount: 2, entry: false}, {value: "ub", children: []*Node{{value: "e", - children: []*Node{{value: "ns", childCount: 0}, - {value: "r", childCount: 0}}, childCount: 2}, - {value: "icon", childCount: 0}}, childCount: 2}}, - childCount: 1}}, count: 6} + children: []*Node{{value: "ns", childCount: 0, entry: true}, + {value: "r", childCount: 0, entry: true}}, childCount: 2, entry: false}, + {value: "icon", childCount: 0, entry: true}}, childCount: 2, entry: false}}, + childCount: 1, entry: false}}, count: 6} } if nodes == 7 { return Trie{child: []*Node{{ value: "r", children: []*Node{{value: "om", children: []*Node{{value: "an", - children: []*Node{{value: "e", childCount: 0}, - {value: "us", childCount: 0}}, - childCount: 2}, - {value: "ulus", childCount: 0}}, childCount: 2}, + children: []*Node{{value: "e", childCount: 0, entry: true}, + {value: "us", childCount: 0, entry: true}}, + childCount: 2, entry: false}, + {value: "ulus", childCount: 0, entry: true}}, childCount: 2, entry: false}, {value: "ub", children: []*Node{{value: "e", - children: []*Node{{value: "ns", childCount: 0}, - {value: "r", childCount: 0}}, childCount: 2}, + children: []*Node{{value: "ns", childCount: 0, entry: true}, + {value: "r", childCount: 0, entry: true}}, childCount: 2, entry: false}, {value: "ic", - children: []*Node{{value: "on", childCount: 0}, - {value: "undus", childCount: 0}}, - childCount: 2}}, - childCount: 2}}, childCount: 1}}, count: 7} + children: []*Node{{value: "on", childCount: 0, entry: true}, + {value: "undus", childCount: 0, entry: true}}, + childCount: 2, entry: false}}, + childCount: 2, entry: false}}, childCount: 1, entry: false}}, count: 7} + } + } + if prefix == 's' { + if nodes == 1 { + return Trie{child: []*Node{{ + value: "s", + children: []*Node{ + {value: "low", childCount: 0, entry: true}}, + childCount: 1, entry: false}}, count: 1} + } + if nodes == 2 { + return Trie{child: []*Node{{ + value: "s", + children: []*Node{{value: "low", + children: []*Node{{value: "er", childCount: 0, entry: true}}, + childCount: 2, entry: true}}, childCount: 1, entry: false}}, count: 2} + } + if nodes == 3 { + return Trie{child: []*Node{{ + value: "s", + children: []*Node{{value: "low", + children: []*Node{{value: "er", + childCount: 0, entry: true}, + {value: "ly", childCount: 0, entry: true}}, childCount: 2, entry: true}}, childCount: 1, entry: false}}, count: 3} + } + } + if prefix == 't' { + if nodes == 1 { + return Trie{child: []*Node{{ + value: "t", + children: []*Node{ + {value: "est", childCount: 0, entry: true}}, + childCount: 1, entry: false}}, count: 1} + } + if nodes == 2 { + return Trie{child: []*Node{{ + value: "t", + children: []*Node{{value: "est", + childCount: 0, entry: true}, {value: "oaster", + childCount: 0, entry: true}}, childCount: 1, entry: false}}, count: 2} + } + if nodes == 3 { + return Trie{child: []*Node{{ + value: "t", + children: []*Node{{value: "est", + childCount: 0, entry: true}, {value: "oast", + children: []*Node{{value: "er", childCount: 0, entry: true}, + {value: "ing", childCount: 0, entry: true}}, + childCount: 2, entry: false}}, + childCount: 2, entry: false}}, count: 3} } } return emptyTrie } var findTests = []struct { - name string - value string - trie Trie - found bool - isLeaf bool + name string + value string + trie Trie + found bool + isEntry bool + isLeaf bool }{ { - name: "find nonexistent element in trie", - value: "romanus", - trie: getTrie(1, 'r'), - found: false, - isLeaf: false, + name: "find nonexistent element in trie", + value: "romanus", + trie: getTrie(1, 'r'), + found: false, + isEntry: false, + isLeaf: false, + }, + { + name: "find trimmed nonexistent element in trie", + value: " \r\n", + trie: getTrie(1, 'r'), + found: false, + isEntry: false, + isLeaf: false, + }, + { + name: "find existing element in trie", + value: "romane", + trie: getTrie(1, 'r'), + found: true, + isEntry: true, + isLeaf: true, + }, + { + name: "find trimmed existing element in trie", + value: " romane\n", + trie: getTrie(1, 'r'), + found: true, + isEntry: true, + isLeaf: true, + }, + { + name: "find existing element in trie with two elements", + value: "romane", + trie: getTrie(2, 'r'), + found: true, + isEntry: true, + isLeaf: true, + }, + { + name: "find existing second element in trie with two elements", + value: "romanus", + trie: getTrie(2, 'r'), + found: true, + isEntry: true, + isLeaf: true, + }, + { + name: "find existing element in trie with three elements", + value: "romane", + trie: getTrie(3, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find trimmed nonexistent element in trie", - value: " \r\n", - trie: getTrie(1, 'r'), - found: false, - isLeaf: false, + name: "find existing second element in trie with three elements", + value: "romanus", + trie: getTrie(3, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing element in trie", - value: "romane", - trie: getTrie(1, 'r'), - found: true, - isLeaf: true, + name: "find existing third element in trie with three elements", + value: "romulus", + trie: getTrie(3, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find trimmed existing element in trie", - value: " romane\n", - trie: getTrie(1, 'r'), - found: true, - isLeaf: true, + name: "find existing element in trie with four elements", + value: "romane", + trie: getTrie(4, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing element in trie with two elements", - value: "romane", - trie: getTrie(2, 'r'), - found: true, - isLeaf: true, + name: "find existing second element in trie with four elements", + value: "romanus", + trie: getTrie(4, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing second element in trie with two elements", - value: "romanus", - trie: getTrie(2, 'r'), - found: true, - isLeaf: true, + name: "find existing third element in trie with four elements", + value: "romulus", + trie: getTrie(4, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing element in trie with three elements", - value: "romane", - trie: getTrie(3, 'r'), - found: true, - isLeaf: true, + name: "find existing fourth element in trie with four elements", + value: "rubens", + trie: getTrie(4, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing second element in trie with three elements", - value: "romanus", - trie: getTrie(3, 'r'), - found: true, - isLeaf: true, + name: "find existing element in trie with five elements", + value: "romane", + trie: getTrie(5, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing third element in trie with three elements", - value: "romulus", - trie: getTrie(3, 'r'), - found: true, - isLeaf: true, + name: "find existing second element in trie with five elements", + value: "romanus", + trie: getTrie(5, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing element in trie with four elements", - value: "romane", - trie: getTrie(4, 'r'), - found: true, - isLeaf: true, + name: "find existing third element in trie with five elements", + value: "romulus", + trie: getTrie(5, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing second element in trie with four elements", - value: "romanus", - trie: getTrie(4, 'r'), - found: true, - isLeaf: true, + name: "find existing fourth element in trie with five elements", + value: "rubens", + trie: getTrie(5, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing third element in trie with four elements", - value: "romulus", - trie: getTrie(4, 'r'), - found: true, - isLeaf: true, + name: "find existing fifth element in trie with five elements", + value: "ruber", + trie: getTrie(5, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing fourth element in trie with four elements", - value: "rubens", - trie: getTrie(4, 'r'), - found: true, - isLeaf: true, + name: "find existing element in trie with six elements", + value: "romane", + trie: getTrie(6, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing element in trie with five elements", - value: "romane", - trie: getTrie(5, 'r'), - found: true, - isLeaf: true, + name: "find existing second element in trie with six elements", + value: "romanus", + trie: getTrie(6, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing second element in trie with five elements", - value: "romanus", - trie: getTrie(5, 'r'), - found: true, - isLeaf: true, + name: "find existing third element in trie with six elements", + value: "romulus", + trie: getTrie(6, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing third element in trie with five elements", - value: "romulus", - trie: getTrie(5, 'r'), - found: true, - isLeaf: true, + name: "find existing fourth element in trie with six elements", + value: "rubens", + trie: getTrie(6, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing fourth element in trie with five elements", - value: "rubens", - trie: getTrie(5, 'r'), - found: true, - isLeaf: true, + name: "find existing fifth element in trie with six elements", + value: "ruber", + trie: getTrie(6, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing fifth element in trie with five elements", - value: "ruber", - trie: getTrie(5, 'r'), - found: true, - isLeaf: true, + name: "find existing sixth element in trie with six elements", + value: "rubicon", + trie: getTrie(6, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing element in trie with six elements", - value: "romane", - trie: getTrie(6, 'r'), - found: true, - isLeaf: true, + name: "find existing element in trie with seven elements", + value: "romane", + trie: getTrie(7, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing second element in trie with six elements", - value: "romanus", - trie: getTrie(6, 'r'), - found: true, - isLeaf: true, + name: "find existing second element in trie with seven elements", + value: "romanus", + trie: getTrie(7, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing third element in trie with six elements", - value: "romulus", - trie: getTrie(6, 'r'), - found: true, - isLeaf: true, + name: "find existing third element in trie with seven elements", + value: "romulus", + trie: getTrie(7, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing fourth element in trie with six elements", - value: "rubens", - trie: getTrie(6, 'r'), - found: true, - isLeaf: true, + name: "find existing fourth element in trie with seven elements", + value: "rubens", + trie: getTrie(7, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing fifth element in trie with six elements", - value: "ruber", - trie: getTrie(6, 'r'), - found: true, - isLeaf: true, + name: "find existing fifth element in trie with seven elements", + value: "ruber", + trie: getTrie(7, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing sixth element in trie with six elements", - value: "rubicon", - trie: getTrie(6, 'r'), - found: true, - isLeaf: true, + name: "find existing sixth element in trie with seven elements", + value: "rubicon", + trie: getTrie(7, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing element in trie with seven elements", - value: "romane", - trie: getTrie(7, 'r'), - found: true, - isLeaf: true, + name: "find existing seventh element in trie with seven elements", + value: "rubicundus", + trie: getTrie(7, 'r'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing second element in trie with seven elements", - value: "romanus", - trie: getTrie(7, 'r'), - found: true, - isLeaf: true, + name: "find existing element in 's' trie", + value: "slow", + trie: getTrie(1, 's'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing third element in trie with seven elements", - value: "romulus", - trie: getTrie(7, 'r'), - found: true, - isLeaf: true, + name: "find trimmed existing element in 's' trie", + value: " slow\n", + trie: getTrie(1, 's'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing fourth element in trie with seven elements", - value: "rubens", - trie: getTrie(7, 'r'), - found: true, - isLeaf: true, + name: "find existing element in 's' trie with two elements", + value: "slow", + trie: getTrie(2, 's'), + found: true, + isEntry: true, + isLeaf: false, }, { - name: "find existing fifth element in trie with seven elements", - value: "ruber", - trie: getTrie(7, 'r'), - found: true, - isLeaf: true, + name: "find existing second element in 's' trie with two elements", + value: "slower", + trie: getTrie(2, 's'), + found: true, + isEntry: true, + isLeaf: true, }, { - name: "find existing sixth element in trie with seven elements", - value: "rubicon", - trie: getTrie(7, 'r'), - found: true, - isLeaf: true, + name: "find existing element in 's' trie with three elements", + value: "slow", + trie: getTrie(3, 's'), + found: true, + isEntry: true, + isLeaf: false, }, { - name: "find existing seventh element in trie with seven elements", - value: "rubicundus", - trie: getTrie(7, 'r'), - found: true, - isLeaf: true, + name: "find existing second element in 's' trie with three elements", + value: "slower", + trie: getTrie(3, 's'), + found: true, + isEntry: true, + isLeaf: true, + }, + { + name: "find existing third element in 's' trie with three elements", + value: "slowly", + trie: getTrie(3, 's'), + found: true, + isEntry: true, + isLeaf: true, + }, + { + name: "find existing element in 't' trie", + value: "test", + trie: getTrie(1, 't'), + found: true, + isEntry: true, + isLeaf: true, + }, + { + name: "find trimmed existing element in 't' trie", + value: " test\n", + trie: getTrie(1, 't'), + found: true, + isEntry: true, + isLeaf: true, + }, + { + name: "find existing element in 't' trie with two elements", + value: "test", + trie: getTrie(2, 't'), + found: true, + isEntry: true, + isLeaf: true, + }, + { + name: "find existing second element in 't' trie with two elements", + value: "toaster", + trie: getTrie(2, 't'), + found: true, + isEntry: true, + isLeaf: true, + }, + { + name: "find existing element in 't' trie with three elements", + value: "test", + trie: getTrie(3, 't'), + found: true, + isEntry: true, + isLeaf: true, + }, + { + name: "find existing second element in 't' trie with three elements", + value: "toaster", + trie: getTrie(3, 't'), + found: true, + isEntry: true, + isLeaf: true, + }, + { + name: "find existing third element in 't' trie with three elements", + value: "toasting", + trie: getTrie(3, 't'), + found: true, + isEntry: true, + isLeaf: true, }, } @@ -456,6 +935,9 @@ func TestFind(t *testing.T) { t.Errorf("test '%s': expected found to be %t", test.name, test.found) } if found { + if n.IsEntry() != test.isEntry { + t.Errorf("test '%s': expected isEntry to be %t", test.name, test.isEntry) + } if n.IsLeaf() != test.isLeaf { t.Errorf("test '%s': expected isLeaf to be %t", test.name, test.isLeaf) } @@ -463,7 +945,7 @@ func TestFind(t *testing.T) { } } -func BenchmarkInsert(b *testing.B) { +func BenchmarkInsertR(b *testing.B) { trie := NewTrie() @@ -552,6 +1034,47 @@ func BenchmarkInsert(b *testing.B) { } } +func BenchmarkInsertS(b *testing.B) { + + trie := NewTrie() + + insertBenchmarks := []struct { + name string + value string + expectedCount int + inserted bool + }{ + { + name: "insert into empty trie", + value: "slow", + expectedCount: 1, + inserted: true, + }, + { + name: "insert into trie with one element", + value: "slower", + expectedCount: 2, + inserted: true, + }, + { + name: "insert into trie with two elements", + value: "slowly", + expectedCount: 3, + inserted: true, + }, + } + + for _, benchmark := range insertBenchmarks { + inserted := trie.Insert(benchmark.value) + if inserted != benchmark.inserted { + b.Errorf("benchmark 's' '%s': expected inserted to be %t", benchmark.name, benchmark.inserted) + } + if trie.Count() != benchmark.expectedCount { + b.Errorf("benchmark 's' '%s': expected count to be %d, but was %d", benchmark.name, benchmark.expectedCount, trie.Count()) + } + } +} + var benchmarkFound bool var benchmarkN *Node