use crate::common::BordaKind;
use crate::outcome::GenericOutcome;
use crate::prng::Prng;
use crate::tally::Tally;
use crate::ElectionError;
use crate::VoteTree;
use std::collections::HashSet;
use BordaKind::*;
pub struct Nanson<'a> {
tally: &'a VoteTree,
borda_kind: BordaKind,
withdraw: HashSet<u32>,
max_rank: Option<u32>,
seed: u32,
}
impl<'a> Nanson<'a> {
pub fn new(tally: &'a VoteTree) -> Self {
let mut withdraw: HashSet<u32> = HashSet::new();
tally.map_withdrawn(|c| {
withdraw.insert(c);
});
Self {
tally,
borda_kind: Borda1,
withdraw,
max_rank: None,
seed: 21,
}
}
pub fn seed(&mut self, seed: u32) -> &mut Self {
self.seed = seed;
self
}
pub fn kind(&mut self, kind: BordaKind) -> &mut Self {
self.borda_kind = kind;
self
}
pub fn max_rank(&mut self, max_rank: u32) -> &mut Self {
self.max_rank = Some(max_rank);
self
}
pub fn withdraw<I>(&mut self, withdraw: I) -> &mut Self
where
I: IntoIterator<Item = u32>,
{
self.withdraw.clear();
for e in withdraw.into_iter() {
self.withdraw.insert(e);
}
self
}
pub fn run(&self) -> Result<GenericOutcome<'a>, ElectionError> {
let mut rnd = Prng::new(self.seed);
use crate::methods::borda::Borda;
let mut borda = Borda::new(self.tally);
borda.kind(self.borda_kind);
if let Some(mr) = self.max_rank {
borda.max_rank(mr);
};
let mut eliminated: HashSet<u32> = HashSet::new();
for e in self.withdraw.iter() {
eliminated.insert(*e);
}
let winner = loop {
let scores = borda.withdraw(eliminated.iter().cloned()).scores()?;
if scores.is_empty() {
return Err(ElectionError::NotEnoughCandidates);
}
let average = (scores.values().map(|x| u128::from(*x)).sum::<u128>()
/ (scores.len() as u128)) as u64;
let mut elim_c: usize = 0;
let mut winner: Option<u32> = None;
for (c, s) in &scores {
if *s <= average {
eliminated.insert(*c);
elim_c += 1;
} else if winner.is_none() {
winner = Some(*c);
} else {
winner = None;
}
}
if let Some(x) = winner {
break x;
}
if elim_c == scores.len() {
let mut winners: Vec<u32> = scores.keys().cloned().collect();
winners.sort_unstable();
break rnd.only_or_random(&winners).unwrap();
};
};
Ok(GenericOutcome::new(
Some(winner),
self.withdraw.iter().cloned(),
self.tally.get_candidates(),
rnd.sealed(),
self.tally.get_meta(),
))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::outcome::Outcome;
#[test]
fn demo() {
let tally = Tally::from_blt_file("examples/tennessee.blt").unwrap();
let outcome = Nanson::new(&tally).run().unwrap();
assert!(outcome.deterministic());
assert_eq!(outcome.winner_name().unwrap(), "Nashville");
}
#[test]
fn with_tie() {
let x = [
(10, vec![0, 1]),
(10, vec![1, 0]),
(5, vec![2, 3]),
(5, vec![3, 2]),
]
.iter()
.collect();
let mut stub = Nanson::new(&x);
stub.seed(97);
let ans = stub.kind(Borda0).run().unwrap();
assert!(!ans.deterministic());
assert_eq!(ans.winner().unwrap(), 1);
stub.seed(99);
assert_eq!(stub.kind(Borda0).run().unwrap().winner().unwrap(), 0);
assert_eq!(stub.kind(Nauru).run().unwrap().winner().unwrap(), 0);
assert_eq!(stub.kind(Borda1).run().unwrap().winner().unwrap(), 0);
}
#[test]
fn single_cand() {
let x = [(10, vec![0])].iter().collect();
let ans = Nanson::new(&x).run().unwrap();
assert!(ans.deterministic());
assert_eq!(ans.winner().unwrap(), 0);
}
#[test]
fn empty() {
let x = [].iter().collect();
match Nanson::new(&x).run().unwrap_err() {
ElectionError::NotEnoughCandidates => (),
_ => unreachable!(),
}
}
}