blob: 1b58a39aa323d939cab3fc307c8b86e58941235a [file] [log] [blame]
// Copyright 2014-2022 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build go1.18
// +build go1.18
package btree
import (
"fmt"
"math/rand"
"reflect"
"sort"
"sync"
"testing"
)
func intRange(s int, reverse bool) []int {
out := make([]int, s)
for i := 0; i < s; i++ {
v := i
if reverse {
v = s - i - 1
}
out[i] = v
}
return out
}
func intAll(t *BTreeG[int]) (out []int) {
t.Ascend(func(a int) bool {
out = append(out, a)
return true
})
return
}
func intAllRev(t *BTreeG[int]) (out []int) {
t.Descend(func(a int) bool {
out = append(out, a)
return true
})
return
}
func TestBTreeG(t *testing.T) {
tr := NewOrderedG[int](*btreeDegree)
const treeSize = 10000
for i := 0; i < 10; i++ {
if min, ok := tr.Min(); ok || min != 0 {
t.Fatalf("empty min, got %+v", min)
}
if max, ok := tr.Max(); ok || max != 0 {
t.Fatalf("empty max, got %+v", max)
}
for _, item := range rand.Perm(treeSize) {
if x, ok := tr.ReplaceOrInsert(item); ok || x != 0 {
t.Fatal("insert found item", item)
}
}
for _, item := range rand.Perm(treeSize) {
if x, ok := tr.ReplaceOrInsert(item); !ok || x != item {
t.Fatal("insert didn't find item", item)
}
}
want := 0
if min, ok := tr.Min(); !ok || min != want {
t.Fatalf("min: ok %v want %+v, got %+v", ok, want, min)
}
want = treeSize - 1
if max, ok := tr.Max(); !ok || max != want {
t.Fatalf("max: ok %v want %+v, got %+v", ok, want, max)
}
got := intAll(tr)
wantRange := intRange(treeSize, false)
if !reflect.DeepEqual(got, wantRange) {
t.Fatalf("mismatch:\n got: %v\nwant: %v", got, wantRange)
}
gotrev := intAllRev(tr)
wantrev := intRange(treeSize, true)
if !reflect.DeepEqual(gotrev, wantrev) {
t.Fatalf("mismatch:\n got: %v\nwant: %v", gotrev, wantrev)
}
for _, item := range rand.Perm(treeSize) {
if x, ok := tr.Delete(item); !ok || x != item {
t.Fatalf("didn't find %v", item)
}
}
if got = intAll(tr); len(got) > 0 {
t.Fatalf("some left!: %v", got)
}
if got = intAllRev(tr); len(got) > 0 {
t.Fatalf("some left!: %v", got)
}
}
}
func ExampleBTreeG() {
tr := NewOrderedG[int](*btreeDegree)
for i := 0; i < 10; i++ {
tr.ReplaceOrInsert(i)
}
fmt.Println("len: ", tr.Len())
v, ok := tr.Get(3)
fmt.Println("get3: ", v, ok)
v, ok = tr.Get(100)
fmt.Println("get100: ", v, ok)
v, ok = tr.Delete(4)
fmt.Println("del4: ", v, ok)
v, ok = tr.Delete(100)
fmt.Println("del100: ", v, ok)
v, ok = tr.ReplaceOrInsert(5)
fmt.Println("replace5: ", v, ok)
v, ok = tr.ReplaceOrInsert(100)
fmt.Println("replace100:", v, ok)
v, ok = tr.Min()
fmt.Println("min: ", v, ok)
v, ok = tr.DeleteMin()
fmt.Println("delmin: ", v, ok)
v, ok = tr.Max()
fmt.Println("max: ", v, ok)
v, ok = tr.DeleteMax()
fmt.Println("delmax: ", v, ok)
fmt.Println("len: ", tr.Len())
// Output:
// len: 10
// get3: 3 true
// get100: 0 false
// del4: 4 true
// del100: 0 false
// replace5: 5 true
// replace100: 0 false
// min: 0 true
// delmin: 0 true
// max: 100 true
// delmax: 100 true
// len: 8
}
func TestDeleteMinG(t *testing.T) {
tr := NewOrderedG[int](3)
for _, v := range rand.Perm(100) {
tr.ReplaceOrInsert(v)
}
var got []int
for v, ok := tr.DeleteMin(); ok; v, ok = tr.DeleteMin() {
got = append(got, v)
}
if want := intRange(100, false); !reflect.DeepEqual(got, want) {
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
}
}
func TestDeleteMaxG(t *testing.T) {
tr := NewOrderedG[int](3)
for _, v := range rand.Perm(100) {
tr.ReplaceOrInsert(v)
}
var got []int
for v, ok := tr.DeleteMax(); ok; v, ok = tr.DeleteMax() {
got = append(got, v)
}
if want := intRange(100, true); !reflect.DeepEqual(got, want) {
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
}
}
func TestAscendRangeG(t *testing.T) {
tr := NewOrderedG[int](2)
for _, v := range rand.Perm(100) {
tr.ReplaceOrInsert(v)
}
var got []int
tr.AscendRange(40, 60, func(a int) bool {
got = append(got, a)
return true
})
if want := intRange(100, false)[40:60]; !reflect.DeepEqual(got, want) {
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
}
got = got[:0]
tr.AscendRange(40, 60, func(a int) bool {
if a > 50 {
return false
}
got = append(got, a)
return true
})
if want := intRange(100, false)[40:51]; !reflect.DeepEqual(got, want) {
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
}
}
func TestDescendRangeG(t *testing.T) {
tr := NewOrderedG[int](2)
for _, v := range rand.Perm(100) {
tr.ReplaceOrInsert(v)
}
var got []int
tr.DescendRange(60, 40, func(a int) bool {
got = append(got, a)
return true
})
if want := intRange(100, true)[39:59]; !reflect.DeepEqual(got, want) {
t.Fatalf("descendrange:\n got: %v\nwant: %v", got, want)
}
got = got[:0]
tr.DescendRange(60, 40, func(a int) bool {
if a < 50 {
return false
}
got = append(got, a)
return true
})
if want := intRange(100, true)[39:50]; !reflect.DeepEqual(got, want) {
t.Fatalf("descendrange:\n got: %v\nwant: %v", got, want)
}
}
func TestAscendLessThanG(t *testing.T) {
tr := NewOrderedG[int](*btreeDegree)
for _, v := range rand.Perm(100) {
tr.ReplaceOrInsert(v)
}
var got []int
tr.AscendLessThan(60, func(a int) bool {
got = append(got, a)
return true
})
if want := intRange(100, false)[:60]; !reflect.DeepEqual(got, want) {
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
}
got = got[:0]
tr.AscendLessThan(60, func(a int) bool {
if a > 50 {
return false
}
got = append(got, a)
return true
})
if want := intRange(100, false)[:51]; !reflect.DeepEqual(got, want) {
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
}
}
func TestDescendLessOrEqualG(t *testing.T) {
tr := NewOrderedG[int](*btreeDegree)
for _, v := range rand.Perm(100) {
tr.ReplaceOrInsert(v)
}
var got []int
tr.DescendLessOrEqual(40, func(a int) bool {
got = append(got, a)
return true
})
if want := intRange(100, true)[59:]; !reflect.DeepEqual(got, want) {
t.Fatalf("descendlessorequal:\n got: %v\nwant: %v", got, want)
}
got = got[:0]
tr.DescendLessOrEqual(60, func(a int) bool {
if a < 50 {
return false
}
got = append(got, a)
return true
})
if want := intRange(100, true)[39:50]; !reflect.DeepEqual(got, want) {
t.Fatalf("descendlessorequal:\n got: %v\nwant: %v", got, want)
}
}
func TestAscendGreaterOrEqualG(t *testing.T) {
tr := NewOrderedG[int](*btreeDegree)
for _, v := range rand.Perm(100) {
tr.ReplaceOrInsert(v)
}
var got []int
tr.AscendGreaterOrEqual(40, func(a int) bool {
got = append(got, a)
return true
})
if want := intRange(100, false)[40:]; !reflect.DeepEqual(got, want) {
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
}
got = got[:0]
tr.AscendGreaterOrEqual(40, func(a int) bool {
if a > 50 {
return false
}
got = append(got, a)
return true
})
if want := intRange(100, false)[40:51]; !reflect.DeepEqual(got, want) {
t.Fatalf("ascendrange:\n got: %v\nwant: %v", got, want)
}
}
func TestDescendGreaterThanG(t *testing.T) {
tr := NewOrderedG[int](*btreeDegree)
for _, v := range rand.Perm(100) {
tr.ReplaceOrInsert(v)
}
var got []int
tr.DescendGreaterThan(40, func(a int) bool {
got = append(got, a)
return true
})
if want := intRange(100, true)[:59]; !reflect.DeepEqual(got, want) {
t.Fatalf("descendgreaterthan:\n got: %v\nwant: %v", got, want)
}
got = got[:0]
tr.DescendGreaterThan(40, func(a int) bool {
if a < 50 {
return false
}
got = append(got, a)
return true
})
if want := intRange(100, true)[:50]; !reflect.DeepEqual(got, want) {
t.Fatalf("descendgreaterthan:\n got: %v\nwant: %v", got, want)
}
}
func BenchmarkInsertG(b *testing.B) {
b.StopTimer()
insertP := rand.Perm(benchmarkTreeSize)
b.StartTimer()
i := 0
for i < b.N {
tr := NewOrderedG[int](*btreeDegree)
for _, item := range insertP {
tr.ReplaceOrInsert(item)
i++
if i >= b.N {
return
}
}
}
}
func BenchmarkSeekG(b *testing.B) {
b.StopTimer()
size := 100000
insertP := rand.Perm(size)
tr := NewOrderedG[int](*btreeDegree)
for _, item := range insertP {
tr.ReplaceOrInsert(item)
}
b.StartTimer()
for i := 0; i < b.N; i++ {
tr.AscendGreaterOrEqual(i%size, func(i int) bool { return false })
}
}
func BenchmarkDeleteInsertG(b *testing.B) {
b.StopTimer()
insertP := rand.Perm(benchmarkTreeSize)
tr := NewOrderedG[int](*btreeDegree)
for _, item := range insertP {
tr.ReplaceOrInsert(item)
}
b.StartTimer()
for i := 0; i < b.N; i++ {
tr.Delete(insertP[i%benchmarkTreeSize])
tr.ReplaceOrInsert(insertP[i%benchmarkTreeSize])
}
}
func BenchmarkDeleteInsertCloneOnceG(b *testing.B) {
b.StopTimer()
insertP := rand.Perm(benchmarkTreeSize)
tr := NewOrderedG[int](*btreeDegree)
for _, item := range insertP {
tr.ReplaceOrInsert(item)
}
tr = tr.Clone()
b.StartTimer()
for i := 0; i < b.N; i++ {
tr.Delete(insertP[i%benchmarkTreeSize])
tr.ReplaceOrInsert(insertP[i%benchmarkTreeSize])
}
}
func BenchmarkDeleteInsertCloneEachTimeG(b *testing.B) {
b.StopTimer()
insertP := rand.Perm(benchmarkTreeSize)
tr := NewOrderedG[int](*btreeDegree)
for _, item := range insertP {
tr.ReplaceOrInsert(item)
}
b.StartTimer()
for i := 0; i < b.N; i++ {
tr = tr.Clone()
tr.Delete(insertP[i%benchmarkTreeSize])
tr.ReplaceOrInsert(insertP[i%benchmarkTreeSize])
}
}
func BenchmarkDeleteG(b *testing.B) {
b.StopTimer()
insertP := rand.Perm(benchmarkTreeSize)
removeP := rand.Perm(benchmarkTreeSize)
b.StartTimer()
i := 0
for i < b.N {
b.StopTimer()
tr := NewOrderedG[int](*btreeDegree)
for _, v := range insertP {
tr.ReplaceOrInsert(v)
}
b.StartTimer()
for _, item := range removeP {
tr.Delete(item)
i++
if i >= b.N {
return
}
}
if tr.Len() > 0 {
panic(tr.Len())
}
}
}
func BenchmarkGetG(b *testing.B) {
b.StopTimer()
insertP := rand.Perm(benchmarkTreeSize)
removeP := rand.Perm(benchmarkTreeSize)
b.StartTimer()
i := 0
for i < b.N {
b.StopTimer()
tr := NewOrderedG[int](*btreeDegree)
for _, v := range insertP {
tr.ReplaceOrInsert(v)
}
b.StartTimer()
for _, item := range removeP {
tr.Get(item)
i++
if i >= b.N {
return
}
}
}
}
func BenchmarkGetCloneEachTimeG(b *testing.B) {
b.StopTimer()
insertP := rand.Perm(benchmarkTreeSize)
removeP := rand.Perm(benchmarkTreeSize)
b.StartTimer()
i := 0
for i < b.N {
b.StopTimer()
tr := NewOrderedG[int](*btreeDegree)
for _, v := range insertP {
tr.ReplaceOrInsert(v)
}
b.StartTimer()
for _, item := range removeP {
tr = tr.Clone()
tr.Get(item)
i++
if i >= b.N {
return
}
}
}
}
func BenchmarkAscendG(b *testing.B) {
arr := rand.Perm(benchmarkTreeSize)
tr := NewOrderedG[int](*btreeDegree)
for _, v := range arr {
tr.ReplaceOrInsert(v)
}
sort.Ints(arr)
b.ResetTimer()
for i := 0; i < b.N; i++ {
j := 0
tr.Ascend(func(item int) bool {
if item != arr[j] {
b.Fatalf("mismatch: expected: %v, got %v", arr[j], item)
}
j++
return true
})
}
}
func BenchmarkDescendG(b *testing.B) {
arr := rand.Perm(benchmarkTreeSize)
tr := NewOrderedG[int](*btreeDegree)
for _, v := range arr {
tr.ReplaceOrInsert(v)
}
sort.Ints(arr)
b.ResetTimer()
for i := 0; i < b.N; i++ {
j := len(arr) - 1
tr.Descend(func(item int) bool {
if item != arr[j] {
b.Fatalf("mismatch: expected: %v, got %v", arr[j], item)
}
j--
return true
})
}
}
func BenchmarkAscendRangeG(b *testing.B) {
arr := rand.Perm(benchmarkTreeSize)
tr := NewOrderedG[int](*btreeDegree)
for _, v := range arr {
tr.ReplaceOrInsert(v)
}
sort.Ints(arr)
b.ResetTimer()
for i := 0; i < b.N; i++ {
j := 100
tr.AscendRange(100, arr[len(arr)-100], func(item int) bool {
if item != arr[j] {
b.Fatalf("mismatch: expected: %v, got %v", arr[j], item)
}
j++
return true
})
if j != len(arr)-100 {
b.Fatalf("expected: %v, got %v", len(arr)-100, j)
}
}
}
func BenchmarkDescendRangeG(b *testing.B) {
arr := rand.Perm(benchmarkTreeSize)
tr := NewOrderedG[int](*btreeDegree)
for _, v := range arr {
tr.ReplaceOrInsert(v)
}
sort.Ints(arr)
b.ResetTimer()
for i := 0; i < b.N; i++ {
j := len(arr) - 100
tr.DescendRange(arr[len(arr)-100], 100, func(item int) bool {
if item != arr[j] {
b.Fatalf("mismatch: expected: %v, got %v", arr[j], item)
}
j--
return true
})
if j != 100 {
b.Fatalf("expected: %v, got %v", len(arr)-100, j)
}
}
}
func BenchmarkAscendGreaterOrEqualG(b *testing.B) {
arr := rand.Perm(benchmarkTreeSize)
tr := NewOrderedG[int](*btreeDegree)
for _, v := range arr {
tr.ReplaceOrInsert(v)
}
sort.Ints(arr)
b.ResetTimer()
for i := 0; i < b.N; i++ {
j := 100
k := 0
tr.AscendGreaterOrEqual(100, func(item int) bool {
if item != arr[j] {
b.Fatalf("mismatch: expected: %v, got %v", arr[j], item)
}
j++
k++
return true
})
if j != len(arr) {
b.Fatalf("expected: %v, got %v", len(arr), j)
}
if k != len(arr)-100 {
b.Fatalf("expected: %v, got %v", len(arr)-100, k)
}
}
}
func BenchmarkDescendLessOrEqualG(b *testing.B) {
arr := rand.Perm(benchmarkTreeSize)
tr := NewOrderedG[int](*btreeDegree)
for _, v := range arr {
tr.ReplaceOrInsert(v)
}
sort.Ints(arr)
b.ResetTimer()
for i := 0; i < b.N; i++ {
j := len(arr) - 100
k := len(arr)
tr.DescendLessOrEqual(arr[len(arr)-100], func(item int) bool {
if item != arr[j] {
b.Fatalf("mismatch: expected: %v, got %v", arr[j], item)
}
j--
k--
return true
})
if j != -1 {
b.Fatalf("expected: %v, got %v", -1, j)
}
if k != 99 {
b.Fatalf("expected: %v, got %v", 99, k)
}
}
}
func cloneTestG(t *testing.T, b *BTreeG[int], start int, p []int, wg *sync.WaitGroup, trees *[]*BTreeG[int], lock *sync.Mutex) {
t.Logf("Starting new clone at %v", start)
lock.Lock()
*trees = append(*trees, b)
lock.Unlock()
for i := start; i < cloneTestSize; i++ {
b.ReplaceOrInsert(p[i])
if i%(cloneTestSize/5) == 0 {
wg.Add(1)
go cloneTestG(t, b.Clone(), i+1, p, wg, trees, lock)
}
}
wg.Done()
}
func TestCloneConcurrentOperationsG(t *testing.T) {
b := NewOrderedG[int](*btreeDegree)
trees := []*BTreeG[int]{}
p := rand.Perm(cloneTestSize)
var wg sync.WaitGroup
wg.Add(1)
go cloneTestG(t, b, 0, p, &wg, &trees, &sync.Mutex{})
wg.Wait()
want := intRange(cloneTestSize, false)
t.Logf("Starting equality checks on %d trees", len(trees))
for i, tree := range trees {
if !reflect.DeepEqual(want, intAll(tree)) {
t.Errorf("tree %v mismatch", i)
}
}
t.Log("Removing half from first half")
toRemove := intRange(cloneTestSize, false)[cloneTestSize/2:]
for i := 0; i < len(trees)/2; i++ {
tree := trees[i]
wg.Add(1)
go func() {
for _, item := range toRemove {
tree.Delete(item)
}
wg.Done()
}()
}
wg.Wait()
t.Log("Checking all values again")
for i, tree := range trees {
var wantpart []int
if i < len(trees)/2 {
wantpart = want[:cloneTestSize/2]
} else {
wantpart = want
}
if got := intAll(tree); !reflect.DeepEqual(wantpart, got) {
t.Errorf("tree %v mismatch, want %v got %v", i, len(want), len(got))
}
}
}
func BenchmarkDeleteAndRestoreG(b *testing.B) {
items := rand.Perm(16392)
b.ResetTimer()
b.Run(`CopyBigFreeList`, func(b *testing.B) {
fl := NewFreeListG[int](16392)
tr := NewWithFreeListG[int](*btreeDegree, Less[int](), fl)
for _, v := range items {
tr.ReplaceOrInsert(v)
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
dels := make([]int, 0, tr.Len())
tr.Ascend(func(b int) bool {
dels = append(dels, b)
return true
})
for _, del := range dels {
tr.Delete(del)
}
// tr is now empty, we make a new empty copy of it.
tr = NewWithFreeListG[int](*btreeDegree, Less[int](), fl)
for _, v := range items {
tr.ReplaceOrInsert(v)
}
}
})
b.Run(`Copy`, func(b *testing.B) {
tr := NewOrderedG[int](*btreeDegree)
for _, v := range items {
tr.ReplaceOrInsert(v)
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
dels := make([]int, 0, tr.Len())
tr.Ascend(func(b int) bool {
dels = append(dels, b)
return true
})
for _, del := range dels {
tr.Delete(del)
}
// tr is now empty, we make a new empty copy of it.
tr = NewOrderedG[int](*btreeDegree)
for _, v := range items {
tr.ReplaceOrInsert(v)
}
}
})
b.Run(`ClearBigFreelist`, func(b *testing.B) {
fl := NewFreeListG[int](16392)
tr := NewWithFreeListG[int](*btreeDegree, Less[int](), fl)
for _, v := range items {
tr.ReplaceOrInsert(v)
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
tr.Clear(true)
for _, v := range items {
tr.ReplaceOrInsert(v)
}
}
})
b.Run(`Clear`, func(b *testing.B) {
tr := NewOrderedG[int](*btreeDegree)
for _, v := range items {
tr.ReplaceOrInsert(v)
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
tr.Clear(true)
for _, v := range items {
tr.ReplaceOrInsert(v)
}
}
})
}