3
0
mirror of https://github.com/ergochat/ergo.git synced 2024-12-22 02:32:39 +01:00
ergo/vendor/github.com/tidwall/rtred/rtree.go
Shivaram Lingamneni fd3cbab6ee bump buntdb to v1.2.3
Potentially fixes the database corruption seen on #1603
2021-04-01 20:45:15 -04:00

279 lines
5.3 KiB
Go

package rtred
import (
"math"
"sync"
"github.com/tidwall/rtred/base"
)
type Iterator func(item Item) bool
type Item interface {
Rect(ctx interface{}) (min []float64, max []float64)
}
type RTree struct {
dims int
maxEntries int
ctx interface{}
trs []*base.RTree
used int
}
func New(ctx interface{}) *RTree {
tr := &RTree{
ctx: ctx,
dims: 20,
maxEntries: 13,
}
tr.trs = make([]*base.RTree, 20)
return tr
}
func (tr *RTree) Insert(item Item) {
if item == nil {
panic("nil item")
}
min, max := item.Rect(tr.ctx)
if len(min) != len(max) {
return // just return
panic("invalid item rectangle")
}
if len(min) < 1 || len(min) > len(tr.trs) {
return // just return
panic("invalid dimension")
}
btr := tr.trs[len(min)-1]
if btr == nil {
btr = base.New(len(min), tr.maxEntries)
tr.trs[len(min)-1] = btr
tr.used++
}
amin := make([]float64, len(min))
amax := make([]float64, len(max))
for i := 0; i < len(min); i++ {
amin[i], amax[i] = min[i], max[i]
}
btr.Insert(amin, amax, item)
}
func (tr *RTree) Remove(item Item) {
if item == nil {
panic("nil item")
}
min, max := item.Rect(tr.ctx)
if len(min) != len(max) {
return // just return
panic("invalid item rectangle")
}
if len(min) < 1 || len(min) > len(tr.trs) {
return // just return
panic("invalid dimension")
}
btr := tr.trs[len(min)-1]
if btr == nil {
return
}
amin := make([]float64, len(min))
amax := make([]float64, len(max))
for i := 0; i < len(min); i++ {
amin[i], amax[i] = min[i], max[i]
}
btr.Remove(amin, amax, item)
if btr.IsEmpty() {
tr.trs[len(min)-1] = nil
tr.used--
}
}
func (tr *RTree) Reset() {
for i := 0; i < len(tr.trs); i++ {
tr.trs[i] = nil
}
tr.used = 0
}
func (tr *RTree) Count() int {
var count int
for _, btr := range tr.trs {
if btr != nil {
count += btr.Count()
}
}
return count
}
func (tr *RTree) Search(bounds Item, iter Iterator) {
if bounds == nil {
panic("nil bounds being used for search")
}
min, max := bounds.Rect(tr.ctx)
if len(min) != len(max) {
return // just return
panic("invalid item rectangle")
}
if len(min) < 1 || len(min) > len(tr.trs) {
return // just return
panic("invalid dimension")
}
used := tr.used
for i, btr := range tr.trs {
if used == 0 {
break
}
if btr != nil {
if !search(btr, min, max, i+1, iter) {
return
}
used--
}
}
}
func search(btr *base.RTree, min, max []float64, dims int, iter Iterator) bool {
amin := make([]float64, dims)
amax := make([]float64, dims)
for i := 0; i < dims; i++ {
if i < len(min) {
amin[i] = min[i]
amax[i] = max[i]
} else {
amin[i] = math.Inf(-1)
amax[i] = math.Inf(+1)
}
}
var ended bool
btr.Search(amin, amax, func(item interface{}) bool {
if !iter(item.(Item)) {
ended = true
return false
}
return true
})
return !ended
}
func (tr *RTree) KNN(bounds Item, center bool, iter func(item Item, dist float64) bool) {
if bounds == nil {
panic("nil bounds being used for search")
}
min, max := bounds.Rect(tr.ctx)
if len(min) != len(max) {
return // just return
panic("invalid item rectangle")
}
if len(min) < 1 || len(min) > len(tr.trs) {
return // just return
panic("invalid dimension")
}
if tr.used == 0 {
return
}
if tr.used == 1 {
for i, btr := range tr.trs {
if btr != nil {
knn(btr, min, max, center, i+1, func(item interface{}, dist float64) bool {
return iter(item.(Item), dist)
})
break
}
}
return
}
type queueT struct {
done bool
step int
item Item
dist float64
}
var mu sync.Mutex
var ended bool
queues := make(map[int][]queueT)
cond := sync.NewCond(&mu)
for i, btr := range tr.trs {
if btr != nil {
dims := i + 1
mu.Lock()
queues[dims] = []queueT{}
cond.Signal()
mu.Unlock()
go func(dims int, btr *base.RTree) {
knn(btr, min, max, center, dims, func(item interface{}, dist float64) bool {
mu.Lock()
if ended {
mu.Unlock()
return false
}
queues[dims] = append(queues[dims], queueT{item: item.(Item), dist: dist})
cond.Signal()
mu.Unlock()
return true
})
mu.Lock()
queues[dims] = append(queues[dims], queueT{done: true})
cond.Signal()
mu.Unlock()
}(dims, btr)
}
}
mu.Lock()
for {
ready := true
for i := range queues {
if len(queues[i]) == 0 {
ready = false
break
}
if queues[i][0].done {
delete(queues, i)
}
}
if len(queues) == 0 {
break
}
if ready {
var j int
var minDist float64
var minItem Item
var minQueue int
for i := range queues {
if j == 0 || queues[i][0].dist < minDist {
minDist = queues[i][0].dist
minItem = queues[i][0].item
minQueue = i
}
}
queues[minQueue] = queues[minQueue][1:]
if !iter(minItem, minDist) {
ended = true
break
}
continue
}
cond.Wait()
}
mu.Unlock()
}
func knn(btr *base.RTree, min, max []float64, center bool, dims int, iter func(item interface{}, dist float64) bool) bool {
amin := make([]float64, dims)
amax := make([]float64, dims)
for i := 0; i < dims; i++ {
if i < len(min) {
amin[i] = min[i]
amax[i] = max[i]
} else {
amin[i] = math.Inf(-1)
amax[i] = math.Inf(+1)
}
}
var ended bool
btr.KNN(amin, amax, center, func(item interface{}, dist float64) bool {
if !iter(item.(Item), dist) {
ended = true
return false
}
return true
})
return !ended
}