Files
obitools4/pkg/obisimilarity/index.go
T

106 lines
3.0 KiB
Go
Raw Normal View History

package obisimilarity
import "sort"
// Member is a copy of the entry of a sequence in an Index, with its own table.
type Member struct {
Entry
ID int // identifier given by the caller
Rank int // rank in the order of insertion
}
const tabChunk = 1024
// A Bucket holds the members of one length, in the order of insertion, and
// their tables by blocks (see tableBlock).
type Bucket struct {
Members []Member
blocks []tableBlock
width int
}
// Commons caches the result of the filter on one block of a bucket.
type Commons struct {
v [64]uint8
blk int
}
// Reset must be called when changing of bucket.
func (c *Commons) Reset() { c.blk = -1 }
// Common returns the sum of the minima of the 4-mer counters of the sequence
// whose Sparse words are given and of the member k of the bucket (without the
// overflow term), or -1 if it is not available, in which case PairScore
// computes it.
func (b *Bucket) Common(k int, sparse []uint16, c *Commons) int {
if b.width == 0 || sparse == nil {
return -1
}
if blk := k / b.width; blk != c.blk {
b.blocks[blk].common(b.width, sparse, c.v[:])
c.blk = blk
}
return int(c.v[k%b.width])
}
// Index stores sequences by length. A sequence can only be similar to the ones
// whose length is compatible with its own (see Scoring.LengthCompatible), so
// it only visits the buckets of these lengths, instead of all the sequences,
// and the filter works on blocks of sequences at once.
//
// An Index can grow, but not while it is read.
type Index struct {
width int // sequences per block of transposed tables, 0 if unused
byLen map[int]*Bucket
lens []int // lengths that have a bucket, increasing
tabs [][]Table4 // chunks holding the tables of the members
n int
}
// NewIndex returns an empty index.
func NewIndex() *Index {
return &Index{width: simdBlockWidth(), byLen: make(map[int]*Bucket)}
}
// Len is the number of sequences in the index.
func (x *Index) Len() int { return x.n }
// Lengths returns the lengths that have a bucket, in increasing order.
func (x *Index) Lengths() []int { return x.lens }
// Bucket returns the bucket of the sequences of the given length.
func (x *Index) Bucket(length int) *Bucket { return x.byLen[length] }
// Add stores a copy of the entry e as the next member of the index, with the
// identifier id, and returns it.
func (x *Index) Add(e Entry, id int) Member {
if x.n%tabChunk == 0 {
x.tabs = append(x.tabs, make([]Table4, tabChunk))
}
tab := &x.tabs[len(x.tabs)-1][x.n%tabChunk]
if e.Table != nil {
*tab = *e.Table
}
e.Table = tab
b := x.byLen[e.Length]
if b == nil {
b = &Bucket{width: x.width}
x.byLen[e.Length] = b
x.lens = append(x.lens, e.Length)
sort.Ints(x.lens)
}
if w := x.width; w > 0 && !e.Ambiguous {
if len(b.Members)%w == 0 {
b.blocks = append(b.blocks, newTableBlock(w))
}
b.blocks[len(b.blocks)-1].set(w, len(b.Members)%w, tab)
} else if w > 0 && len(b.Members)%w == 0 {
b.blocks = append(b.blocks, newTableBlock(w))
}
m := Member{Entry: e, ID: id, Rank: x.n}
b.Members = append(b.Members, m)
x.n++
return m
}