diff --git a/search/persistent.go b/search/persistent.go index e855437..f10dcdb 100644 --- a/search/persistent.go +++ b/search/persistent.go @@ -26,7 +26,7 @@ type DynamicPersistent interface { Dynamic AtInstant(float64) Dynamic ThisInstant() Dynamic - ToStaticPersitent() StaticPersistent + ToStaticPersistent() StaticPersistent } // Persistable types are dynamic types, convertible to PersistentDynamic. diff --git a/search/tree/bst.go b/search/tree/bst.go index f5401aa..1ad282c 100644 --- a/search/tree/bst.go +++ b/search/tree/bst.go @@ -39,7 +39,7 @@ func (bst *BST) isValid() bool { // ToPersistent converts this BST into a Persistent BST. func (bst *BST) ToPersistent() search.DynamicPersistent { - pbst := new(PersistentBST) + pbst := new(FullPersistentBST) pbst.instant = math.MaxFloat64 * -1 pbst.instants = []BSTInstant{{BST: bst, instant: pbst.instant}} return pbst diff --git a/search/tree/persistent.go b/search/tree/persistent.go deleted file mode 100644 index ddd9549..0000000 --- a/search/tree/persistent.go +++ /dev/null @@ -1,102 +0,0 @@ -package tree - -import ( - "github.com/200sc/go-compgeo/printutil" - "github.com/200sc/go-compgeo/search" -) - -type PersistentBST struct { - instant float64 - index int - // Implicitly sorted - instants []BSTInstant -} - -type BSTInstant struct { - *BST - instant float64 -} - -func (pbst *PersistentBST) ThisInstant() search.Dynamic { - return pbst.instants[pbst.index] -} - -func (pbst *PersistentBST) AtInstant(ins float64) search.Dynamic { - // binary search - bot := 0 - top := len(pbst.instants) - 1 - var mid int - for { - if top <= bot { - if pbst.instants[bot].instant > ins { - bot-- - } - return pbst.instants[bot] - } - mid = (bot + top) / 2 - v := pbst.instants[mid].instant - if v == ins { - return pbst.instants[mid] - } else if v < ins { - bot = mid + 1 - } else { - top = mid - 1 - } - } -} - -func (pbst *PersistentBST) ToStaticPersitent() search.StaticPersistent { - return nil -} -func (pbst *PersistentBST) MinInstant() float64 { - return pbst.instants[0].instant -} -func (pbst *PersistentBST) MaxInstant() float64 { - return pbst.instants[len(pbst.instants)-1].instant -} -func (pbst *PersistentBST) SetInstant(ins float64) { - if ins < pbst.instant { - panic("Decreasing instants is not yet supported") - } else if ins == pbst.instant { - return - } - bsti := BSTInstant{} - bsti.BST = pbst.instants[len(pbst.instants)-1].copy() - bsti.instant = ins - pbst.instants = append(pbst.instants, bsti) - pbst.instant = ins - pbst.index++ -} - -func (pbst *PersistentBST) Insert(n search.Node) error { - return pbst.AtInstant(pbst.instant).Insert(n) -} -func (pbst *PersistentBST) Delete(n search.Node) error { - return pbst.AtInstant(pbst.instant).Delete(n) -} -func (pbst *PersistentBST) ToStatic() search.Static { - return pbst.AtInstant(pbst.instant).ToStatic() -} -func (pbst *PersistentBST) Size() int { - return pbst.AtInstant(pbst.instant).Size() -} -func (pbst *PersistentBST) InOrderTraverse() []search.Node { - return pbst.AtInstant(pbst.instant).InOrderTraverse() -} -func (pbst *PersistentBST) Search(f interface{}) (bool, interface{}) { - return pbst.AtInstant(pbst.instant).Search(f) -} -func (pbst *PersistentBST) SearchDown(f interface{}, d int) (search.Comparable, interface{}) { - return pbst.AtInstant(pbst.instant).SearchDown(f, d) -} -func (pbst *PersistentBST) SearchUp(f interface{}, u int) (search.Comparable, interface{}) { - return pbst.AtInstant(pbst.instant).SearchUp(f, u) -} -func (pbst *PersistentBST) String() string { - s := "" - for _, ins := range pbst.instants { - s += printutil.Stringf64(ins.instant) + ":\n" - s += ins.BST.String() - } - return s -} diff --git a/search/tree/persistentFullCopy.go b/search/tree/persistentFullCopy.go new file mode 100644 index 0000000..356e013 --- /dev/null +++ b/search/tree/persistentFullCopy.go @@ -0,0 +1,137 @@ +package tree + +import ( + "github.com/200sc/go-compgeo/printutil" + "github.com/200sc/go-compgeo/search" +) + +// FullPersistentBST is an implementation of a persistent +// binary search tree using full copies, with each +// instant represented by a separate BST. +type FullPersistentBST struct { + instant float64 + index int + // Implicitly sorted + instants []BSTInstant +} + +// BSTInstant is a single BST within a Persistent BST. +type BSTInstant struct { + *BST + instant float64 +} + +// ThisInstant returns the subtree at the most recent +// instant set +func (pbst *FullPersistentBST) ThisInstant() search.Dynamic { + return pbst.instants[pbst.index] +} + +// AtInstant returns the subtree of pbst at the given instant +func (pbst *FullPersistentBST) AtInstant(ins float64) search.Dynamic { + // binary search + bot := 0 + top := len(pbst.instants) - 1 + var mid int + for { + if top <= bot { + // round down + if pbst.instants[bot].instant > ins { + bot-- + } + return pbst.instants[bot] + } + mid = (bot + top) / 2 + v := pbst.instants[mid].instant + if v == ins { + return pbst.instants[mid] + } else if v < ins { + bot = mid + 1 + } else { + top = mid - 1 + } + } +} + +// ToStaticPersistent returns a static peristent version +// of the pbst +func (pbst *FullPersistentBST) ToStaticPersistent() search.StaticPersistent { + // Todo + return nil +} + +// MinInstant returns the minimum instant ever set on pbst. +func (pbst *FullPersistentBST) MinInstant() float64 { + return pbst.instants[0].instant +} + +// MaxInstant returns the maximum instant ever set on pbst. +func (pbst *FullPersistentBST) MaxInstant() float64 { + return pbst.instants[len(pbst.instants)-1].instant +} + +// SetInstant increments the pbst to the given instant. +func (pbst *FullPersistentBST) SetInstant(ins float64) { + if ins < pbst.instant { + panic("Decreasing instants is not yet supported") + } else if ins == pbst.instant { + return + } + bsti := BSTInstant{} + bsti.BST = pbst.instants[len(pbst.instants)-1].copy() + bsti.instant = ins + pbst.instants = append(pbst.instants, bsti) + pbst.instant = ins + pbst.index++ +} + +// Insert peforms Insert on the current set instant's search tree. +func (pbst *FullPersistentBST) Insert(n search.Node) error { + return pbst.AtInstant(pbst.instant).Insert(n) +} + +// Delete performs Delete on the current set instant's search tree. +func (pbst *FullPersistentBST) Delete(n search.Node) error { + return pbst.AtInstant(pbst.instant).Delete(n) +} + +// ToStatic performs ToStatic on the current set instant's search tree. +func (pbst *FullPersistentBST) ToStatic() search.Static { + return pbst.AtInstant(pbst.instant).ToStatic() +} + +// Size performs Size on the current set instant's search tree. +func (pbst *FullPersistentBST) Size() int { + return pbst.AtInstant(pbst.instant).Size() +} + +// InOrderTraverse performs InOrderTraverse on the current +// set instant's search tree. +func (pbst *FullPersistentBST) InOrderTraverse() []search.Node { + return pbst.AtInstant(pbst.instant).InOrderTraverse() +} + +// Search performs Search on the current set instant's search tree. +func (pbst *FullPersistentBST) Search(f interface{}) (bool, interface{}) { + return pbst.AtInstant(pbst.instant).Search(f) +} + +// SearchDown performs SearchDown on the current set instant's search tree. +func (pbst *FullPersistentBST) SearchDown(f interface{}, d int) (search.Comparable, interface{}) { + return pbst.AtInstant(pbst.instant).SearchDown(f, d) +} + +// SearchUp performs SearchUp on the current set instant's search tree. +func (pbst *FullPersistentBST) SearchUp(f interface{}, u int) (search.Comparable, interface{}) { + return pbst.AtInstant(pbst.instant).SearchUp(f, u) +} + +// String returns a string representation of pbst. +func (pbst *FullPersistentBST) String() string { + s := "" + for _, ins := range pbst.instants { + s += printutil.Stringf64(ins.instant) + ":\n" + s += ins.BST.String() + } + return s +} diff --git a/search/tree/persistent_test.go b/search/tree/persistent_test.go index 7d2dd13..be95f1d 100644 --- a/search/tree/persistent_test.go +++ b/search/tree/persistent_test.go @@ -1,2 +1,64 @@ package tree +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +var ( + instantInputs1 = [][]testNode{ + {{1, 1}}, + {{2, 1}}, + {{3, 1}}, + {{4, 1}}, + {{5, 1}}, + {{6, 1}}, + {{7, 1}}, + {{8, 1}}, + {{9, 1}}, + {{10, 1}}, + } +) + +func TestPBSTDefinedInput1(t *testing.T) { + tree := New(RedBlack).ToPersistent() + for i, ls := range instantInputs1 { + tree.SetInstant(float64(i)) + for _, v := range ls { + err := tree.Insert(v) + assert.Nil(t, err) + } + } + for i := range instantInputs1 { + t2 := tree.AtInstant(float64(i)) + for j, ls2 := range instantInputs1 { + if j > i { + break + } + for _, v := range ls2 { + found, _ := t2.Search(v.key) + assert.True(t, found) + } + } + } + for i, ls := range instantInputs1 { + tree.SetInstant(float64(len(instantInputs1) + i)) + for _, v := range ls { + err := tree.Delete(v) + assert.Nil(t, err) + } + } + for i := range instantInputs1 { + t2 := tree.AtInstant(float64(len(instantInputs1) + i)) + for j, ls2 := range instantInputs1 { + if j > i { + break + } + for _, v := range ls2 { + found, _ := t2.Search(v.key) + assert.False(t, found) + } + } + } +} diff --git a/search/tree/rb_test.go b/search/tree/rb_test.go index fcb9364..94c25c8 100644 --- a/search/tree/rb_test.go +++ b/search/tree/rb_test.go @@ -191,16 +191,16 @@ func TestPredSucc(t *testing.T) { tree.Insert(v) } - _, v := tree.SearchUp(9.5) + _, v := tree.SearchUp(9.5, 0) assert.Equal(t, v, compFloat(1)) - _, v = tree.SearchDown(9.5) + _, v = tree.SearchDown(9.5, 0) assert.Equal(t, v, compFloat(2)) t2 := tree.ToStatic() - _, v = t2.SearchUp(9.5) + _, v = t2.SearchUp(9.5, 0) assert.Equal(t, v, compFloat(1)) - _, v = t2.SearchDown(9.5) + _, v = t2.SearchDown(9.5, 0) assert.Equal(t, v, compFloat(2)) }