123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- package vector
- import (
- "errors"
- "math/rand"
- "strconv"
- "testing"
- "time"
- )
- // Nx is the minimum number of elements for the internal tree of the vector to
- // be x levels deep.
- const (
- N1 = tailMaxLen + 1 // 33
- N2 = nodeSize + tailMaxLen + 1 // 65
- N3 = nodeSize*nodeSize + tailMaxLen + 1 // 1057
- N4 = nodeSize*nodeSize*nodeSize + tailMaxLen + 1 // 32801
- )
- func init() {
- rand.Seed(time.Now().UTC().UnixNano())
- }
- func TestVector(t *testing.T) {
- run := func(n int) {
- t.Run(strconv.Itoa(n), func(t *testing.T) {
- v := testCons(t, n)
- testIndex(t, v, 0, n)
- testAssoc(t, v, "233")
- testIterator(t, v.Iterator(), 0, n)
- testPop(t, v)
- })
- }
- for i := 0; i <= N3; i++ {
- run(i)
- }
- run(N4)
- }
- // Regression test against #4.
- func TestIterator_VectorWithNil(t *testing.T) {
- run := func(n int) {
- t.Run(strconv.Itoa(n), func(t *testing.T) {
- v := Empty
- for i := 0; i < n; i++ {
- v = v.Cons(nil)
- }
- iterated := 0
- for it := v.Iterator(); it.HasElem(); it.Next() {
- iterated++
- if it.Elem() != nil {
- t.Errorf("element not nil")
- }
- }
- if iterated != n {
- t.Errorf("did not iterate %d items", n)
- }
- })
- }
- for i := 0; i <= N3; i++ {
- run(i)
- }
- run(N4)
- }
- // testCons creates a vector containing 0...n-1 with Cons, and ensures that the
- // length of the old and new vectors are expected after each Cons. It returns
- // the created vector.
- func testCons(t *testing.T, n int) Vector {
- v := Empty
- for i := 0; i < n; i++ {
- oldv := v
- v = v.Cons(i)
- if count := oldv.Len(); count != i {
- t.Errorf("oldv.Count() == %v, want %v", count, i)
- }
- if count := v.Len(); count != i+1 {
- t.Errorf("v.Count() == %v, want %v", count, i+1)
- }
- }
- return v
- }
- // testIndex tests Index, assuming that the vector contains begin...int-1.
- func testIndex(t *testing.T, v Vector, begin, end int) {
- n := v.Len()
- for i := 0; i < n; i++ {
- elem, _ := v.Index(i)
- if elem != i {
- t.Errorf("v.Index(%v) == %v, want %v", i, elem, i)
- }
- }
- for _, i := range []int{-2, -1, n, n + 1, n * 2} {
- if elem, _ := v.Index(i); elem != nil {
- t.Errorf("v.Index(%d) == %v, want nil", i, elem)
- }
- }
- }
- // testIterator tests the iterator, assuming that the result is begin...end-1.
- func testIterator(t *testing.T, it Iterator, begin, end int) {
- i := begin
- for ; it.HasElem(); it.Next() {
- elem := it.Elem()
- if elem != i {
- t.Errorf("iterator produce %v, want %v", elem, i)
- }
- i++
- }
- if i != end {
- t.Errorf("iterator produces up to %v, want %v", i, end)
- }
- }
- // testAssoc tests Assoc by replacing each element.
- func testAssoc(t *testing.T, v Vector, subst interface{}) {
- n := v.Len()
- for i := 0; i <= n; i++ {
- oldv := v
- v = v.Assoc(i, subst)
- if i < n {
- elem, _ := oldv.Index(i)
- if elem != i {
- t.Errorf("oldv.Index(%v) == %v, want %v", i, elem, i)
- }
- }
- elem, _ := v.Index(i)
- if elem != subst {
- t.Errorf("v.Index(%v) == %v, want %v", i, elem, subst)
- }
- }
- n++
- for _, i := range []int{-1, n + 1, n + 2, n * 2} {
- newv := v.Assoc(i, subst)
- if newv != nil {
- t.Errorf("v.Assoc(%d) = %v, want nil", i, newv)
- }
- }
- }
- // testPop tests Pop by removing each element.
- func testPop(t *testing.T, v Vector) {
- n := v.Len()
- for i := 0; i < n; i++ {
- oldv := v
- v = v.Pop()
- if count := oldv.Len(); count != n-i {
- t.Errorf("oldv.Count() == %v, want %v", count, n-i)
- }
- if count := v.Len(); count != n-i-1 {
- t.Errorf("oldv.Count() == %v, want %v", count, n-i-1)
- }
- }
- newv := v.Pop()
- if newv != nil {
- t.Errorf("v.Pop() = %v, want nil", newv)
- }
- }
- func TestSubVector(t *testing.T) {
- v := Empty
- for i := 0; i < 10; i++ {
- v = v.Cons(i)
- }
- sv := v.SubVector(0, 4)
- testIndex(t, sv, 0, 4)
- testAssoc(t, sv, "233")
- testIterator(t, sv.Iterator(), 0, 4)
- testPop(t, sv)
- sv = v.SubVector(1, 4)
- if !checkVector(sv, 1, 2, 3) {
- t.Errorf("v[0:4] is not expected")
- }
- if !checkVector(sv.Assoc(1, "233"), 1, "233", 3) {
- t.Errorf("v[0:4].Assoc is not expected")
- }
- if !checkVector(sv.Cons("233"), 1, 2, 3, "233") {
- t.Errorf("v[0:4].Cons is not expected")
- }
- if !checkVector(sv.Pop(), 1, 2) {
- t.Errorf("v[0:4].Pop is not expected")
- }
- if !checkVector(sv.SubVector(1, 2), 2) {
- t.Errorf("v[0:4][1:2] is not expected")
- }
- testIterator(t, sv.Iterator(), 1, 4)
- if !checkVector(v.SubVector(1, 1)) {
- t.Errorf("v[1:1] is not expected")
- }
- // Begin is allowed to be equal to n if end is also n
- if !checkVector(v.SubVector(10, 10)) {
- t.Errorf("v[10:10] is not expected")
- }
- bad := v.SubVector(-1, 0)
- if bad != nil {
- t.Errorf("v.SubVector(-1, 0) = %v, want nil", bad)
- }
- bad = v.SubVector(5, 100)
- if bad != nil {
- t.Errorf("v.SubVector(5, 100) = %v, want nil", bad)
- }
- bad = v.SubVector(-1, 100)
- if bad != nil {
- t.Errorf("v.SubVector(-1, 100) = %v, want nil", bad)
- }
- bad = v.SubVector(4, 2)
- if bad != nil {
- t.Errorf("v.SubVector(4, 2) = %v, want nil", bad)
- }
- }
- // Regression test for https://b.elv.sh/1287: crash when tree has a height >= 1
- // and start of subvector is in the tail.
- func TestSubVector_BeginFromTail(t *testing.T) {
- v := Empty
- for i := 0; i < 65; i++ {
- v = v.Cons(i)
- }
- sv := v.SubVector(64, 65)
- testIterator(t, sv.Iterator(), 64, 65)
- }
- func checkVector(v Vector, values ...interface{}) bool {
- if v.Len() != len(values) {
- return false
- }
- for i, a := range values {
- if x, _ := v.Index(i); x != a {
- return false
- }
- }
- return true
- }
- func TestVectorEqual(t *testing.T) {
- v1, v2 := Empty, Empty
- for i := 0; i < N3; i++ {
- elem := rand.Int63()
- v1 = v1.Cons(elem)
- v2 = v2.Cons(elem)
- if !eqVector(v1, v2) {
- t.Errorf("Not equal after Cons'ing %d elements", i+1)
- }
- }
- }
- func eqVector(v1, v2 Vector) bool {
- if v1.Len() != v2.Len() {
- return false
- }
- for i := 0; i < v1.Len(); i++ {
- a1, _ := v1.Index(i)
- a2, _ := v2.Index(i)
- if a1 != a2 {
- return false
- }
- }
- return true
- }
- var marshalJSONTests = []struct {
- in Vector
- wantOut string
- wantErr error
- }{
- {makeVector("1", 2, nil), `["1",2,null]`, nil},
- {makeVector("1", makeVector(2)), `["1",[2]]`, nil},
- {makeVector(0, 1, 2, 3, 4, 5).SubVector(1, 5), `[1,2,3,4]`, nil},
- {makeVector(0, func() {}), "", errors.New("element 1: json: unsupported type: func()")},
- }
- func TestMarshalJSON(t *testing.T) {
- for i, test := range marshalJSONTests {
- out, err := test.in.MarshalJSON()
- if string(out) != test.wantOut {
- t.Errorf("v%d.MarshalJSON -> out %q, want %q", i, out, test.wantOut)
- }
- if err == nil || test.wantErr == nil {
- if err != test.wantErr {
- t.Errorf("v%d.MarshalJSON -> err %v, want %v", i, err, test.wantErr)
- }
- } else {
- if err.Error() != test.wantErr.Error() {
- t.Errorf("v%d.MarshalJSON -> err %v, want %v", i, err, test.wantErr)
- }
- }
- }
- }
- func makeVector(elements ...interface{}) Vector {
- v := Empty
- for _, element := range elements {
- v = v.Cons(element)
- }
- return v
- }
- func BenchmarkConsNativeN1(b *testing.B) { benchmarkNativeAppend(b, N1) }
- func BenchmarkConsNativeN2(b *testing.B) { benchmarkNativeAppend(b, N2) }
- func BenchmarkConsNativeN3(b *testing.B) { benchmarkNativeAppend(b, N3) }
- func BenchmarkConsNativeN4(b *testing.B) { benchmarkNativeAppend(b, N4) }
- func benchmarkNativeAppend(b *testing.B, n int) {
- for r := 0; r < b.N; r++ {
- var s []interface{}
- for i := 0; i < n; i++ {
- s = append(s, i)
- }
- }
- }
- func BenchmarkConsPersistentN1(b *testing.B) { benchmarkCons(b, N1) }
- func BenchmarkConsPersistentN2(b *testing.B) { benchmarkCons(b, N2) }
- func BenchmarkConsPersistentN3(b *testing.B) { benchmarkCons(b, N3) }
- func BenchmarkConsPersistentN4(b *testing.B) { benchmarkCons(b, N4) }
- func benchmarkCons(b *testing.B, n int) {
- for r := 0; r < b.N; r++ {
- v := Empty
- for i := 0; i < n; i++ {
- v = v.Cons(i)
- }
- }
- }
- var (
- sliceN4 = make([]interface{}, N4)
- vectorN4 = Empty
- )
- func init() {
- for i := 0; i < N4; i++ {
- vectorN4 = vectorN4.Cons(i)
- }
- }
- var x interface{}
- func BenchmarkIndexSeqNativeN4(b *testing.B) { benchmarkIndexSeqNative(b, N4) }
- func benchmarkIndexSeqNative(b *testing.B, n int) {
- for r := 0; r < b.N; r++ {
- for i := 0; i < n; i++ {
- x = sliceN4[i]
- }
- }
- }
- func BenchmarkIndexSeqPersistentN4(b *testing.B) { benchmarkIndexSeqPersistent(b, N4) }
- func benchmarkIndexSeqPersistent(b *testing.B, n int) {
- for r := 0; r < b.N; r++ {
- for i := 0; i < n; i++ {
- x, _ = vectorN4.Index(i)
- }
- }
- }
- var randIndicies []int
- func init() {
- randIndicies = make([]int, N4)
- for i := 0; i < N4; i++ {
- randIndicies[i] = rand.Intn(N4)
- }
- }
- func BenchmarkIndexRandNative(b *testing.B) {
- for r := 0; r < b.N; r++ {
- for _, i := range randIndicies {
- x = sliceN4[i]
- }
- }
- }
- func BenchmarkIndexRandPersistent(b *testing.B) {
- for r := 0; r < b.N; r++ {
- for _, i := range randIndicies {
- x, _ = vectorN4.Index(i)
- }
- }
- }
- func nativeEqual(s1, s2 []int) bool {
- if len(s1) != len(s2) {
- return false
- }
- for i, v1 := range s1 {
- if v1 != s2[i] {
- return false
- }
- }
- return true
- }
- func BenchmarkEqualNative(b *testing.B) {
- b.StopTimer()
- var s1, s2 []int
- for i := 0; i < N4; i++ {
- s1 = append(s1, i)
- s2 = append(s2, i)
- }
- b.StartTimer()
- for r := 0; r < b.N; r++ {
- eq := nativeEqual(s1, s2)
- if !eq {
- panic("not equal")
- }
- }
- }
- func BenchmarkEqualPersistent(b *testing.B) {
- b.StopTimer()
- v1, v2 := Empty, Empty
- for i := 0; i < N4; i++ {
- v1 = v1.Cons(i)
- v2 = v2.Cons(i)
- }
- b.StartTimer()
- for r := 0; r < b.N; r++ {
- eq := eqVector(v1, v2)
- if !eq {
- panic("not equal")
- }
- }
- }
|