use std::collections::HashMap;
use crate::core::{Candidate, Cardinal, Id, Method, Profile, SingleWinner};
#[derive(Debug, Clone, serde::Serialize)]
pub struct Star;
impl Method for Star {
type Ballot = Cardinal;
type Winner = SingleWinner;
fn outcome(&self, candidates: &[Candidate], profile: Profile<Self::Ballot>) -> Self::Winner {
let mut tally: HashMap<Id, usize> = HashMap::with_capacity(profile.len());
for ballot in profile.iter() {
for (id, score) in &ballot.0 {
*tally.entry(*id).or_insert(0) += score;
}
}
let mut sorted_ids: Vec<_> = tally.iter().collect();
sorted_ids.sort_by_key(|&(_, score)| score);
sorted_ids.reverse();
if sorted_ids.len() < 2 {
return SingleWinner::None;
}
let (c1, _) = sorted_ids[0];
let (c2, _) = sorted_ids[1];
let (c1_tally, c2_tally) =
profile
.iter()
.fold((0, 0), |(c1_tally, c2_tally), preference| {
let score1 = preference.0.get(c1).unwrap();
let score2 = preference.0.get(c2).unwrap();
(
c1_tally + usize::from(score1 > score2),
c2_tally + usize::from(score2 > score1),
)
});
let winners = match (c1_tally, c2_tally) {
(c1_tally, c2_tally) if c1_tally > c2_tally => vec![*c1],
(c1_tally, c2_tally) if c2_tally > c1_tally => vec![*c2],
(c1_tally, c2_tally) if c1_tally == c2_tally => vec![*c1, *c2],
_ => vec![],
};
match winners.len() {
0 => SingleWinner::none(),
1 => SingleWinner::win(candidates, winners[0]),
_ => SingleWinner::tie(candidates, &winners),
}
}
}