2026-02-05 14:41:41 +01:00
|
|
|
package obikmer
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
|
|
"git.metabarcoding.org/obitools/obitools4/obitools4/pkg/obiseq"
|
|
|
|
|
"github.com/RoaringBitmap/roaring/roaring64"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// KmerSet encapsule un ensemble de k-mers stockés dans un Roaring Bitmap
|
|
|
|
|
// Fournit des méthodes utilitaires pour manipuler des ensembles de k-mers
|
|
|
|
|
type KmerSet struct {
|
2026-02-05 15:32:19 +01:00
|
|
|
id string // Identifiant unique du KmerSet
|
|
|
|
|
k int // Taille des k-mers (immutable)
|
2026-02-05 15:02:27 +01:00
|
|
|
bitmap *roaring64.Bitmap // Bitmap contenant les k-mers
|
|
|
|
|
Metadata map[string]interface{} // Métadonnées utilisateur (clé=valeur atomique)
|
2026-02-05 14:41:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewKmerSet crée un nouveau KmerSet vide
|
|
|
|
|
func NewKmerSet(k int) *KmerSet {
|
|
|
|
|
return &KmerSet{
|
2026-02-05 15:32:19 +01:00
|
|
|
k: k,
|
2026-02-05 15:02:27 +01:00
|
|
|
bitmap: roaring64.New(),
|
|
|
|
|
Metadata: make(map[string]interface{}),
|
2026-02-05 14:41:41 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewKmerSetFromBitmap crée un KmerSet à partir d'un bitmap existant
|
|
|
|
|
func NewKmerSetFromBitmap(k int, bitmap *roaring64.Bitmap) *KmerSet {
|
|
|
|
|
return &KmerSet{
|
2026-02-05 15:32:19 +01:00
|
|
|
k: k,
|
2026-02-05 15:02:27 +01:00
|
|
|
bitmap: bitmap,
|
|
|
|
|
Metadata: make(map[string]interface{}),
|
2026-02-05 14:41:41 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-05 15:32:19 +01:00
|
|
|
// K retourne la taille des k-mers (immutable)
|
|
|
|
|
func (ks *KmerSet) K() int {
|
|
|
|
|
return ks.k
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-05 14:41:41 +01:00
|
|
|
// Add ajoute un k-mer à l'ensemble
|
|
|
|
|
func (ks *KmerSet) Add(kmer uint64) {
|
|
|
|
|
ks.bitmap.Add(kmer)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AddSequence ajoute tous les k-mers d'une séquence à l'ensemble
|
|
|
|
|
// Utilise un itérateur pour éviter l'allocation d'un vecteur intermédiaire
|
|
|
|
|
func (ks *KmerSet) AddSequence(seq *obiseq.BioSequence) {
|
|
|
|
|
rawSeq := seq.Sequence()
|
2026-02-05 15:32:19 +01:00
|
|
|
for canonical := range IterNormalizedKmers(rawSeq, ks.k) {
|
2026-02-05 14:41:41 +01:00
|
|
|
ks.bitmap.Add(canonical)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AddSequences ajoute tous les k-mers de plusieurs séquences en batch
|
|
|
|
|
func (ks *KmerSet) AddSequences(sequences *obiseq.BioSequenceSlice) {
|
|
|
|
|
for _, seq := range *sequences {
|
|
|
|
|
ks.AddSequence(seq)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Contains vérifie si un k-mer est dans l'ensemble
|
|
|
|
|
func (ks *KmerSet) Contains(kmer uint64) bool {
|
|
|
|
|
return ks.bitmap.Contains(kmer)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Len retourne le nombre de k-mers dans l'ensemble
|
|
|
|
|
func (ks *KmerSet) Len() uint64 {
|
|
|
|
|
return ks.bitmap.GetCardinality()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// MemoryUsage retourne l'utilisation mémoire en bytes
|
|
|
|
|
func (ks *KmerSet) MemoryUsage() uint64 {
|
|
|
|
|
return ks.bitmap.GetSizeInBytes()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clear vide l'ensemble
|
|
|
|
|
func (ks *KmerSet) Clear() {
|
|
|
|
|
ks.bitmap.Clear()
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-05 15:32:19 +01:00
|
|
|
// Copy crée une copie de l'ensemble (cohérent avec BioSequence.Copy)
|
|
|
|
|
func (ks *KmerSet) Copy() *KmerSet {
|
2026-02-05 15:02:27 +01:00
|
|
|
// Copier les métadonnées
|
|
|
|
|
metadata := make(map[string]interface{}, len(ks.Metadata))
|
|
|
|
|
for k, v := range ks.Metadata {
|
|
|
|
|
metadata[k] = v
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-05 14:41:41 +01:00
|
|
|
return &KmerSet{
|
2026-02-05 15:32:19 +01:00
|
|
|
id: ks.id,
|
|
|
|
|
k: ks.k,
|
2026-02-05 15:02:27 +01:00
|
|
|
bitmap: ks.bitmap.Clone(),
|
|
|
|
|
Metadata: metadata,
|
2026-02-05 14:41:41 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-05 15:32:19 +01:00
|
|
|
// Id retourne l'identifiant du KmerSet (cohérent avec BioSequence.Id)
|
|
|
|
|
func (ks *KmerSet) Id() string {
|
|
|
|
|
return ks.id
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetId définit l'identifiant du KmerSet (cohérent avec BioSequence.SetId)
|
|
|
|
|
func (ks *KmerSet) SetId(id string) {
|
|
|
|
|
ks.id = id
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-05 14:41:41 +01:00
|
|
|
// Union retourne l'union de cet ensemble avec un autre
|
|
|
|
|
func (ks *KmerSet) Union(other *KmerSet) *KmerSet {
|
2026-02-05 15:32:19 +01:00
|
|
|
if ks.k != other.k {
|
|
|
|
|
panic(fmt.Sprintf("Cannot union KmerSets with different k values: %d vs %d", ks.k, other.k))
|
2026-02-05 14:41:41 +01:00
|
|
|
}
|
|
|
|
|
result := ks.bitmap.Clone()
|
|
|
|
|
result.Or(other.bitmap)
|
2026-02-05 15:32:19 +01:00
|
|
|
return NewKmerSetFromBitmap(ks.k, result)
|
2026-02-05 14:41:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Intersect retourne l'intersection de cet ensemble avec un autre
|
|
|
|
|
func (ks *KmerSet) Intersect(other *KmerSet) *KmerSet {
|
2026-02-05 15:32:19 +01:00
|
|
|
if ks.k != other.k {
|
|
|
|
|
panic(fmt.Sprintf("Cannot intersect KmerSets with different k values: %d vs %d", ks.k, other.k))
|
2026-02-05 14:41:41 +01:00
|
|
|
}
|
|
|
|
|
result := ks.bitmap.Clone()
|
|
|
|
|
result.And(other.bitmap)
|
2026-02-05 15:32:19 +01:00
|
|
|
return NewKmerSetFromBitmap(ks.k, result)
|
2026-02-05 14:41:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Difference retourne la différence de cet ensemble avec un autre (this - other)
|
|
|
|
|
func (ks *KmerSet) Difference(other *KmerSet) *KmerSet {
|
2026-02-05 15:32:19 +01:00
|
|
|
if ks.k != other.k {
|
|
|
|
|
panic(fmt.Sprintf("Cannot subtract KmerSets with different k values: %d vs %d", ks.k, other.k))
|
2026-02-05 14:41:41 +01:00
|
|
|
}
|
|
|
|
|
result := ks.bitmap.Clone()
|
|
|
|
|
result.AndNot(other.bitmap)
|
2026-02-05 15:32:19 +01:00
|
|
|
return NewKmerSetFromBitmap(ks.k, result)
|
2026-02-05 14:41:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Iterator retourne un itérateur sur tous les k-mers de l'ensemble
|
|
|
|
|
func (ks *KmerSet) Iterator() roaring64.IntIterable64 {
|
|
|
|
|
return ks.bitmap.Iterator()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Bitmap retourne le bitmap sous-jacent (pour compatibilité)
|
|
|
|
|
func (ks *KmerSet) Bitmap() *roaring64.Bitmap {
|
|
|
|
|
return ks.bitmap
|
|
|
|
|
}
|