Files
obitools4/pkg/obiprimer/roles.go
T
Eric Coissac 56f5fe33d7 Implement strict word finding and sequence filtering
This introduces new structures for defining sequence roles and constraints, core logic for strict word identification, and comprehensive unit tests to ensure accuracy and correct behavior.
2026-11-03 22:34:10 +11:00

77 lines
2.2 KiB
Go

// Package obiprimer implements the core of obiprimers, the OBITools4
// re-implementation of ecoPrimers: finding conserved words usable as primers
// in a set of example sequences, and the primer pairs they form.
package obiprimer
import (
"fmt"
"git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obiseq"
"git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obitax"
)
// Role tells how a sequence takes part in the primer search.
type Role uint8
const (
// Ignored sequences are not used at all.
Ignored Role = iota
// Example sequences are those the primers must amplify.
Example
// CounterExample sequences are those the primers must not amplify.
CounterExample
)
// Selection defines the roles from the taxonomy, with the ecoPrimers
// semantics of -r (restrict), -i (ignore) and -E (exception):
//
// - a sequence is an Example when it belongs to Restrict (all sequences when
// Restrict is nil) and not to Except;
// - otherwise it is a CounterExample when it belongs to Ignore or to Except;
// - otherwise it is Ignored.
//
// Nil predicates mean "no such constraint".
type Selection struct {
Restrict obiseq.SequencePredicate
Ignore obiseq.SequencePredicate
Except obiseq.SequencePredicate
}
// Role returns the role of seq.
func (s Selection) Role(seq *obiseq.BioSequence) Role {
inRestrict := s.Restrict == nil || s.Restrict(seq)
inExcept := s.Except != nil && s.Except(seq)
switch {
case inRestrict && !inExcept:
return Example
case inExcept, s.Ignore != nil && s.Ignore(seq):
return CounterExample
default:
return Ignored
}
}
// BelongingTo returns a predicate true for the sequences whose taxon is a
// sub-clade of at least one of the given taxids. It returns nil (no
// constraint) when taxids is empty.
func BelongingTo(taxonomy *obitax.Taxonomy, taxids []string) (obiseq.SequencePredicate, error) {
var pred obiseq.SequencePredicate
for _, id := range taxids {
taxon, _, err := taxonomy.Taxon(id)
if err != nil {
return nil, fmt.Errorf("obiprimer: taxid %q: %w", id, err)
}
if taxon == nil {
return nil, fmt.Errorf("obiprimer: taxid %q is unknown from the taxonomy", id)
}
p := obiseq.IsSubCladeOf(taxonomy, taxon)
if pred == nil {
pred = p
} else {
pred = pred.Or(p)
}
}
return pred, nil
}