mirror of
https://github.com/fluencelabs/go-libp2p-kad-dht
synced 2025-04-24 14:22:13 +00:00
Add immutable add and test.
This commit is contained in:
parent
3dde188b9a
commit
58bd8767f8
@ -1,49 +0,0 @@
|
||||
package xortrie
|
||||
|
||||
// Union computes the union of the keys in p and q.
|
||||
// p and q must be non-nil. The returned trie is never nil.
|
||||
func Union(p, q *XorTrie) *XorTrie {
|
||||
return union(0, p, q)
|
||||
}
|
||||
|
||||
func union(depth int, p, q *XorTrie) *XorTrie {
|
||||
switch {
|
||||
case p.isLeaf() && q.isLeaf():
|
||||
switch {
|
||||
case p.isEmpty() && q.isEmpty():
|
||||
return &XorTrie{}
|
||||
case p.isEmpty() && !q.isEmpty():
|
||||
return &XorTrie{key: q.key} // singleton
|
||||
case !p.isEmpty() && q.isEmpty():
|
||||
return &XorTrie{key: p.key} // singleton
|
||||
case !p.isEmpty() && !q.isEmpty():
|
||||
u := &XorTrie{}
|
||||
u.Add(depth, p.key)
|
||||
u.Add(depth, q.key)
|
||||
return u
|
||||
}
|
||||
case p.isLeaf() && !q.isLeaf():
|
||||
if p.isEmpty() {
|
||||
return Copy(q) //XXX: copy vs reuse? Mutable/immutable union?
|
||||
} else {
|
||||
if _, found := q.Find(p.key); found {
|
||||
XXX
|
||||
} else {
|
||||
XXX
|
||||
}
|
||||
}
|
||||
case !p.isLeaf() && q.isLeaf():
|
||||
return Union(q, p)
|
||||
case !p.isLeaf() && !q.isLeaf():
|
||||
XXX
|
||||
disjointUnion := &XorTrie{
|
||||
branch: [2]*XorTrie{
|
||||
union(deph+1, p.branch[0], q.branch[0]),
|
||||
union(deph+1, p.branch[1], q.branch[1]),
|
||||
},
|
||||
}
|
||||
disjointUnion.shrink()
|
||||
return disjointUnion
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
package xortrie
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestUnion(t *testing.T) {
|
||||
XXX
|
||||
}
|
36
xortrie/add.go
Normal file
36
xortrie/add.go
Normal file
@ -0,0 +1,36 @@
|
||||
package xortrie
|
||||
|
||||
// Add adds the key q to trie, returning a new trie.
|
||||
// Add is immutable/non-destructive: The original trie remains unchanged.
|
||||
func Add(trie *XorTrie, q TrieKey) *XorTrie {
|
||||
return add(0, trie, q)
|
||||
}
|
||||
|
||||
func add(depth int, trie *XorTrie, q TrieKey) *XorTrie {
|
||||
dir := q.BitAt(depth)
|
||||
if !trie.isLeaf() {
|
||||
s := &XorTrie{}
|
||||
s.branch[dir] = add(depth+1, trie.branch[dir], q)
|
||||
s.branch[1-dir] = trie.branch[1-dir]
|
||||
return s
|
||||
} else {
|
||||
if trie.key == nil {
|
||||
return &XorTrie{key: q}
|
||||
} else {
|
||||
if TrieKeyEqual(trie.key, q) {
|
||||
return trie
|
||||
} else {
|
||||
s := &XorTrie{}
|
||||
if q.BitAt(depth) == trie.key.BitAt(depth) {
|
||||
s.branch[dir] = add(depth+1, &XorTrie{key: trie.key}, q)
|
||||
s.branch[1-dir] = &XorTrie{}
|
||||
return s
|
||||
} else {
|
||||
s.branch[dir] = add(depth+1, &XorTrie{key: trie.key}, q)
|
||||
s.branch[1-dir] = &XorTrie{}
|
||||
}
|
||||
return s
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
26
xortrie/add_test.go
Normal file
26
xortrie/add_test.go
Normal file
@ -0,0 +1,26 @@
|
||||
package xortrie
|
||||
|
||||
import "testing"
|
||||
|
||||
// Verify mutable and immutable add do the same thing.
|
||||
func TestMutableAndImmutableAddSame(t *testing.T) {
|
||||
for _, s := range testAddSameSamples {
|
||||
mut := NewXorTrie()
|
||||
immut := NewXorTrie()
|
||||
for _, k := range s.Keys {
|
||||
mut.Add(k)
|
||||
immut = Add(immut, k)
|
||||
}
|
||||
if !XorTrieEqual(mut, immut) {
|
||||
t.Errorf("mutable trie %v differs from immutable trie %v", mut, immut)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type testAddSameSample struct {
|
||||
Keys []TrieKey
|
||||
}
|
||||
|
||||
var testAddSameSamples = []*testAddSameSample{
|
||||
{Keys: []TrieKey{{1, 3, 5, 7, 11, 13}}},
|
||||
}
|
@ -48,4 +48,12 @@ var testIntersectSamples = []*testIntersectSample{
|
||||
LeftKeys: []TrieKey{{1, 2, 3}},
|
||||
RightKeys: []TrieKey{{1, 3, 5}},
|
||||
},
|
||||
{
|
||||
LeftKeys: []TrieKey{{1, 2, 3, 4, 5, 6}},
|
||||
RightKeys: []TrieKey{{3, 5, 7}},
|
||||
},
|
||||
{
|
||||
LeftKeys: []TrieKey{{23, 3, 7, 13, 17}},
|
||||
RightKeys: []TrieKey{{2, 11, 17, 19, 23}},
|
||||
},
|
||||
}
|
||||
|
22
xortrie/key.go
Normal file
22
xortrie/key.go
Normal file
@ -0,0 +1,22 @@
|
||||
package xortrie
|
||||
|
||||
import "bytes"
|
||||
|
||||
// TrieKey is a vector of bits backed by a Go byte slice in big endian byte order and big-endian bit order.
|
||||
type TrieKey []byte
|
||||
|
||||
func (bs TrieKey) BitAt(offset int) byte {
|
||||
if bs[offset/8]&(1<<(offset%8)) == 0 {
|
||||
return 0
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
func (bs TrieKey) BitLen() int {
|
||||
return 8 * len(bs)
|
||||
}
|
||||
|
||||
func TrieKeyEqual(x, y TrieKey) bool {
|
||||
return bytes.Equal(x, y)
|
||||
}
|
@ -1,26 +1,5 @@
|
||||
package xortrie
|
||||
|
||||
import "bytes"
|
||||
|
||||
// TrieKey is a vector of bits backed by a Go byte slice in big endian byte order and big-endian bit order.
|
||||
type TrieKey []byte
|
||||
|
||||
func (bs TrieKey) BitAt(offset int) byte {
|
||||
if bs[offset/8]&(1<<(offset%8)) == 0 {
|
||||
return 0
|
||||
} else {
|
||||
return 1
|
||||
}
|
||||
}
|
||||
|
||||
func (bs TrieKey) BitLen() int {
|
||||
return 8 * len(bs)
|
||||
}
|
||||
|
||||
func TrieKeyEqual(x, y TrieKey) bool {
|
||||
return bytes.Equal(x, y)
|
||||
}
|
||||
|
||||
// XorTrie is a trie for equal-length bit vectors, which stores values only in the leaves.
|
||||
// XorTrie node invariants:
|
||||
// (1) Either both branches are nil, or both are non-nil.
|
||||
|
Loading…
x
Reference in New Issue
Block a user