use crate::common::ElectionError;
use crate::outcome::GenericOutcome;
use crate::prng::Prng;
use crate::tally::{Tally, VoteTree};
use std::collections::HashSet;
pub struct Irv<'a> {
tally: &'a VoteTree,
withdrawn: HashSet<u32>,
seed: u32,
}
impl<'a> Irv<'a> {
pub fn new(tally: &'a VoteTree) -> Self {
let mut withdrawn: HashSet<u32> = HashSet::new();
tally.map_withdrawn(|c| {
withdrawn.insert(c);
});
Self {
tally,
withdrawn,
seed: 21,
}
}
pub fn withdraw<I>(&mut self, withdraw: I) -> &mut Self
where
I: IntoIterator<Item = u32>,
{
self.withdrawn.clear();
for e in withdraw.into_iter() {
self.withdrawn.insert(e);
}
self
}
pub fn seed(&mut self, seed: u32) -> &mut Self {
self.seed = seed;
self
}
pub fn run(&self) -> Result<GenericOutcome<'a>, ElectionError> {
let candidates = self.tally.get_candidates();
let mut eliminated = self.withdrawn.clone();
let mut rnd = Prng::new(self.seed);
let winner = loop {
let (excess, mut score) = self.tally.assign_votes(&eliminated);
for i in 0..candidates {
if !eliminated.contains(&i) {
score.entry(i).or_insert(0);
}
}
let quota = (self.tally.ballot_count() - excess) / 2;
let max_score = score.values().max();
let mut winners: Vec<u32> = score
.iter()
.filter(|(_, &s)| max_score.map_or(false, |&ms| s == ms && s > quota))
.map(|(&c, _)| c)
.collect();
if !winners.is_empty() {
winners.sort_unstable();
let final_winner = rnd.only_or_random(&winners).unwrap();
break final_winner;
} else {
let min_score = score.values().min();
let mut worse: Vec<u32> = score
.iter()
.filter(|(_, &s)| min_score.map_or(false, |&ms| s == ms))
.map(|(&c, _)| c)
.collect();
if worse.is_empty() {
return Err(ElectionError::NotEnoughCandidates);
}
worse.sort_unstable();
let looser = rnd.only_or_random(&worse).unwrap();
eliminated.insert(looser);
}
};
Ok(GenericOutcome::new(
Some(winner),
self.withdrawn.iter().cloned(),
candidates,
rnd.sealed(),
self.tally.get_meta(),
))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Outcome;
#[test]
fn basic() {
let x = [
(1, vec![0, 1]),
(2, vec![0, 2]),
(3, vec![1, 0]),
(2, vec![1, 2]),
(2, vec![2, 0]),
(2, vec![2, 1]),
(1, vec![4, 3, 2]),
]
.iter()
.collect();
let ans = Irv::new(&x).run().unwrap();
assert_eq!(ans.winner().unwrap(), 2);
}
#[test]
fn empty() {
let x = [].iter().collect();
match Irv::new(&x).run().unwrap_err() {
ElectionError::NotEnoughCandidates => (),
_ => unreachable!(),
}
}
}