use std::collections::{BTreeMap, HashSet};
use crate::core::{Candidate, Id, Method, MultiWinner, Ordinal, Profile};
#[derive(Debug, Clone, serde::Serialize)]
pub struct STV {
seats: usize,
}
impl STV {
#[must_use]
pub const fn new(seats: usize) -> Self {
Self { seats }
}
}
impl Method for STV {
type Ballot = Ordinal;
type Winner = MultiWinner;
fn outcome(&self, candidates: &[Candidate], profile: Profile<Self::Ballot>) -> Self::Winner {
let mut ballots: Vec<Self::Ballot> = profile.into_iter().collect();
let mut winners = HashSet::new();
let droop_quota = ballots.len() / (self.seats + 1) + 1;
let mut tally: BTreeMap<Id, usize> = BTreeMap::new();
while winners.len() < self.seats {
tally.clear();
for ballot in &ballots {
if let Some(&first_choice) = ballot.0.first() {
*tally.entry(first_choice).or_insert(0) += 1;
}
}
let elected = tally
.iter()
.filter(|&(_, &votes)| votes >= droop_quota)
.max_by_key(|&(_, &votes)| votes);
if let Some((winner, _)) = elected {
winners.insert(*winner);
ballots.iter_mut().for_each(|b| b.0.retain(|c| c != winner));
} else {
let (loser, _) = tally.iter().min_by_key(|&(_, &votes)| votes).unwrap();
ballots.iter_mut().for_each(|b| b.0.retain(|c| c != loser));
}
}
MultiWinner::Elected(
winners
.into_iter()
.filter_map(|id| candidates.iter().find(|c| c.id() == id).cloned())
.collect(),
)
}
}