mirror of
https://github.com/metabarcoding/obitools4.git
synced 2025-12-07 16:20:27 +00:00
119 lines
2.8 KiB
Go
119 lines
2.8 KiB
Go
package obiutils
|
|
|
|
import (
|
|
"container/heap"
|
|
)
|
|
|
|
// MinMultiset maintient un multiset de valeurs et expose le minimum courant.
|
|
// T doit être comparable pour servir de clé de map. L'ordre est défini par less.
|
|
type MinMultiset[T comparable] struct {
|
|
pq priorityQueue[T] // tas min
|
|
less func(a, b T) bool
|
|
count map[T]int // cardinalité logique par valeur
|
|
pending map[T]int // suppressions en attente (lazy delete)
|
|
size int // taille logique totale
|
|
}
|
|
|
|
// New crée un multiset. less doit imposer un ordre strict total.
|
|
func NewMinMultiset[T comparable](less func(a, b T) bool) *MinMultiset[T] {
|
|
m := &MinMultiset[T]{
|
|
pq: priorityQueue[T]{less: less},
|
|
less: less,
|
|
count: make(map[T]int),
|
|
pending: make(map[T]int),
|
|
}
|
|
heap.Init(&m.pq)
|
|
return m
|
|
}
|
|
|
|
// Add ajoute une occurrence.
|
|
func (m *MinMultiset[T]) Add(v T) {
|
|
heap.Push(&m.pq, v)
|
|
m.count[v]++
|
|
m.size++
|
|
}
|
|
|
|
// RemoveOne retire UNE occurrence de v. Retourne false si absente.
|
|
func (m *MinMultiset[T]) RemoveOne(v T) bool {
|
|
if m.count[v] == 0 {
|
|
return false
|
|
}
|
|
m.count[v]--
|
|
m.pending[v]++
|
|
m.size--
|
|
m.shrink()
|
|
return true
|
|
}
|
|
|
|
// Min retourne le minimum courant. ok=false si vide.
|
|
func (m *MinMultiset[T]) Min() (v T, ok bool) {
|
|
if m.size == 0 {
|
|
var zero T
|
|
return zero, false
|
|
}
|
|
m.cleanTop()
|
|
return m.pq.data[0], true
|
|
}
|
|
|
|
// Len retourne la taille logique.
|
|
func (m *MinMultiset[T]) Len() int { return m.size }
|
|
|
|
// --- interne ---
|
|
|
|
// retire du sommet toutes les valeurs marquées à supprimer.
|
|
func (m *MinMultiset[T]) cleanTop() {
|
|
for m.pq.Len() > 0 {
|
|
top := m.pq.data[0]
|
|
if m.pending[top] > 0 {
|
|
m.pending[top]--
|
|
if m.pending[top] == 0 {
|
|
delete(m.pending, top) // ← nettoyage de la map
|
|
}
|
|
heap.Pop(&m.pq)
|
|
continue
|
|
}
|
|
break
|
|
}
|
|
}
|
|
|
|
// rééquilibre le tas si trop de tombstones.
|
|
func (m *MinMultiset[T]) shrink() {
|
|
// nettoyage léger au retrait pour borner la dérive
|
|
if m.pq.Len() > 0 {
|
|
m.cleanTop()
|
|
}
|
|
}
|
|
|
|
// priorityQueue implémente heap.Interface pour T.
|
|
type priorityQueue[T any] struct {
|
|
data []T
|
|
less func(a, b T) bool
|
|
}
|
|
|
|
func (q priorityQueue[T]) Len() int { return len(q.data) }
|
|
func (q priorityQueue[T]) Less(i, j int) bool { return q.less(q.data[i], q.data[j]) }
|
|
func (q priorityQueue[T]) Swap(i, j int) { q.data[i], q.data[j] = q.data[j], q.data[i] }
|
|
func (q *priorityQueue[T]) Push(x any) { q.data = append(q.data, x.(T)) }
|
|
func (q *priorityQueue[T]) Pop() any {
|
|
n := len(q.data)
|
|
x := q.data[n-1]
|
|
q.data = q.data[:n-1]
|
|
return x
|
|
}
|
|
func (q priorityQueue[T]) peek() (T, bool) {
|
|
if len(q.data) == 0 {
|
|
var z T
|
|
return z, false
|
|
}
|
|
return q.data[0], true
|
|
}
|
|
func (q *priorityQueue[T]) Top() (T, bool) { return q.peek() }
|
|
func (q *priorityQueue[T]) PushValue(v T) { heap.Push(q, v) }
|
|
func (q *priorityQueue[T]) PopValue() (T, bool) {
|
|
if q.Len() == 0 {
|
|
var z T
|
|
return z, false
|
|
}
|
|
return heap.Pop(q).(T), true
|
|
}
|