mirror of
https://github.com/metabarcoding/obitools4.git
synced 2026-11-09 09:24:41 +00:00
95 lines
2.3 KiB
Go
95 lines
2.3 KiB
Go
package obisimilarity
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"sync"
|
||
|
|
"sync/atomic"
|
||
|
|
)
|
||
|
|
|
||
|
|
// Entry is a distinct sequence of a dataset, with what the comparisons need.
|
||
|
|
type Entry struct {
|
||
|
|
Seq []byte
|
||
|
|
Length int
|
||
|
|
Count int // count of the sequence (cumulated for identical sequences)
|
||
|
|
Table *Table4
|
||
|
|
Sparse []uint16 // words of the table (see SparseWords), nil if unusable
|
||
|
|
// Ambiguous sequences contain IUPAC letters other than a, c, g and t: they
|
||
|
|
// have no 4-mer table, and the filter does not apply to them.
|
||
|
|
Ambiguous bool
|
||
|
|
}
|
||
|
|
|
||
|
|
const (
|
||
|
|
notNucleotide = iota
|
||
|
|
plainNucleotide
|
||
|
|
ambiguousNucleotide
|
||
|
|
)
|
||
|
|
|
||
|
|
var nucleotideClass = func() (t [256]uint8) {
|
||
|
|
for _, c := range "acgt" {
|
||
|
|
t[c] = plainNucleotide
|
||
|
|
}
|
||
|
|
for _, c := range "umrwsykvhdbn" {
|
||
|
|
t[c] = ambiguousNucleotide
|
||
|
|
}
|
||
|
|
return t
|
||
|
|
}()
|
||
|
|
|
||
|
|
// Classify tells if a sequence (in lower case) can be compared. It returns
|
||
|
|
// ok=false for an empty sequence and for a sequence with anything else than
|
||
|
|
// IUPAC letters; IUPAC letters other than a, c, g and t are only accepted if
|
||
|
|
// acceptAmbiguous is set, and then ambiguous is true.
|
||
|
|
func Classify(s []byte, acceptAmbiguous bool) (ok, ambiguous bool) {
|
||
|
|
if len(s) == 0 {
|
||
|
|
return false, false
|
||
|
|
}
|
||
|
|
for _, c := range s {
|
||
|
|
switch nucleotideClass[c] {
|
||
|
|
case notNucleotide:
|
||
|
|
return false, false
|
||
|
|
case ambiguousNucleotide:
|
||
|
|
if !acceptAmbiguous {
|
||
|
|
return false, false
|
||
|
|
}
|
||
|
|
ambiguous = true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return true, ambiguous
|
||
|
|
}
|
||
|
|
|
||
|
|
// BuildEntries describes n sequences, their 4-mer tables being computed in
|
||
|
|
// parallel. get returns the sequence i, its count, and whether it is
|
||
|
|
// ambiguous. The tables are stored contiguously.
|
||
|
|
func BuildEntries(n int, get func(i int) (seq []byte, count int, ambiguous bool), workers int) []Entry {
|
||
|
|
entries := make([]Entry, n)
|
||
|
|
tables := make([]Table4, n)
|
||
|
|
|
||
|
|
var next atomic.Int64
|
||
|
|
var wg sync.WaitGroup
|
||
|
|
for w := 0; w < max(workers, 1); w++ {
|
||
|
|
wg.Add(1)
|
||
|
|
go func() {
|
||
|
|
defer wg.Done()
|
||
|
|
for {
|
||
|
|
i := int(next.Add(1)) - 1
|
||
|
|
if i >= n {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
seq, count, ambiguous := get(i)
|
||
|
|
var sparse []uint16
|
||
|
|
if !ambiguous {
|
||
|
|
tables[i].Fill(seq)
|
||
|
|
sparse = SparseWords(&tables[i])
|
||
|
|
}
|
||
|
|
entries[i] = Entry{
|
||
|
|
Seq: seq,
|
||
|
|
Length: len(seq),
|
||
|
|
Count: count,
|
||
|
|
Table: &tables[i],
|
||
|
|
Sparse: sparse,
|
||
|
|
Ambiguous: ambiguous,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
}
|
||
|
|
wg.Wait()
|
||
|
|
return entries
|
||
|
|
}
|