Add immutable add and test.

This commit is contained in:
Petar Maymounkov 2020-03-25 10:36:00 -07:00
parent 3dde188b9a
commit 58bd8767f8
7 changed files with 92 additions and 77 deletions

View File

@ -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")
}

View File

@ -1,7 +0,0 @@
package xortrie
import "testing"
func TestUnion(t *testing.T) {
XXX
}

36
xortrie/add.go Normal file
View 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
View 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}}},
}

View File

@ -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
View 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)
}

View File

@ -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.