From 4fa4a37dd9fe809ac7683ed684a20b190f216b3d Mon Sep 17 00:00:00 2001 From: drdr xp Date: Mon, 23 Sep 2019 19:09:26 +0800 Subject: [PATCH] api-change: trie: remove Trie --- docs/README.md.j2 | 4 - trie/errors.go | 14 - trie/example_trie_tostring_test.go | 36 -- trie/slimtrie_test.go | 5 - trie/trie.go | 335 ------------- trie/trie_str.go | 83 ---- trie/trie_test.go | 774 ----------------------------- 7 files changed, 1251 deletions(-) delete mode 100644 trie/example_trie_tostring_test.go delete mode 100644 trie/trie.go delete mode 100644 trie/trie_str.go delete mode 100644 trie/trie_test.go diff --git a/docs/README.md.j2 b/docs/README.md.j2 index 4814aca9..75fb6f57 100644 --- a/docs/README.md.j2 +++ b/docs/README.md.j2 @@ -91,12 +91,8 @@ memory, as a minimized index of huge amount external data. - - - - ## Memory overhead diff --git a/trie/errors.go b/trie/errors.go index e3e5a797..a66949a1 100644 --- a/trie/errors.go +++ b/trie/errors.go @@ -3,20 +3,6 @@ package trie import "errors" var ( - // ErrTooManyTrieNodes indicates the number of trie nodes(not number of - // keys) exceeded. - ErrTooManyTrieNodes = errors.New("exceeds max node count=2^31-1") - - // ErrTrieBranchValueOverflow indicate input key consists of a word greater - // than the max 4-bit word(0x0f). - ErrTrieBranchValueOverflow = errors.New("branch value must <=0x0f") - - // ErrDuplicateKeys indicates two keys are identical. - ErrDuplicateKeys = errors.New("keys can not be duplicate") - - // ErrKVLenNotMatch means the keys and values to create Trie has different - // number of elements. - ErrKVLenNotMatch = errors.New("length of keys and values not equal") // ErrKeyOutOfOrder means keys to create Trie are not ascendingly ordered. ErrKeyOutOfOrder = errors.New("keys not ascending sorted") diff --git a/trie/example_trie_tostring_test.go b/trie/example_trie_tostring_test.go deleted file mode 100644 index 849dd9c8..00000000 --- a/trie/example_trie_tostring_test.go +++ /dev/null @@ -1,36 +0,0 @@ -package trie - -import "fmt" - -func ExampleNode_String() { - keys := [][]byte{ - {1, 2, 3}, - {1, 2, 3, 4}, - {2, 3}, - {2, 3, 4}, - {2, 3, 4, 5}, - } - values := []int{0, 1, 2, 3, 4} - trie, err := NewTrie(keys, values, false) - if err != nil { - panic(err) - } - - fmt.Println(trie) - - // Output: - // *2 - // -1-> - // -2-> - // -3->*2 - // -$->=0 - // -4-> - // -$->=1 - // -2-> - // -3->*2 - // -$->=2 - // -4->*2 - // -$->=3 - // -5-> - // -$->=4 -} diff --git a/trie/slimtrie_test.go b/trie/slimtrie_test.go index 5567ef18..736c890b 100644 --- a/trie/slimtrie_test.go +++ b/trie/slimtrie_test.go @@ -9,16 +9,11 @@ import ( "github.com/golang/protobuf/proto" "github.com/kr/pretty" "github.com/openacid/errors" - "github.com/openacid/low/bitword" "github.com/openacid/slim/encode" "github.com/openacid/testkeys" "github.com/stretchr/testify/require" ) -var ( - bw4 = bitword.BitWord[4] -) - type searchRst struct { lVal interface{} eqVal interface{} diff --git a/trie/trie.go b/trie/trie.go deleted file mode 100644 index 63397181..00000000 --- a/trie/trie.go +++ /dev/null @@ -1,335 +0,0 @@ -package trie - -import ( - "github.com/openacid/errors" - "github.com/openacid/low/tree" - "github.com/openacid/low/typehelper" -) - -// Node is a Trie node. -type Node struct { - Children map[int]*Node - Branches []int - Step uint16 - Value interface{} - - squash bool - - // TODO inner node count. fix it - NodeCnt int -} - -const leafBranch = -1 - -// NewTrie creates a trie from a serial of ascendingly ordered keys and corresponding values. -// -// `values` must be a slice, or it panic. -// if `squash` is `true`, indicate this trie to squash preceding branches every time after Append a new -// key. -func NewTrie(keys [][]byte, values interface{}, squash bool) (root *Node, err error) { - - root = &Node{Children: make(map[int]*Node), Step: 1, squash: squash, NodeCnt: 1} - - if keys == nil { - return - } - - valSlice := typehelper.ToSlice(values) - - if len(keys) != len(valSlice) { - err = ErrKVLenNotMatch - return - } - - for i := 0; i < len(keys); i++ { - key := keys[i] - _, err = root.Append(key, valSlice[i]) - if err != nil { - err = errors.Wrapf(err, "trie failed to add kvs") - return - } - } - - if squash { - root.Squash() - } - - return -} - -// String outputs multiline trie structure. -func (r *Node) String() string { - s := &trieStringly{tnode: r} - return tree.String(s) -} - -// Squash compresses a Trie by removing single-branch nodes. -func (r *Node) Squash() int { - - var cnt int - - for _, n := range r.Children { - cnt += n.Squash() - } - - if len(r.Branches) == 1 && r.Branches[0] != leafBranch { - child := r.Children[r.Branches[0]] - r.Branches = child.Branches - r.Children = child.Children - r.Step = child.Step + 1 - cnt++ - } - - return cnt -} - -// removeSameLeaf removes leaf that has the same value as preceding leaf. -// -// a ------->e =1 -// `>b------>f =2 -// `>c->d->g =2 // "g" and "d" is removed, c has other child and is kept. -// `--->h =3 -// -// Since 0.5.5 -func (r *Node) removeSameLeaf() { - - var prevValue interface{} = nil - - // wrapped as a generalized tree - s := &trieStringly{tnode: r} - - tree.DepthFirst(s, - func(t tree.Tree, parent, branch, node interface{}) { - - n := node.(*Node) - needRemove := false - - v, isLeaf := t.LeafVal(node) - if isLeaf { - if v == prevValue { - // same value no need to store - needRemove = true - } else { - prevValue = v - } - } else { - if len(n.Branches) == 0 { - needRemove = true - } - } - - if needRemove && parent != nil && branch != nil { - p := parent.(*Node) - b := branch.(int) - - delete(p.Children, b) - - for i, bb := range p.Branches { - if bb == b { - p.Branches = append(p.Branches[:i], p.Branches[i+1:]...) - } - } - if !isLeaf { - r.NodeCnt-- - } - - } - }) -} - -// Search for `key` in a Trie. -// -// It returns 3 values of: -// The left sibling value. -// The matching value. -// The right sibling value. -// -// Any of them could be nil. -func (r *Node) Search(key []byte) (ltValue, eqValue, gtValue interface{}) { - - var eqNode = r - var ltNode *Node - var gtNode *Node - - lenKey := len(key) - - for i := -1; ; { - i += int(eqNode.Step) - - if lenKey < i { - gtNode = eqNode - eqNode = nil - break - } - - var br int - if lenKey == i { - br = leafBranch - } else { - br = int(key[i]) - } - - li, ri := neighborBranches(eqNode.Branches, br) - if li >= 0 { - ltNode = eqNode.Children[eqNode.Branches[li]] - } - if ri >= 0 { - gtNode = eqNode.Children[eqNode.Branches[ri]] - } - - eqNode = eqNode.Children[br] - - if eqNode == nil { - break - } - - if br == leafBranch { - break - } - } - - if ltNode != nil { - ltValue = ltNode.rightMost().Value - } - if gtNode != nil { - gtValue = gtNode.leftMost().Value - } - if eqNode != nil { - eqValue = eqNode.Value - } - - return -} - -func neighborBranches(branches []int, br int) (ltIndex, rtIndex int) { - - if len(branches) == 0 { - return - } - - var i int - var b int - - for i, b = range branches { - if b >= br { - break - } - } - - if b == br { - rtIndex = i + 1 - ltIndex = i - 1 - - if rtIndex == len(branches) { - rtIndex = -1 - } - return - } - - if b > br { - rtIndex = i - ltIndex = i - 1 - return - } - - rtIndex = -1 - ltIndex = i - - return -} - -func (r *Node) leftMost() *Node { - - node := r - for { - if len(node.Branches) == 0 { - return node - } - - firstBr := node.Branches[0] - node = node.Children[firstBr] - } -} - -func (r *Node) rightMost() *Node { - - node := r - for { - if len(node.Branches) == 0 { - return node - } - - lastBr := node.Branches[len(node.Branches)-1] - node = node.Children[lastBr] - } -} - -// Append adds a key-value pair into Trie. -// -// The key to add must be greater than any existent key in the Trie. -// -// It returns the leaf node representing the added key. -func (r *Node) Append(key []byte, value interface{}) (leaf *Node, err error) { - - var node = r - var j int - - for j = 0; j < len(key); j++ { - br := int(key[j]) - if node.Children[br] == nil { - l := len(node.Branches) - if l > 0 && node.Branches[l-1] > br { - err = errors.Wrapf(ErrKeyOutOfOrder, "append %q", key) - return - } - break - } - node = node.Children[br] - } - - if j == len(key) { - leaf = node.Children[leafBranch] - if leaf != nil { - err = ErrDuplicateKeys - return - } - - if len(node.Branches) != 0 { - // means this key is a prefix of an existed key, so key's adding order is not ascending. - err = errors.Wrapf(ErrKeyOutOfOrder, "append %q is a prefix", key) - return - } - } - - commonNode := node - - var ltNode *Node - numBr := len(commonNode.Branches) - if numBr > 0 { - ltNode = commonNode.Children[commonNode.Branches[numBr-1]] - } - - for _, b := range key[j:] { - br := int(b) - n := &Node{Children: make(map[int]*Node), Step: 1, squash: node.squash} - - node.Children[br] = n - node.Branches = append(node.Branches, br) - node = n - - r.NodeCnt++ - } - - leaf = &Node{Value: value} - - node.Children[leafBranch] = leaf - node.Branches = append(node.Branches, leafBranch) - - if commonNode.squash { - if ltNode != nil { - r.NodeCnt -= ltNode.Squash() - } - } - - return -} diff --git a/trie/trie_str.go b/trie/trie_str.go deleted file mode 100644 index 807ec14c..00000000 --- a/trie/trie_str.go +++ /dev/null @@ -1,83 +0,0 @@ -package trie - -import "fmt" - -// trieStringly is a wrapper that implements tree.Tree . -// It is a helper to convert trie to string. -// -// Since 0.5.1 -type trieStringly struct { - tnode *Node -} - -// Child implements tree.Tree -// -// Since 0.5.1 -func (s *trieStringly) Child(node, branch interface{}) interface{} { - - if node == nil && branch == nil { - // root - return s.tnode - } - - n := s.trieNode(node) - b := branch.(int) - return n.Children[b] -} - -// Labels implements tree.Tree -// -// Since 0.5.1 -func (s *trieStringly) Labels(node interface{}) []interface{} { - n := s.trieNode(node) - rst := []interface{}{} - for _, b := range n.Branches { - rst = append(rst, b) - } - return rst -} - -// NodeID implements tree.Tree -// -// Since 0.5.1 -func (s *trieStringly) NodeID(node interface{}) string { - return "" -} - -// LabelInfo implements tree.Tree -// -// Since 0.5.5 -func (s *trieStringly) LabelInfo(label interface{}) string { - l := label.(int) - if l == -1 { - return "$" - } - return fmt.Sprintf("%d", l) -} - -// NodeInfo implements tree.Tree -// -// Since 0.5.1 -func (s *trieStringly) NodeInfo(node interface{}) string { - n := s.trieNode(node) - if n.Step <= 1 { - return "" - } - return fmt.Sprintf("+%d", n.Step) -} - -// LeafVal implements tree.Tree -// -// Since 0.5.1 -func (s *trieStringly) LeafVal(node interface{}) (interface{}, bool) { - n := s.trieNode(node) - return n.Value, n.Value != nil -} - -// trieNode convert a interface to SlimTrie node id. -func (s *trieStringly) trieNode(node interface{}) *Node { - if node == nil { - return s.tnode - } - return node.(*Node) -} diff --git a/trie/trie_test.go b/trie/trie_test.go deleted file mode 100644 index 8f4f6427..00000000 --- a/trie/trie_test.go +++ /dev/null @@ -1,774 +0,0 @@ -package trie - -import ( - "fmt" - "reflect" - "testing" - - "github.com/openacid/errors" - "github.com/openacid/slim/benchhelper" - "github.com/stretchr/testify/require" -) - -func TestTrie(t *testing.T) { - - type ExpectType struct { - key []byte - rst int - } - - var cases = []struct { - key [][]byte - value interface{} - expected []ExpectType - }{ - { - key: [][]byte{ - {'a', 'b', 'c'}, - {'a', 'b', 'd'}, - {'b', 'c', 'd'}, - {'b', 'c', 'e'}, - {'c', 'd', 'e'}, - }, - value: []int{ - 0, - 1, - 2, - 3, - 4, - }, - expected: []ExpectType{ - {[]byte{'a', 'b', 'c'}, 0}, - {[]byte{'a', 'b', 'd'}, 1}, - {[]byte{'b', 'c', 'd'}, 2}, - {[]byte{'b', 'c', 'e'}, 3}, - {[]byte{'c', 'd', 'e'}, 4}, - }, - }, - { - key: [][]byte{ - {'a', 'b', 'c'}, - {'a', 'b', 'c', 'd'}, - {'b', 'c'}, - {'b', 'c', 'd'}, - {'b', 'c', 'd', 'e'}, - }, - value: []int{ - 0, - 1, - 2, - 3, - 4, - }, - expected: []ExpectType{ - {[]byte{'a', 'b', 'c'}, 0}, - {[]byte{'a', 'b', 'c', 'd'}, 1}, - {[]byte{'b', 'c'}, 2}, - {[]byte{'b', 'c', 'd'}, 3}, - {[]byte{'b', 'c', 'd', 'e'}, 4}, - }, - }, - } - - for _, c := range cases { - - trie, _ := NewTrie(c.key, c.value, false) - for _, ex := range c.expected { - _, rst, _ := trie.Search(ex.key) - - if !reflect.DeepEqual(ex.rst, rst) { - t.Fatal("ks: ", ex.key, "expected value: ", ex.rst, "rst: ", rst) - } - } - - trie.Squash() - for _, ex := range c.expected { - _, rst, _ := trie.Search(ex.key) - - if !reflect.DeepEqual(ex.rst, rst) { - t.Error("ks: ", ex.key, "expected value: ", ex.rst, "rst: ", rst) - } - } - - trie.Squash() - trie.Squash() - for _, ex := range c.expected { - _, rst, _ := trie.Search(ex.key) - - if !reflect.DeepEqual(ex.rst, rst) { - t.Error("ks: ", ex.key, "expected value: ", ex.rst, "rst: ", rst) - } - } - - trie, _ = NewTrie(c.key, c.value, true) - for _, ex := range c.expected { - _, rst, _ := trie.Search(ex.key) - - if !reflect.DeepEqual(ex.rst, rst) { - t.Error("ks: ", ex.key, "expected value: ", ex.rst, "rst: ", rst) - } - } - } -} - -func TestTrieSearch(t *testing.T) { - - var key = [][]byte{ - {'a', 'b', 'c'}, - {'a', 'b', 'c', 'd'}, - {'a', 'b', 'd'}, - {'a', 'b', 'd', 'e'}, - {'b', 'c'}, - {'b', 'c', 'd'}, - {'b', 'c', 'd', 'e'}, - {'c', 'd', 'e'}, - } - var value = []int{ - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - } - - var trie, _ = NewTrie(key, value, false) - - type ExpectType struct { - ltVal interface{} - eqVal interface{} - gtVal interface{} - } - - var cases = []struct { - key []byte - expected ExpectType - }{ - { - []byte{'a', 'b', 'c'}, - ExpectType{nil, 0, 1}, - }, - { - []byte{'a', 'b', 'd'}, - ExpectType{1, 2, 3}, - }, - { - []byte{'b', 'c', 'd'}, - ExpectType{4, 5, 6}, - }, - { - []byte{'b', 'c', 'e'}, - ExpectType{6, nil, 7}, - }, - { - []byte{'c', 'd', 'e'}, - ExpectType{6, 7, nil}, - }, - { - []byte{'a', 'c', 'b'}, - ExpectType{3, nil, 4}, - }, - { - []byte{90, 'a', 'v'}, - ExpectType{nil, nil, 0}, - }, - { - []byte{'a', 'b'}, - ExpectType{nil, nil, 0}, - }, - { - []byte{'a', 'c'}, - ExpectType{3, nil, 4}, - }, - { - []byte{90}, - ExpectType{nil, nil, 0}, - }, - { - []byte{'a', 'b', 'c', 'd', 'e'}, - ExpectType{1, nil, 2}, - }, - } - - for _, c := range cases { - - ltVal, eqVal, gtVal := trie.Search(c.key) - - if !reflect.DeepEqual(c.expected.ltVal, ltVal) { - t.Error("key: ", c.key, "expected lt value: ", c.expected.ltVal, "rst: ", ltVal) - } - if !reflect.DeepEqual(c.expected.eqVal, eqVal) { - t.Error("key: ", c.key, "expected eq value: ", c.expected.eqVal, "rst: ", eqVal) - } - if !reflect.DeepEqual(c.expected.gtVal, gtVal) { - t.Error("key: ", c.key, "expected gt value: ", c.expected.gtVal, "rst: ", gtVal) - } - } - - var squashedCases = []struct { - key []byte - expected ExpectType - }{ - { - []byte{'a', 'b'}, - ExpectType{nil, nil, 0}, - }, - { - []byte{'a', 'b', 'c'}, - ExpectType{nil, 0, 1}, - }, - { - []byte{'a', 'd', 'c'}, - ExpectType{nil, 0, 1}, - }, - { - []byte{'a', 'b', 'd'}, - ExpectType{1, 2, 3}, - }, - { - []byte{'a', 'c', 'd'}, - ExpectType{1, 2, 3}, - }, - { - []byte{'c', 'd', 'e'}, - ExpectType{6, 7, nil}, - }, - { - []byte{'c', 'f', 'e'}, - ExpectType{6, 7, nil}, - }, - { - []byte{'c', 'f', 'f'}, - ExpectType{6, 7, nil}, - }, - { - []byte{'c'}, - ExpectType{6, nil, 7}, - }, - { - []byte{'a', 'c'}, - ExpectType{nil, nil, 0}, - }, - { - []byte{'a', 'b', 'c', 'd', 'e'}, - ExpectType{1, nil, 2}, - }, - } - - trie.Squash() - for _, c := range squashedCases { - - ltVal, eqVal, gtVal := trie.Search(c.key) - - if !reflect.DeepEqual(c.expected.ltVal, ltVal) { - t.Error("key: ", c.key, "expected lt value: ", c.expected.ltVal, "rst: ", ltVal) - } - if !reflect.DeepEqual(c.expected.eqVal, eqVal) { - t.Error("key: ", c.key, "expected eq value: ", c.expected.eqVal, "rst: ", eqVal) - } - if !reflect.DeepEqual(c.expected.gtVal, gtVal) { - t.Error("key: ", c.key, "expected gt value: ", c.expected.gtVal, "rst: ", gtVal) - } - } -} - -func TestTrieNew(t *testing.T) { - - var cases = []struct { - keys [][]byte - values interface{} - expectedErr error - }{ - { - keys: [][]byte{ - {'a', 'b', 'c'}, - {'b', 'c', 'd'}, - {'c', 'd', 'e'}, - }, - values: []int{0, 1, 2}, - expectedErr: nil, - }, - { - keys: [][]byte{ - {'a', 'b', 'c'}, - {'a', 'b', 'c'}, - {'c', 'd', 'e'}, - }, - values: []int{0, 1, 2}, - expectedErr: errors.Wrapf(ErrDuplicateKeys, "key: abc"), - }, - } - benchhelper.WantPanic(t, func() { - NewTrie([][]byte{{'a', 'b', 'c'}}, map[string]int{"abc": 0}, false) - }, "values is map") - - for _, c := range cases { - _, err := NewTrie(c.keys, c.values, false) - if errors.Cause(err) != errors.Cause(c.expectedErr) { - t.Fatalf("new trie: expectedErr: %v, got: %v", c.expectedErr, err) - } - } -} - -func TestNewError(t *testing.T) { - - benchhelper.WantPanic(t, func() { NewTrie([][]byte{}, nil, false) }, "values is nil") - benchhelper.WantPanic(t, func() { NewTrie([][]byte{{1}}, 1, false) }, "values is int") - - cases := []struct { - keys [][]byte - vals interface{} - wanterr error - }{ - {nil, nil, nil}, - {nil, []int{}, nil}, - {nil, 1, nil}, - {[][]byte{}, []int{1}, ErrKVLenNotMatch}, - {[][]byte{{1}}, []int{}, ErrKVLenNotMatch}, - {[][]byte{{1, 2}, {1}}, []int{1, 2}, ErrKeyOutOfOrder}, - {[][]byte{{1, 2}, {1, 1}}, []int{1, 2}, ErrKeyOutOfOrder}, - {[][]byte{{1, 2}, {1, 2}}, []int{1, 2}, ErrDuplicateKeys}, - } - - for i, c := range cases { - _, err := NewTrie(c.keys, c.vals, false) - if c.wanterr != errors.Cause(err) { - t.Fatalf("%d-th: input: keys:%v; vals: %v; wanterr: %v; actual: %v", - i+1, c.keys, c.vals, c.wanterr, err) - } - } -} - -func TestAppend(t *testing.T) { - - tr, err := NewTrie([][]byte{{2, 3}, {2, 5}}, []int{1, 2}, false) - if err != nil { - t.Fatalf("expect no error but: %v", err) - } - - cases := []struct { - keys []byte - wanterr error - }{ - {[]byte{1}, ErrKeyOutOfOrder}, - {[]byte{1, 2}, ErrKeyOutOfOrder}, - {[]byte{2}, ErrKeyOutOfOrder}, - {[]byte{2, 2}, ErrKeyOutOfOrder}, - {[]byte{2, 3}, ErrDuplicateKeys}, - {[]byte{2, 4}, ErrKeyOutOfOrder}, - {[]byte{2, 5}, ErrDuplicateKeys}, - {[]byte{2, 6}, nil}, - } - - for i, c := range cases { - _, err := tr.Append(c.keys, 1) - if c.wanterr != errors.Cause(err) { - t.Fatalf("%d-th: input: %v; want: %v; actual: %v", - i+1, c.keys, c.wanterr, err) - } - } -} - -func TestAppendOneKeySquash(t *testing.T) { - - keys := [][]byte{ - {1, 2, 3}, - } - values := []int{ - 3, - } - - type ExpectType struct { - key []byte - value interface{} - } - - rt, err := NewTrie(keys, values, true) - if err != nil { - t.Error("NewTrie failed: ", err) - } - - cases := []ExpectType{ - {[]byte{1, 2, 2}, 3}, - {[]byte{1, 2, 3}, 3}, - {[]byte{1, 3, 3}, 3}, - {[]byte{3, 3, 3}, 3}, - {[]byte{4, 4, 4}, 3}, - {[]byte{1, 2}, nil}, - {[]byte{1, 2, 3, 4}, nil}, - } - - for _, c := range cases { - _, rst, _ := rt.Search(c.key) - if rst != c.value { - t.Error("ks: ", c.key, "expected value: ", c.value, "rst: ", rst) - } - } - -} - -func TestNewTrieSquash(t *testing.T) { - keys := [][]byte{ - {1, 2, 3, 4, 0}, - {1, 2, 3, 4, 1}, - {1, 2, 3, 4, 2}, - {1, 2, 3, 4, 3}, - {1, 3, 3, 5, 4}, - } - - values := []int{ - 0, - 1, - 2, - 3, - 4, - } - - type ExpectType struct { - key []byte - value interface{} - } - - // NewTrie with squash==true - rt, err := NewTrie(keys, values, true) - if err != nil { - t.Error("NewTrie failed: ", err) - } - - cases := []ExpectType{ - {[]byte{1, 2, 3, 4, 0}, 0}, - {[]byte{1, 2, 3, 5, 0}, 0}, - {[]byte{1, 2, 3, 4, 1}, 1}, - {[]byte{1, 2, 3, 5, 1}, 1}, - {[]byte{1, 2, 3, 4, 2}, 2}, - {[]byte{1, 2, 3, 5, 2}, 2}, - {[]byte{1, 2, 3, 4, 3}, 3}, - {[]byte{1, 2, 3, 5, 3}, 3}, - {[]byte{1, 3, 3, 5, 4}, 4}, - } - - for _, c := range cases { - _, rst, _ := rt.Search(c.key) - if rst != c.value { - t.Error("ks: ", c.key, "expected value: ", c.value, "rst: ", rst) - } - } - - // NewTrie with squash==false - rt, err = NewTrie(keys, values, false) - if err != nil { - t.Error("NewTri failed: ", err) - } - - cases = []ExpectType{ - {[]byte{1, 2, 3, 4, 0}, 0}, - {[]byte{1, 2, 3, 5, 0}, nil}, - {[]byte{1, 2, 3, 4, 1}, 1}, - {[]byte{1, 2, 3, 5, 1}, nil}, - {[]byte{1, 2, 3, 4, 2}, 2}, - {[]byte{1, 2, 3, 5, 2}, nil}, - {[]byte{1, 2, 3, 4, 3}, 3}, - {[]byte{1, 2, 3, 5, 3}, nil}, - {[]byte{1, 3, 3, 5, 4}, 4}, - } - - for _, c := range cases { - _, rst, _ := rt.Search(c.key) - if rst != c.value { - t.Error("ks: ", c.key, "expected value: ", c.value, "rst: ", rst) - } - } -} - -func TestToStrings(t *testing.T) { - var keys = [][]byte{ - {'a', 'b', 'c'}, - {'a', 'b', 'c', 'd'}, - {'a', 'b', 'd'}, - {'a', 'b', 'd', 'e'}, - {'b', 'c'}, - {'b', 'c', 'd'}, - {'b', 'c', 'd', 'e'}, - {'c', 'd', 'e'}, - } - var values = []int{0, 1, 2, 3, 4, 5, 6, 7} - - expect := ` -*3 --97-> - -98->*2 - -99->*2 - -$->=0 - -100-> - -$->=1 - -100->*2 - -$->=2 - -101-> - -$->=3 --98-> - -99->*2 - -$->=4 - -100->*2 - -$->=5 - -101-> - -$->=6 --99-> - -100-> - -101-> - -$->=7`[1:] - - var trie, _ = NewTrie(keys, values, false) - trie, err := NewTrie(keys, values, false) - if err != nil { - t.Fatalf("expect no err: %s", err) - } - - if expect != trie.String() { - t.Fatalf("expect: \n%v\n; but: \n%v\n", expect, trie.String()) - } -} - -func TestTrie_removeSameLeaf(t *testing.T) { - - ta := require.New(t) - - var keys = [][]byte{ - {'a', 'b', 'c'}, - {'a', 'b', 'c', 'd'}, - {'a', 'b', 'd'}, - {'a', 'b', 'd', 'e'}, - {'b', 'c'}, - {'b', 'c', 'd'}, - {'b', 'c', 'd', 'e'}, - {'c', 'd', 'e'}, - } - var values = []int{0, 0, 0, 3, 4, 5, 5, 5} - - want := ` -*2 --97-> - -98->*2 - -99-> - -$->=0 - -100-> - -101-> - -$->=3 --98-> - -99->*2 - -$->=4 - -100-> - -$->=5`[1:] - - trie, err := NewTrie(keys, values, false) - ta.Nil(err) - - trie.removeSameLeaf() - fmt.Println(trie) - - ta.Equal(want, trie.String()) - ta.Equal(9, trie.NodeCnt, "non-leaf node count") -} - -func TestTrie_UnsquashedSearch(t *testing.T) { - - cases := []slimCase{ - { - keys: []string{ - from8bit(1, 2, 3), - from8bit(1, 2, 4), - from8bit(2, 3, 4), - from8bit(2, 3, 5), - from8bit(3, 4, 5), - }, - values: []int{0, 1, 2, 3, 4}, - searches: []searchCase{ - {from8bit(1, 2, 3), searchRst{nil, 0, 1}}, - {from8bit(1, 2, 4), searchRst{0, 1, 2}}, - {from8bit(2, 3, 4), searchRst{1, 2, 3}}, - {from8bit(2, 3, 5), searchRst{2, 3, 4}}, - {from8bit(3, 4, 5), searchRst{3, 4, nil}}, - }, - }, - { - keys: []string{ - from8bit(1, 2, 3), - from8bit(1, 2, 3, 4), - from8bit(2, 3), - from8bit(2, 3, 0), - from8bit(2, 3, 4), - from8bit(2, 3, 4, 5), - from8bit(2, 3, 15), - }, - values: []int{0, 1, 2, 3, 4, 5, 6}, - searches: []searchCase{ - {from8bit(1, 2, 3), searchRst{nil, 0, 1}}, - {from8bit(1, 2, 3, 4), searchRst{0, 1, 2}}, - {from8bit(2, 3), searchRst{1, 2, 3}}, - {from8bit(2, 3, 0), searchRst{2, 3, 4}}, - {from8bit(2, 3, 4), searchRst{3, 4, 5}}, - {from8bit(2, 3, 4, 5), searchRst{4, 5, 6}}, - {from8bit(2, 3, 15), searchRst{5, 6, nil}}, - }, - }, - { - keys: []string{ - "abc", - "abcd", - "abd", - "abde", - "bc", - "bcd", - "bcde", - "cde", - }, - values: []int{0, 1, 2, 3, 4, 5, 6, 7}, - searches: []searchCase{ - {"ab", searchRst{nil, nil, 0}}, - {"abc", searchRst{nil, 0, 1}}, - {"abcde", searchRst{1, nil, 2}}, - {"abd", searchRst{1, 2, 3}}, - {"ac", searchRst{3, nil, 4}}, - {"acb", searchRst{3, nil, 4}}, - {"acd", searchRst{3, nil, 4}}, - {"adc", searchRst{3, nil, 4}}, - {"bcd", searchRst{4, 5, 6}}, - {"bce", searchRst{6, nil, 7}}, - {"c", searchRst{6, nil, 7}}, - {"cde", searchRst{6, 7, nil}}, - {"cfe", searchRst{7, nil, nil}}, - {"cff", searchRst{7, nil, nil}}, - }, - }, - } - - for _, c := range cases { - - keys := bw4.FromStrs(c.keys) - - // Unsquashed Trie - - trie, err := NewTrie(keys, c.values, false) - if err != nil { - t.Fatalf("expected no error but: %+v", err) - } - - for _, ex := range c.searches { - lt, eq, gt := trie.Search(bw4.FromStr(ex.key)) - rst := searchRst{lt, eq, gt} - - if !reflect.DeepEqual(ex.want, rst) { - fmt.Println(trie) - fmt.Println("search:", bw4.FromStr(ex.key)) - t.Fatal("key: ", bw4.FromStr(ex.key), "expected value: ", ex.want, "rst: ", rst) - } - } - } -} - -func TestTrie_SquashedTrieSearch(t *testing.T) { - - cases := []slimCase{ - { - keys: []string{ - from8bit(1, 2, 3), - from8bit(1, 2, 4), - from8bit(2, 3, 4), - from8bit(2, 3, 5), - from8bit(3, 4, 5), - }, - values: []int{0, 1, 2, 3, 4}, - searches: []searchCase{ - {from8bit(1, 2, 3), searchRst{nil, 0, 1}}, - {from8bit(1, 2, 4), searchRst{0, 1, 2}}, - {from8bit(2, 3, 4), searchRst{1, 2, 3}}, - {from8bit(2, 3, 5), searchRst{2, 3, 4}}, - {from8bit(3, 4, 5), searchRst{3, 4, nil}}, - }, - }, - { - keys: []string{ - from8bit(1, 2, 3), - from8bit(1, 2, 3, 4), - from8bit(2, 3), - from8bit(2, 3, 0), - from8bit(2, 3, 4), - from8bit(2, 3, 4, 5), - from8bit(2, 3, 15), - }, - values: []int{0, 1, 2, 3, 4, 5, 6}, - searches: []searchCase{ - {from8bit(1, 2, 3), searchRst{nil, 0, 1}}, - {from8bit(1, 2, 3, 4), searchRst{0, 1, 2}}, - {from8bit(2, 3), searchRst{1, 2, 3}}, - {from8bit(2, 3, 0), searchRst{2, 3, 4}}, - {from8bit(2, 3, 4), searchRst{3, 4, 5}}, - {from8bit(2, 3, 4, 5), searchRst{4, 5, 6}}, - {from8bit(2, 3, 15), searchRst{5, 6, nil}}, - }, - }, - { - keys: []string{ - "abc", - "abcd", - "abd", - "abde", - "bc", - "bcd", - "bcde", - "cde", - }, - values: []int{0, 1, 2, 3, 4, 5, 6, 7}, - searches: []searchCase{ - {"ab", searchRst{nil, nil, 0}}, - {"abc", searchRst{nil, 0, 1}}, - {"abcde", searchRst{1, nil, 2}}, - {"abd", searchRst{1, 2, 3}}, - {"ac", searchRst{nil, nil, 0}}, - {"acb", searchRst{nil, nil, 0}}, - {"acd", searchRst{1, 2, 3}}, - {"adc", searchRst{nil, 0, 1}}, - {"bcd", searchRst{4, 5, 6}}, - {"bce", searchRst{4, 5, 6}}, - {"c", searchRst{6, nil, 7}}, - {"cde", searchRst{6, 7, nil}}, - {"cfe", searchRst{6, 7, nil}}, - {"cff", searchRst{6, 7, nil}}, - }, - }, - } - - for _, c := range cases { - - keys := bw4.FromStrs(c.keys) - - // Squashed Trie - - trie, err := NewTrie(keys, c.values, true) - if err != nil { - t.Fatalf("expected no error but: %+v", err) - } - - for _, ex := range c.searches { - lt, eq, gt := trie.Search(bw4.FromStr(ex.key)) - rst := searchRst{lt, eq, gt} - - if !reflect.DeepEqual(ex.want, rst) { - t.Fatal("key: ", ex.key, "expected value: ", ex.want, "rst: ", rst) - } - } - - // Squashed twice Trie - - trie.Squash() - for _, ex := range c.searches { - lt, eq, gt := trie.Search(bw4.FromStr(ex.key)) - rst := searchRst{lt, eq, gt} - - if !reflect.DeepEqual(ex.want, rst) { - t.Fatal("key: ", ex.key, "expected value: ", ex.want, "rst: ", rst) - } - } - } -}