make Elem interface have a method on it, implement that in seq (bleh), implement GoType

This commit is contained in:
Brian Picciano 2014-10-18 20:03:16 -04:00
parent 99b67fa801
commit 4188d0b84a
11 changed files with 432 additions and 196 deletions

View File

@ -28,9 +28,18 @@ func (kv *KV) Hash(i uint32) uint32 {
// compared to another KV, only compares the other key as well.
func (kv *KV) Equal(v types.Elem) bool {
if kv2, ok := v.(*KV); ok {
return equal(kv.Key, kv2.Key)
return kv.Key.Equal(kv2.Key)
}
return equal(kv.Key, v)
return kv.Key.Equal(v)
}
func (kv *KV) fullEqual(v types.Elem) bool {
kv2, ok := v.(*KV)
if !ok {
return false
}
return kv.Key.Equal(kv2.Key) && kv.Val.Equal(kv2.Val)
}
// Implementation of String for Stringer
@ -65,6 +74,38 @@ func (hm *HashMap) FirstRest() (types.Elem, Seq, bool) {
return el, &HashMap{nset.(*Set)}, ok
}
// Implementation of Equal for types.Elem interface. Completes in O(Nlog(M))
// time if e is another HashMap, where M is the size of the given HashMap
func (hm *HashMap) Equal(e types.Elem) bool {
// This can't just use Set's Equal because that would end up using KeyVal's
// Equal, which is not a true Equal
hm2, ok := e.(*HashMap)
if !ok {
return false
}
var el types.Elem
s := Seq(hm)
size := uint64(0)
for {
el, s, ok = s.FirstRest()
if !ok {
return size == hm2.Size()
}
size++
kv := el.(*KV)
k, v := kv.Key, kv.Val
v2, ok := hm2.Get(k)
if !ok || !v.Equal(v2) {
return false
}
}
}
// Returns a new HashMap with the given value set on the given key. Also returns
// whether or not this was the first time setting that key (false if it was
// already there and was overwritten). Has the same complexity as Set's SetVal

View File

@ -14,11 +14,16 @@ func kvints(kvs ...*KV) ([]*KV, []types.Elem) {
return kvs, ints
}
// Test that HashMap implements types.Elem (compile-time check)
func TestHashMapElem(t *T) {
_ = types.Elem(NewHashMap())
}
// Test creating a Set and calling the Seq interface methods on it
func TestHashMapSeq(t *T) {
kvs, ints := kvints(
KeyVal(1, "one"),
KeyVal(2, "two"),
keyValV(1, "one"),
keyValV(2, "two"),
)
// Testing creation and Seq interface methods
@ -29,27 +34,50 @@ func TestHashMapSeq(t *T) {
assertEmpty(ms, t)
}
// Test that the Equal method on HashMaps works
func TestHashMapEqual(t *T) {
hm, hm2 := NewHashMap(), NewHashMap()
assertValue(hm.Equal(hm2), true, t)
assertValue(hm2.Equal(hm), true, t)
hm = NewHashMap(keyValV(1, "one"), keyValV(2, "two"))
assertValue(hm.Equal(hm2), false, t)
assertValue(hm2.Equal(hm), false, t)
hm2 = NewHashMap(keyValV(1, "one"))
assertValue(hm.Equal(hm2), false, t)
assertValue(hm2.Equal(hm), false, t)
hm2 = NewHashMap(keyValV(1, "one"), keyValV(2, "three?"))
assertValue(hm.Equal(hm2), false, t)
assertValue(hm2.Equal(hm), false, t)
hm2 = NewHashMap(keyValV(1, "one"), keyValV(2, "two"))
assertValue(hm.Equal(hm2), true, t)
assertValue(hm2.Equal(hm), true, t)
}
// Test getting values from a HashMap
func TestHashMapGet(t *T) {
kvs := []*KV{
KeyVal(1, "one"),
KeyVal(2, "two"),
keyValV(1, "one"),
keyValV(2, "two"),
}
// Degenerate case
m := NewHashMap()
assertEmpty(m, t)
v, ok := m.Get(1)
v, ok := m.Get(types.GoType{1})
assertValue(v, nil, t)
assertValue(ok, false, t)
m = NewHashMap(kvs...)
v, ok = m.Get(1)
v, ok = m.Get(types.GoType{1})
assertSeqContentsHashMap(m, kvs, t)
assertValue(v, "one", t)
assertValue(v, types.GoType{"one"}, t)
assertValue(ok, true, t)
v, ok = m.Get(3)
v, ok = m.Get(types.GoType{3})
assertSeqContentsHashMap(m, kvs, t)
assertValue(v, nil, t)
assertValue(ok, false, t)
@ -60,21 +88,21 @@ func TestHashMapSet(t *T) {
// Set on empty
m := NewHashMap()
m1, ok := m.Set(1, "one")
m1, ok := m.Set(types.GoType{1}, types.GoType{"one"})
assertEmpty(m, t)
assertSeqContentsHashMap(m1, []*KV{KeyVal(1, "one")}, t)
assertSeqContentsHashMap(m1, []*KV{keyValV(1, "one")}, t)
assertValue(ok, true, t)
// Set on same key
m2, ok := m1.Set(1, "wat")
assertSeqContentsHashMap(m1, []*KV{KeyVal(1, "one")}, t)
assertSeqContentsHashMap(m2, []*KV{KeyVal(1, "wat")}, t)
m2, ok := m1.Set(types.GoType{1}, types.GoType{"wat"})
assertSeqContentsHashMap(m1, []*KV{keyValV(1, "one")}, t)
assertSeqContentsHashMap(m2, []*KV{keyValV(1, "wat")}, t)
assertValue(ok, false, t)
// Set on second new key
m3, ok := m2.Set(2, "two")
assertSeqContentsHashMap(m2, []*KV{KeyVal(1, "wat")}, t)
assertSeqContentsHashMap(m3, []*KV{KeyVal(1, "wat"), KeyVal(2, "two")}, t)
m3, ok := m2.Set(types.GoType{2}, types.GoType{"two"})
assertSeqContentsHashMap(m2, []*KV{keyValV(1, "wat")}, t)
assertSeqContentsHashMap(m3, []*KV{keyValV(1, "wat"), keyValV(2, "two")}, t)
assertValue(ok, true, t)
}
@ -83,31 +111,31 @@ func TestHashMapSet(t *T) {
func TestHashMapDel(t *T) {
kvs := []*KV{
KeyVal(1, "one"),
KeyVal(2, "two"),
KeyVal(3, "three"),
keyValV(1, "one"),
keyValV(2, "two"),
keyValV(3, "three"),
}
kvs1 := []*KV{
KeyVal(2, "two"),
KeyVal(3, "three"),
keyValV(2, "two"),
keyValV(3, "three"),
}
// Degenerate case
m := NewHashMap()
m1, ok := m.Del(1)
m1, ok := m.Del(types.GoType{1})
assertEmpty(m, t)
assertEmpty(m1, t)
assertValue(ok, false, t)
// Delete actual key
m = NewHashMap(kvs...)
m1, ok = m.Del(1)
m1, ok = m.Del(types.GoType{1})
assertSeqContentsHashMap(m, kvs, t)
assertSeqContentsHashMap(m1, kvs1, t)
assertValue(ok, true, t)
// Delete it again!
m2, ok := m1.Del(1)
m2, ok := m1.Del(types.GoType{1})
assertSeqContentsHashMap(m1, kvs1, t)
assertSeqContentsHashMap(m2, kvs1, t)
assertValue(ok, false, t)

View File

@ -30,63 +30,41 @@ func hash(v types.Elem, i uint32) uint32 {
case Setable:
return vt.Hash(i) % ARITY
case types.GoType:
switch gvt := vt.V.(type) {
case uint:
return uint32(vt) % ARITY
return uint32(gvt) % ARITY
case uint8:
return uint32(vt) % ARITY
return uint32(gvt) % ARITY
case uint32:
return uint32(vt) % ARITY
return uint32(gvt) % ARITY
case uint64:
return uint32(vt) % ARITY
return uint32(gvt) % ARITY
case int:
return uint32(vt) % ARITY
return uint32(gvt) % ARITY
case int8:
return uint32(vt) % ARITY
return uint32(gvt) % ARITY
case int16:
return uint32(vt) % ARITY
return uint32(gvt) % ARITY
case int32:
return uint32(vt) % ARITY
return uint32(gvt) % ARITY
case int64:
return uint32(vt) % ARITY
return uint32(gvt) % ARITY
case float32:
return uint32(vt) % ARITY
return uint32(gvt) % ARITY
case float64:
return uint32(vt) % ARITY
return uint32(gvt) % ARITY
case string:
return crc32.ChecksumIEEE([]byte(vt)) % ARITY
return crc32.ChecksumIEEE([]byte(gvt)) % ARITY
case []byte:
return crc32.ChecksumIEEE(vt) % ARITY
return crc32.ChecksumIEEE(gvt) % ARITY
}
}
default:
err := fmt.Sprintf("%s not hashable", reflect.TypeOf(v))
panic(err)
}
}
// Returns whether two values (potentially Setable's) are equivalent
func equal(v1, v2 types.Elem) bool {
if v1t, ok := v1.(Setable); ok {
return v1t.Equal(v2)
} else if v2t, ok := v2.(Setable); ok {
return v2t.Equal(v1)
} else if v1t, ok := v1.([]byte); ok {
if v2t, ok := v2.([]byte); ok {
if len(v1t) != len(v2t) {
return false
}
for i := range v1t {
if v1t[i] != v2t[i] {
return false
}
}
return true
}
return false
} else {
return v1 == v2
}
}
// The number of children each node in Set (implemented as a hash tree) can have
@ -142,7 +120,7 @@ func (set *Set) shallowTrySetOrInit(val types.Elem) (bool, bool) {
set.val = val
set.full = true
return true, false
} else if equal(set.val, val) {
} else if set.val.Equal(val) {
set.val = val
set.full = true
return true, true
@ -215,7 +193,7 @@ func (set *Set) SetVal(val types.Elem) (*Set, bool) {
func (set *Set) internalDelVal(val types.Elem, i uint32) (*Set, bool) {
if set == nil {
return nil, false
} else if set.full && equal(val, set.val) {
} else if set.full && set.val.Equal(val) {
cset := set.clone()
cset.val = nil
cset.full = false
@ -247,7 +225,7 @@ func (set *Set) DelVal(val types.Elem) (*Set, bool) {
func (set *Set) internalGetVal(val types.Elem, i uint32) (types.Elem, bool) {
if set == nil {
return nil, false
} else if set.full && equal(val, set.val) {
} else if set.full && set.val.Equal(val) {
return set.val, true
} else if set.kids == nil {
return nil, false
@ -301,6 +279,32 @@ func (set *Set) FirstRest() (types.Elem, Seq, bool) {
return el, Seq(restSet), ok
}
// Implementation of Equal for types.Elem interface. Completes in O(Nlog(M))
// time if e is another Set, where M is the size of the given Set
func (set *Set) Equal(e types.Elem) bool {
set2, ok := e.(*Set)
if !ok {
return false
}
var el types.Elem
s := Seq(set)
size := uint64(0)
for {
el, s, ok = s.FirstRest()
if !ok {
return size == set2.Size()
}
size++
_, ok = set2.GetVal(el)
if !ok {
return false
}
}
}
// Implementation of String for Stringer interface
func (set *Set) String() string {
return ToString(set, "#{", "}#")

View File

@ -6,9 +6,14 @@ import (
"github.com/mediocregopher/ginger/types"
)
// Test that HashSet implements types.Elem (compile-time check)
func TestSetElem(t *T) {
_ = types.Elem(NewSet())
}
// Test creating a Set and calling the Seq interface methods on it
func TestSetSeq(t *T) {
ints := []types.Elem{1, "a", 5.0}
ints := elemSliceV(nil, 1, "a", 5.0)
// Testing creation and Seq interface methods
s := NewSet(ints...)
@ -22,25 +27,48 @@ func TestSetSeq(t *T) {
assertValue(len(ToSlice(s)), 0, t)
}
// Test that the Equal method on Sets works
func TestSetEqual(t *T) {
s, s2 := NewSet(), NewSet()
assertValue(s.Equal(s2), true, t)
assertValue(s2.Equal(s), true, t)
s = NewSet(elemSliceV(0, 1, 2)...)
assertValue(s.Equal(s2), false, t)
assertValue(s2.Equal(s), false, t)
s2 = NewSet(elemSliceV(0, 1)...)
assertValue(s.Equal(s2), false, t)
assertValue(s2.Equal(s), false, t)
s2 = NewSet(elemSliceV(0, 1, 3)...)
assertValue(s.Equal(s2), false, t)
assertValue(s2.Equal(s), false, t)
s2 = NewSet(elemSliceV(0, 1, 2)...)
assertValue(s.Equal(s2), true, t)
assertValue(s2.Equal(s), true, t)
}
// Test setting a value on a Set
func TestSetVal(t *T) {
ints := []types.Elem{0, 1, 2, 3, 4}
ints1 := []types.Elem{0, 1, 2, 3, 4, 5}
ints := elemSliceV(0, 1, 2, 3, 4)
ints1 := elemSliceV(0, 1, 2, 3, 4, 5)
// Degenerate case
s := NewSet()
assertEmpty(s, t)
s, ok := s.SetVal(0)
assertSeqContentsSet(s, []types.Elem{0}, t)
s, ok := s.SetVal(types.GoType{0})
assertSeqContentsSet(s, elemSliceV(0), t)
assertValue(ok, true, t)
s = NewSet(ints...)
s1, ok := s.SetVal(5)
s1, ok := s.SetVal(types.GoType{5})
assertSeqContentsSet(s, ints, t)
assertSeqContentsSet(s1, ints1, t)
assertValue(ok, true, t)
s2, ok := s1.SetVal(5)
s2, ok := s1.SetVal(types.GoType{5})
assertSeqContentsSet(s1, ints1, t)
assertSeqContentsSet(s2, ints1, t)
assertValue(ok, false, t)
@ -48,41 +76,41 @@ func TestSetVal(t *T) {
// Test deleting a value from a Set
func TestDelVal(t *T) {
ints := []types.Elem{0, 1, 2, 3, 4}
ints1 := []types.Elem{0, 1, 2, 3}
ints2 := []types.Elem{1, 2, 3, 4}
ints3 := []types.Elem{1, 2, 3, 4, 5}
ints := elemSliceV(0, 1, 2, 3, 4)
ints1 := elemSliceV(0, 1, 2, 3)
ints2 := elemSliceV(1, 2, 3, 4)
ints3 := elemSliceV(1, 2, 3, 4, 5)
// Degenerate case
s := NewSet()
assertEmpty(s, t)
s, ok := s.DelVal(0)
s, ok := s.DelVal(types.GoType{0})
assertEmpty(s, t)
assertValue(ok, false, t)
s = NewSet(ints...)
s1, ok := s.DelVal(4)
s1, ok := s.DelVal(types.GoType{4})
assertSeqContentsSet(s, ints, t)
assertSeqContentsSet(s1, ints1, t)
assertValue(ok, true, t)
s1, ok = s1.DelVal(4)
s1, ok = s1.DelVal(types.GoType{4})
assertSeqContentsSet(s1, ints1, t)
assertValue(ok, false, t)
// 0 is the value on the root node of s, which is kind of a special case. We
// want to test deleting it and setting a new value (which should get put on
// the root node).
s2, ok := s.DelVal(0)
s2, ok := s.DelVal(types.GoType{0})
assertSeqContentsSet(s, ints, t)
assertSeqContentsSet(s2, ints2, t)
assertValue(ok, true, t)
s2, ok = s2.DelVal(0)
s2, ok = s2.DelVal(types.GoType{0})
assertSeqContentsSet(s2, ints2, t)
assertValue(ok, false, t)
s3, ok := s2.SetVal(5)
s3, ok := s2.SetVal(types.GoType{5})
assertSeqContentsSet(s2, ints2, t)
assertSeqContentsSet(s3, ints3, t)
assertValue(ok, true, t)
@ -92,36 +120,36 @@ func TestDelVal(t *T) {
func GetVal(t *T) {
//Degenerate case
s := NewSet()
v, ok := s.GetVal(1)
v, ok := s.GetVal(types.GoType{1})
assertValue(v, nil, t)
assertValue(ok, false, t)
s = NewSet(0, 1, 2, 3, 4)
v, ok = s.GetVal(1)
s = NewSet(elemSliceV(0, 1, 2, 3, 4)...)
v, ok = s.GetVal(types.GoType{1})
assertValue(v, 1, t)
assertValue(ok, true, t)
// After delete
s, _ = s.DelVal(1)
v, ok = s.GetVal(1)
s, _ = s.DelVal(types.GoType{1})
v, ok = s.GetVal(types.GoType{1})
assertValue(v, nil, t)
assertValue(ok, false, t)
// After set
s, _ = s.SetVal(1)
v, ok = s.GetVal(1)
s, _ = s.SetVal(types.GoType{1})
v, ok = s.GetVal(types.GoType{1})
assertValue(v, 1, t)
assertValue(ok, true, t)
// After delete root node
s, _ = s.DelVal(0)
v, ok = s.GetVal(0)
s, _ = s.DelVal(types.GoType{0})
v, ok = s.GetVal(types.GoType{0})
assertValue(v, nil, t)
assertValue(ok, false, t)
// After set root node
s, _ = s.SetVal(5)
v, ok = s.GetVal(5)
s, _ = s.SetVal(types.GoType{5})
v, ok = s.GetVal(types.GoType{5})
assertValue(v, 5, t)
assertValue(ok, true, t)
}
@ -133,25 +161,25 @@ func TestSetSize(t *T) {
assertValue(s.Size(), uint64(0), t)
// Initialization case
s = NewSet(0, 1, 2)
s = NewSet(elemSliceV(0, 1, 2)...)
assertValue(s.Size(), uint64(3), t)
// Setting (both value not in and a value already in)
s, _ = s.SetVal(3)
s, _ = s.SetVal(types.GoType{3})
assertValue(s.Size(), uint64(4), t)
s, _ = s.SetVal(3)
s, _ = s.SetVal(types.GoType{3})
assertValue(s.Size(), uint64(4), t)
// Deleting (both value already in and a value not in)
s, _ = s.DelVal(3)
s, _ = s.DelVal(types.GoType{3})
assertValue(s.Size(), uint64(3), t)
s, _ = s.DelVal(3)
s, _ = s.DelVal(types.GoType{3})
assertValue(s.Size(), uint64(3), t)
// Deleting and setting the root node
s, _ = s.DelVal(0)
s, _ = s.DelVal(types.GoType{0})
assertValue(s.Size(), uint64(2), t)
s, _ = s.SetVal(5)
s, _ = s.SetVal(types.GoType{5})
assertValue(s.Size(), uint64(3), t)
}
@ -162,8 +190,8 @@ func TestUnion(t *T) {
empty := NewSet()
assertEmpty(empty.Union(empty), t)
ints1 := []types.Elem{0, 1, 2}
ints2 := []types.Elem{3, 4, 5}
ints1 := elemSliceV(0, 1, 2)
ints2 := elemSliceV(3, 4, 5)
intsu := append(ints1, ints2...)
s1 := NewSet(ints1...)
s2 := NewSet(ints2...)
@ -183,10 +211,10 @@ func TestIntersection(t *T) {
empty := NewSet()
assertEmpty(empty.Intersection(empty), t)
ints1 := []types.Elem{0, 1, 2}
ints2 := []types.Elem{1, 2, 3}
ints3 := []types.Elem{4, 5, 6}
intsi := []types.Elem{1, 2}
ints1 := elemSliceV(0, 1, 2)
ints2 := elemSliceV(1, 2, 3)
ints3 := elemSliceV(4, 5, 6)
intsi := elemSliceV(1, 2)
s1 := NewSet(ints1...)
s2 := NewSet(ints2...)
s3 := NewSet(ints3...)
@ -208,9 +236,9 @@ func TestDifference(t *T) {
empty := NewSet()
assertEmpty(empty.Difference(empty), t)
ints1 := []types.Elem{0, 1, 2, 3}
ints2 := []types.Elem{2, 3, 4}
intsd := []types.Elem{0, 1}
ints1 := elemSliceV(0, 1, 2, 3)
ints2 := elemSliceV(2, 3, 4)
intsd := elemSliceV(0, 1)
s1 := NewSet(ints1...)
s2 := NewSet(ints2...)
@ -229,9 +257,9 @@ func TestSymDifference(t *T) {
empty := NewSet()
assertEmpty(empty.SymDifference(empty), t)
ints1 := []types.Elem{0, 1, 2, 3}
ints2 := []types.Elem{2, 3, 4}
intsd := []types.Elem{0, 1, 4}
ints1 := elemSliceV(0, 1, 2, 3)
ints2 := elemSliceV(2, 3, 4)
intsd := elemSliceV(0, 1, 4)
s1 := NewSet(ints1...)
s2 := NewSet(ints2...)

View File

@ -50,6 +50,20 @@ func (l *Lazy) FirstRest() (types.Elem, Seq, bool) {
}
}
// Implementation of Equal for types.Elem interface. Treats a List as another
// Lazy. Completes in O(N) time if e is another List or List.
func (l *Lazy) Equal(e types.Elem) bool {
var ls2 *List
if l2, ok := e.(*Lazy); ok {
ls2 = ToList(l2)
} else if ls2, ok = e.(*List); ok {
} else {
return false
}
ls := ToList(l)
return ls.Equal(ls2)
}
// Implementation of String for Stringer
func (l *Lazy) String() string {
return ToString(l, "<<", ">>")

View File

@ -7,19 +7,26 @@ import (
"github.com/mediocregopher/ginger/types"
)
// Test that Lazy implements types.Elem (compile-time check)
func TestLazyElem(t *T) {
_ = types.Elem(NewLazy(nil))
}
// Test lazy operation and thread-safety
func TestLazyBasic(t *T) {
ch := make(chan int)
ch := make(chan types.GoType)
mapfn := func(el types.Elem) types.Elem {
i := el.(int)
i := el.(types.GoType)
ch <- i
return i
}
intl := []types.Elem{0, 1, 2, 3, 4}
intl := elemSliceV(0, 1, 2, 3, 4)
l := NewList(intl...)
ml := LMap(mapfn, l)
// ml is a lazy list of intl, which will write to ch the first time any of
// the elements are read. This for loop ensures ml is thread-safe
for i := 0; i < 10; i++ {
go func() {
mlintl := ToSlice(ml)
@ -29,6 +36,8 @@ func TestLazyBasic(t *T) {
}()
}
// This loop and subsequent close ensure that ml only ever "creates" each
// element once
for _, el := range intl {
select {
case elch := <-ch:
@ -42,7 +51,7 @@ func TestLazyBasic(t *T) {
// Test that arbitrary Seqs can turn into Lazy
func TestToLazy(t *T) {
intl := []types.Elem{0, 1, 2, 3, 4}
intl := elemSliceV(0, 1, 2, 3, 4)
l := NewList(intl...)
ll := ToLazy(l)
assertSeqContents(ll, intl, t)

View File

@ -39,6 +39,36 @@ func (l *List) FirstRest() (types.Elem, Seq, bool) {
}
}
// Implementation of Equal for types.Elem interface. Completes in O(N) time if e
// is another List.
func (l *List) Equal(e types.Elem) bool {
l2, ok := e.(*List)
if !ok {
return false
}
var el, el2 types.Elem
var ok2 bool
s, s2 := Seq(l), Seq(l2)
for {
el, s, ok = s.FirstRest()
el2, s2, ok2 = s2.FirstRest()
if !ok && !ok2 {
return true
}
if ok != ok2 {
return false
}
if !el.Equal(el2) {
return false
}
}
}
// Implementation of String for Stringer interface.
func (l *List) String() string {
return ToString(l, "(", ")")

View File

@ -6,6 +6,11 @@ import (
"github.com/mediocregopher/ginger/types"
)
// Test that List implements types.Elem (compile-time check)
func TestListElem(t *T) {
_ = types.Elem(NewList())
}
// Asserts that the given list is properly formed and has all of its size fields
// filled in correctly
func assertSaneList(l *List, t *T) {
@ -22,7 +27,7 @@ func assertSaneList(l *List, t *T) {
// Test creating a list and calling the Seq interface methods on it
func TestListSeq(t *T) {
ints := []types.Elem{1, "a", 5.0}
ints := elemSliceV(1, "a", 5.0)
// Testing creation and Seq interface methods
l := NewList(ints...)
@ -40,39 +45,62 @@ func TestListSeq(t *T) {
assertValue(emptyl, nilpointer, t)
}
// Test that the Equal method on Lists works
func TestListEqual(t *T) {
l, l2 := NewList(), NewList()
assertValue(l.Equal(l2), true, t)
assertValue(l2.Equal(l), true, t)
l2 = NewList(elemSliceV(1, 2, 3)...)
assertValue(l.Equal(l2), false, t)
assertValue(l2.Equal(l), false, t)
l = NewList(elemSliceV(1, 2, 3, 4)...)
assertValue(l.Equal(l2), false, t)
assertValue(l2.Equal(l), false, t)
l2 = NewList(elemSliceV(1, 2, 3, 4)...)
assertValue(l.Equal(l2), true, t)
assertValue(l2.Equal(l), true, t)
}
// Test the string representation of a List
func TestStringSeq(t *T) {
l := NewList(0, 1, 2, 3)
l := NewList(elemSliceV(0, 1, 2, 3)...)
assertValue(l.String(), "( 0 1 2 3 )", t)
l = NewList(0, 1, 2, NewList(3, 4), 5, NewList(6, 7, 8))
l = NewList(elemSliceV(
0, 1, 2,
NewList(elemSliceV(3, 4)...),
5,
NewList(elemSliceV(6, 7, 8)...))...)
assertValue(l.String(), "( 0 1 2 ( 3 4 ) 5 ( 6 7 8 ) )", t)
}
// Test prepending an element to the beginning of a list
func TestPrepend(t *T) {
// Normal case
intl := []types.Elem{3, 2, 1, 0}
intl := elemSliceV(3, 2, 1, 0)
l := NewList(intl...)
nl := l.Prepend(4)
nl := l.Prepend(types.GoType{4})
assertSaneList(l, t)
assertSaneList(nl, t)
assertSeqContents(l, intl, t)
assertSeqContents(nl, []types.Elem{4, 3, 2, 1, 0}, t)
assertSeqContents(nl, elemSliceV(4, 3, 2, 1, 0), t)
// Degenerate case
l = NewList()
nl = l.Prepend(0)
nl = l.Prepend(types.GoType{0})
assertEmpty(l, t)
assertSaneList(nl, t)
assertSeqContents(nl, []types.Elem{0}, t)
assertSeqContents(nl, elemSliceV(0), t)
}
// Test prepending a Seq to the beginning of a list
func TestPrependSeq(t *T) {
//Normal case
intl1 := []types.Elem{3, 4}
intl2 := []types.Elem{0, 1, 2}
intl1 := elemSliceV(3, 4)
intl2 := elemSliceV(0, 1, 2)
l1 := NewList(intl1...)
l2 := NewList(intl2...)
nl := l1.PrependSeq(l2)
@ -81,7 +109,7 @@ func TestPrependSeq(t *T) {
assertSaneList(nl, t)
assertSeqContents(l1, intl1, t)
assertSeqContents(l2, intl2, t)
assertSeqContents(nl, []types.Elem{0, 1, 2, 3, 4}, t)
assertSeqContents(nl, elemSliceV(0, 1, 2, 3, 4), t)
// Degenerate cases
blank1 := NewList()
@ -105,34 +133,34 @@ func TestPrependSeq(t *T) {
// Test appending to the end of a List
func TestAppend(t *T) {
// Normal case
intl := []types.Elem{3, 2, 1}
intl := elemSliceV(3, 2, 1)
l := NewList(intl...)
nl := l.Append(0)
nl := l.Append(types.GoType{0})
assertSaneList(l, t)
assertSaneList(nl, t)
assertSeqContents(l, intl, t)
assertSeqContents(nl, []types.Elem{3, 2, 1, 0}, t)
assertSeqContents(nl, elemSliceV(3, 2, 1, 0), t)
// Edge case (algorithm gets weird here)
l = NewList(1)
nl = l.Append(0)
l = NewList(elemSliceV(1)...)
nl = l.Append(types.GoType{0})
assertSaneList(l, t)
assertSaneList(nl, t)
assertSeqContents(l, []types.Elem{1}, t)
assertSeqContents(nl, []types.Elem{1, 0}, t)
assertSeqContents(l, elemSliceV(1), t)
assertSeqContents(nl, elemSliceV(1, 0), t)
// Degenerate case
l = NewList()
nl = l.Append(0)
nl = l.Append(types.GoType{0})
assertEmpty(l, t)
assertSaneList(nl, t)
assertSeqContents(nl, []types.Elem{0}, t)
assertSeqContents(nl, elemSliceV(0), t)
}
// Test retrieving items from a List
func TestNth(t *T) {
// Normal case, in bounds
intl := []types.Elem{0, 2, 4, 6, 8}
intl := elemSliceV(0, 2, 4, 6, 8)
l := NewList(intl...)
r, ok := l.Nth(3)
assertSaneList(l, t)

View File

@ -50,11 +50,11 @@ func testSeqNoOrderGen(t *T, s Seq, ints []types.Elem) Seq {
// Test reversing a Seq
func TestReverse(t *T) {
// Normal case
intl := []types.Elem{3, 2, 1}
intl := elemSliceV(3, 2, 1)
l := NewList(intl...)
nl := Reverse(l)
assertSeqContents(l, intl, t)
assertSeqContents(nl, []types.Elem{1, 2, 3}, t)
assertSeqContents(nl, elemSliceV(1, 2, 3), t)
// Degenerate case
l = NewList()
@ -65,15 +65,15 @@ func TestReverse(t *T) {
func testMapGen(t *T, mapFn func(func(types.Elem) types.Elem, Seq) Seq) {
fn := func(n types.Elem) types.Elem {
return n.(int) + 1
return types.GoType{n.(types.GoType).V.(int) + 1}
}
// Normal case
intl := []types.Elem{1, 2, 3}
intl := elemSliceV(1, 2, 3)
l := NewList(intl...)
nl := mapFn(fn, l)
assertSeqContents(l, intl, t)
assertSeqContents(nl, []types.Elem{2, 3, 4}, t)
assertSeqContents(nl, elemSliceV(2, 3, 4), t)
// Degenerate case
l = NewList()
@ -95,27 +95,31 @@ func TestLMap(t *T) {
// Test reducing over a Seq
func TestReduce(t *T) {
fn := func(acc, el types.Elem) (types.Elem, bool) {
return acc.(int) + el.(int), false
acci := acc.(types.GoType).V.(int)
eli := el.(types.GoType).V.(int)
return types.GoType{acci + eli}, false
}
// Normal case
intl := []types.Elem{1, 2, 3, 4}
intl := elemSliceV(1, 2, 3, 4)
l := NewList(intl...)
r := Reduce(fn, 0, l)
r := Reduce(fn, types.GoType{0}, l)
assertSeqContents(l, intl, t)
assertValue(r, 10, t)
// Short-circuit case
fns := func(acc, el types.Elem) (types.Elem, bool) {
return acc.(int) + el.(int), el.(int) > 2
acci := acc.(types.GoType).V.(int)
eli := el.(types.GoType).V.(int)
return types.GoType{acci + eli}, eli > 2
}
r = Reduce(fns, 0, l)
r = Reduce(fns, types.GoType{0}, l)
assertSeqContents(l, intl, t)
assertValue(r, 6, t)
// Degenerate case
l = NewList()
r = Reduce(fn, 0, l)
r = Reduce(fn, types.GoType{0}, l)
assertEmpty(l, t)
assertValue(r, 0, t)
}
@ -123,11 +127,11 @@ func TestReduce(t *T) {
// Test the Any function
func TestAny(t *T) {
fn := func(el types.Elem) bool {
return el.(int) > 3
return el.(types.GoType).V.(int) > 3
}
// Value found case
intl := []types.Elem{1, 2, 3, 4}
intl := elemSliceV(1, 2, 3, 4)
l := NewList(intl...)
r, ok := Any(fn, l)
assertSeqContents(l, intl, t)
@ -135,7 +139,7 @@ func TestAny(t *T) {
assertValue(ok, true, t)
// Value not found case
intl = []types.Elem{1, 2, 3}
intl = elemSliceV(1, 2, 3)
l = NewList(intl...)
r, ok = Any(fn, l)
assertSeqContents(l, intl, t)
@ -153,18 +157,18 @@ func TestAny(t *T) {
// Test the All function
func TestAll(t *T) {
fn := func(el types.Elem) bool {
return el.(int) > 3
return el.(types.GoType).V.(int) > 3
}
// All match case
intl := []types.Elem{4, 5, 6}
intl := elemSliceV(4, 5, 6)
l := NewList(intl...)
ok := All(fn, l)
assertSeqContents(l, intl, t)
assertValue(ok, true, t)
// Not all match case
intl = []types.Elem{3, 4, 2, 5}
intl = elemSliceV(3, 4, 2, 5)
l = NewList(intl...)
ok = All(fn, l)
assertSeqContents(l, intl, t)
@ -179,15 +183,15 @@ func TestAll(t *T) {
func testFilterGen(t *T, filterFn func(func(types.Elem) bool, Seq) Seq) {
fn := func(el types.Elem) bool {
return el.(int)%2 != 0
return el.(types.GoType).V.(int)%2 != 0
}
// Normal case
intl := []types.Elem{1, 2, 3, 4, 5}
intl := elemSliceV(1, 2, 3, 4, 5)
l := NewList(intl...)
r := filterFn(fn, l)
assertSeqContents(l, intl, t)
assertSeqContents(r, []types.Elem{1, 3, 5}, t)
assertSeqContents(r, elemSliceV(1, 3, 5), t)
// Degenerate cases
l = NewList()
@ -209,19 +213,19 @@ func TestLFilter(t *T) {
// Test Flatten-ing of a Seq
func TestFlatten(t *T) {
// Normal case
intl1 := []types.Elem{0, 1, 2}
intl2 := []types.Elem{3, 4, 5}
intl1 := elemSliceV(0, 1, 2)
intl2 := elemSliceV(3, 4, 5)
l1 := NewList(intl1...)
l2 := NewList(intl2...)
blank := NewList()
intl := []types.Elem{-1, l1, l2, 6, blank, 7}
intl := elemSliceV(-1, l1, l2, 6, blank, 7)
l := NewList(intl...)
nl := Flatten(l)
assertSeqContents(l1, intl1, t)
assertSeqContents(l2, intl2, t)
assertEmpty(blank, t)
assertSeqContents(l, intl, t)
assertSeqContents(nl, []types.Elem{-1, 0, 1, 2, 3, 4, 5, 6, 7}, t)
assertSeqContents(nl, elemSliceV(-1, 0, 1, 2, 3, 4, 5, 6, 7), t)
// Degenerate case
nl = Flatten(blank)
@ -231,11 +235,11 @@ func TestFlatten(t *T) {
func testTakeGen(t *T, takeFn func(uint64, Seq) Seq) {
// Normal case
intl := []types.Elem{0, 1, 2, 3, 4}
intl := elemSliceV(0, 1, 2, 3, 4)
l := NewList(intl...)
nl := takeFn(3, l)
assertSeqContents(l, intl, t)
assertSeqContents(nl, []types.Elem{0, 1, 2}, t)
assertSeqContents(nl, elemSliceV(0, 1, 2), t)
// Edge cases
nl = takeFn(5, l)
@ -269,28 +273,28 @@ func TestLTake(t *T) {
func testTakeWhileGen(t *T, takeWhileFn func(func(types.Elem) bool, Seq) Seq) {
pred := func(el types.Elem) bool {
return el.(int) < 3
return el.(types.GoType).V.(int) < 3
}
// Normal case
intl := []types.Elem{0, 1, 2, 3, 4, 5}
intl := elemSliceV(0, 1, 2, 3, 4, 5)
l := NewList(intl...)
nl := takeWhileFn(pred, l)
assertSeqContents(l, intl, t)
assertSeqContents(nl, []types.Elem{0, 1, 2}, t)
assertSeqContents(nl, elemSliceV(0, 1, 2), t)
// Edge cases
intl = []types.Elem{5, 5, 5}
intl = elemSliceV(5, 5, 5)
l = NewList(intl...)
nl = takeWhileFn(pred, l)
assertSeqContents(l, intl, t)
assertEmpty(nl, t)
intl = []types.Elem{0, 1, 2}
intl = elemSliceV(0, 1, 2)
l = NewList(intl...)
nl = takeWhileFn(pred, l)
assertSeqContents(l, intl, t)
assertSeqContents(nl, []types.Elem{0, 1, 2}, t)
assertSeqContents(nl, elemSliceV(0, 1, 2), t)
// Degenerate case
l = NewList()
@ -312,11 +316,11 @@ func TestLTakeWhile(t *T) {
// Test dropping from a Seq
func TestDrop(t *T) {
// Normal case
intl := []types.Elem{0, 1, 2, 3, 4}
intl := elemSliceV(0, 1, 2, 3, 4)
l := NewList(intl...)
nl := Drop(3, l)
assertSeqContents(l, intl, t)
assertSeqContents(nl, []types.Elem{3, 4}, t)
assertSeqContents(nl, elemSliceV(3, 4), t)
// Edge cases
nl = Drop(5, l)
@ -341,24 +345,24 @@ func TestDrop(t *T) {
// Test dropping from a Seq until a given condition
func TestDropWhile(t *T) {
pred := func(el types.Elem) bool {
return el.(int) < 3
return el.(types.GoType).V.(int) < 3
}
// Normal case
intl := []types.Elem{0, 1, 2, 3, 4, 5}
intl := elemSliceV(0, 1, 2, 3, 4, 5)
l := NewList(intl...)
nl := DropWhile(pred, l)
assertSeqContents(l, intl, t)
assertSeqContents(nl, []types.Elem{3, 4, 5}, t)
assertSeqContents(nl, elemSliceV(3, 4, 5), t)
// Edge cases
intl = []types.Elem{5, 5, 5}
intl = elemSliceV(5, 5, 5)
l = NewList(intl...)
nl = DropWhile(pred, l)
assertSeqContents(l, intl, t)
assertSeqContents(nl, intl, t)
intl = []types.Elem{0, 1, 2}
intl = elemSliceV(0, 1, 2)
l = NewList(intl...)
nl = DropWhile(pred, l)
assertSeqContents(l, intl, t)

View File

@ -19,6 +19,18 @@ func intSlicesEq(a, b []types.Elem) bool {
return true
}
func elemSliceV(a ...interface{}) []types.Elem {
ret := make([]types.Elem, 0, len(a))
for i := range a {
if e, ok := a[i].(types.Elem); ok {
ret = append(ret, e)
} else {
ret = append(ret, types.GoType{a[i]})
}
}
return ret
}
// Asserts that the given Seq is empty (contains no elements)
func assertEmpty(s Seq, t *testing.T) {
if Size(s) != 0 {
@ -59,7 +71,7 @@ func assertSeqContentsSet(s Seq, ints []types.Elem, t *testing.T) {
}
func assertSeqContentsHashMap(s Seq, kvs []*KV, t *testing.T) {
m := map[types.Elem]bool{}
m := map[KV]bool{}
for i := range kvs {
m[*kvs[i]] = true
}
@ -76,9 +88,16 @@ func assertSeqContentsHashMap(s Seq, kvs []*KV, t *testing.T) {
}
// Asserts that v1 is the same as v2
func assertValue(v1, v2 types.Elem, t *testing.T) {
func assertValue(v1, v2 interface{}, t *testing.T) {
if gv1, ok := v1.(types.GoType); ok {
v1 = gv1.V
}
if gv2, ok := v2.(types.GoType); ok {
v2 = gv2.V
}
if v1 != v2 {
t.Fatalf("Value wrong: %v not %v", v1, v2)
t.Logf("Value wrong: %v not %v", v1, v2)
panic("bail")
}
}
@ -88,3 +107,7 @@ func assertInMap(v1 types.Elem, m map[types.Elem]bool, t *testing.T) {
t.Fatalf("Value not in set: %v not in %v", v1, m)
}
}
func keyValV(k, v interface{}) *KV {
return KeyVal(types.GoType{k}, types.GoType{v})
}

View File

@ -1,11 +1,38 @@
// This package describes ginger's base types and the interfaces covered by them
package types
import (
"fmt"
)
// Elem is a generic type which can be used as a wrapper type for all ginger
// types, both base types and data structures
type Elem interface {
// Returns whether one element is equal to another. Since all ginger values
// are immutable, this must be a deep-equals check.
Equal(Elem) bool
}
// Number can either be either an Int or a Float
type Number interface {
Elem
}
// Wraps a go type like int, string, or []byte. GoType is a struct whose only
// field is an interface{}, so using a pointer to is not necessary. Just pass
// around the value type.
type GoType struct {
V interface{}
}
func (g GoType) Equal(e Elem) bool {
if g2, ok := e.(GoType); ok {
return g.V == g2.V
}
return false
}
func (g GoType) String() string {
return fmt.Sprintf("%v", g.V)
}