2026-11-03 14:41:30 +11:00
|
|
|
package obiprimer
|
|
|
|
|
|
|
|
|
|
// baseCode maps a nucleotide to 0..3 (A,C,G,T; U is T) and everything else to 4,
|
|
|
|
|
// which never equals a word base.
|
|
|
|
|
var baseCode = func() (t [256]byte) {
|
|
|
|
|
for i := range t {
|
|
|
|
|
t[i] = 4
|
|
|
|
|
}
|
|
|
|
|
for i, c := range "ACGT" {
|
|
|
|
|
t[c], t[c+'a'-'A'] = byte(i), byte(i)
|
|
|
|
|
}
|
|
|
|
|
t['U'], t['u'] = 3, 3
|
|
|
|
|
return
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
func codesOf(s string) []byte {
|
|
|
|
|
out := make([]byte, len(s))
|
|
|
|
|
for i := range out {
|
|
|
|
|
out[i] = baseCode[s[i]]
|
|
|
|
|
}
|
|
|
|
|
return out
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// matchNaive is the reference implementation: every window of every sequence
|
|
|
|
|
// is compared with every word, base by base. It is slow and exists to define
|
|
|
|
|
// the exact semantics (see MatchParams) that faster implementations must
|
|
|
|
|
// reproduce.
|
|
|
|
|
func matchNaive(c *Corpus, words []uint64, p MatchParams) ([]WordMatches, error) {
|
|
|
|
|
if err := p.validate(); err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
fwd, rev := wordStrings(words, p.K)
|
|
|
|
|
fc, rc := make([][]byte, len(words)), make([][]byte, len(words))
|
|
|
|
|
for i := range words {
|
|
|
|
|
fc[i], rc[i] = codesOf(fwd[i]), codesOf(rev[i])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mk := func() scanFunc {
|
|
|
|
|
var codes []byte
|
2026-11-03 22:33:09 +11:00
|
|
|
return func(raw []byte, emit func(word, pos, errors int, reverse bool)) {
|
2026-11-03 14:41:30 +11:00
|
|
|
n := len(raw)
|
|
|
|
|
if n < p.K {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
codes = codes[:0]
|
|
|
|
|
for _, b := range raw {
|
|
|
|
|
codes = append(codes, baseCode[b])
|
|
|
|
|
}
|
|
|
|
|
windows := n - p.K + 1
|
|
|
|
|
if p.Circular {
|
|
|
|
|
codes = append(codes, codes[:p.K-1]...)
|
|
|
|
|
windows = n
|
|
|
|
|
}
|
|
|
|
|
for pos := range windows {
|
|
|
|
|
win := codes[pos : pos+p.K]
|
|
|
|
|
for w := range words {
|
|
|
|
|
for strand, pat := range [2][]byte{fc[w], rc[w]} {
|
|
|
|
|
errs := 0
|
|
|
|
|
for j, b := range pat {
|
|
|
|
|
if win[j] != b {
|
|
|
|
|
if errs++; errs > p.MaxErrors {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if errs <= p.MaxErrors {
|
|
|
|
|
emit(w, pos, errs, strand == 1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return matchCorpus(c, words, mk), nil
|
|
|
|
|
}
|