use std::collections::{BTreeSet, HashMap, HashSet};
use crate::database::PeptideDb;
use crate::score::PeptideHit;
#[derive(Debug, Clone)]
pub struct ProteinGroup {
pub leading: String,
pub members: Vec<String>,
pub decoy: bool,
}
pub fn build_groups(
db: &PeptideDb,
observed: &[PeptideHit],
confident: &HashSet<String>,
) -> (Vec<ProteinGroup>, HashMap<u32, (bool, String)>) {
let mut prot_peps: HashMap<(bool, &str), BTreeSet<u32>> = HashMap::new();
for h in observed {
for prot in &db.peptides[h.peptide_idx as usize].proteins {
prot_peps
.entry((h.decoy, prot.as_ref()))
.or_default()
.insert(h.peptide_idx);
}
}
let mut sig_group: HashMap<(bool, Vec<u32>), usize> = HashMap::new();
let mut groups: Vec<ProteinGroup> = Vec::new();
let mut prot_group: HashMap<(bool, &str), usize> = HashMap::new();
for ((decoy, acc), peps) in &prot_peps {
let sig = (*decoy, peps.iter().copied().collect::<Vec<_>>());
let gid = *sig_group.entry(sig).or_insert_with(|| {
groups.push(ProteinGroup {
leading: String::new(),
members: Vec::new(),
decoy: *decoy,
});
groups.len() - 1
});
groups[gid].members.push((*acc).to_string());
prot_group.insert((*decoy, *acc), gid);
}
for g in &mut groups {
g.members.sort();
g.leading = g
.members
.iter()
.find(|m| confident.contains(*m))
.cloned()
.unwrap_or_else(|| g.members[0].clone());
}
let mut pep_gids: HashMap<u32, Vec<usize>> = HashMap::new();
for h in observed {
let mut set: HashSet<usize> = HashSet::new();
for prot in &db.peptides[h.peptide_idx as usize].proteins {
if let Some(&gid) = prot_group.get(&(h.decoy, prot.as_ref())) {
set.insert(gid);
}
}
pep_gids.entry(h.peptide_idx).or_insert_with(|| {
let mut v: Vec<usize> = set.into_iter().collect();
v.sort_unstable();
v
});
}
let mut uniq_count: HashMap<usize, usize> = HashMap::new();
for gids in pep_gids.values() {
if gids.len() == 1 {
*uniq_count.entry(gids[0]).or_default() += 1;
}
}
let mut pep_group: HashMap<u32, (bool, String)> = HashMap::new();
for h in observed {
let gids = &pep_gids[&h.peptide_idx];
if gids.is_empty() {
continue;
}
let gid = *gids
.iter()
.filter(|&&g| h.decoy || confident.contains(&groups[g].leading))
.max_by(|&&a, &&b| {
uniq_count
.get(&a)
.copied()
.unwrap_or(0)
.cmp(&uniq_count.get(&b).copied().unwrap_or(0))
.then(groups[b].leading.cmp(&groups[a].leading))
})
.unwrap_or(&gids[0]);
let leading = &groups[gid].leading;
if h.decoy || confident.contains(leading) {
pep_group.insert(h.peptide_idx, (h.decoy, leading.clone()));
}
}
(groups, pep_group)
}
pub fn members_by_leading(groups: &[ProteinGroup]) -> HashMap<(bool, String), Vec<String>> {
groups
.iter()
.map(|g| ((g.decoy, g.leading.clone()), g.members.clone()))
.collect()
}