/// Trait for kmer row filters used in the rebuild command.
///
/// `row` contains raw per-genome counts (or 0/1 for presence/absence data).
/// `n_genomes` equals `row.len()`.
pub trait KmerFilter: Send + Sync {
    fn passes(&self, row: &[u32], n_genomes: usize) -> bool;
}

// ── Quorum filters ─────────────────────────────────────────────────────────────

fn present_count(row: &[u32], threshold: u32) -> usize {
    row.iter().filter(|&&v| v > threshold).count()
}

/// At least `frac` fraction of genomes contain this kmer (count > `threshold`).
pub struct MinGenomeFraction {
    pub frac: f64,
    pub threshold: u32,
}

impl KmerFilter for MinGenomeFraction {
    fn passes(&self, row: &[u32], n_genomes: usize) -> bool {
        let p = present_count(row, self.threshold);
        p as f64 / n_genomes as f64 >= self.frac
    }
}

/// At most `frac` fraction of genomes contain this kmer (count > `threshold`).
pub struct MaxGenomeFraction {
    pub frac: f64,
    pub threshold: u32,
}

impl KmerFilter for MaxGenomeFraction {
    fn passes(&self, row: &[u32], n_genomes: usize) -> bool {
        let p = present_count(row, self.threshold);
        p as f64 / n_genomes as f64 <= self.frac
    }
}

/// At least `count` genomes contain this kmer (count > `threshold`).
pub struct MinGenomeCount {
    pub count: usize,
    pub threshold: u32,
}

impl KmerFilter for MinGenomeCount {
    fn passes(&self, row: &[u32], _n_genomes: usize) -> bool {
        present_count(row, self.threshold) >= self.count
    }
}

/// At most `count` genomes contain this kmer (count > `threshold`).
pub struct MaxGenomeCount {
    pub count: usize,
    pub threshold: u32,
}

impl KmerFilter for MaxGenomeCount {
    fn passes(&self, row: &[u32], _n_genomes: usize) -> bool {
        present_count(row, self.threshold) <= self.count
    }
}

// ── Total-count filters (count indexes only) ───────────────────────────────────

/// Sum of counts across all genomes >= `total`.
pub struct MinTotalCount {
    pub total: u32,
}

impl KmerFilter for MinTotalCount {
    fn passes(&self, row: &[u32], _n_genomes: usize) -> bool {
        row.iter().sum::<u32>() >= self.total
    }
}

/// Sum of counts across all genomes <= `total`.
pub struct MaxTotalCount {
    pub total: u32,
}

impl KmerFilter for MaxTotalCount {
    fn passes(&self, row: &[u32], _n_genomes: usize) -> bool {
        row.iter().sum::<u32>() <= self.total
    }
}
