use crate::common::{ElectionError, SetType};
use crate::methods::smith_schwartz::smith_schwartz_condensation;
use crate::outcome::{GenericOutcome, Outcome};
use crate::tally::{Tally, VoteMatrix, VoteTree};
use crate::Irv;
use std::collections::{HashMap, HashSet};
pub struct Alternative<'a> {
tally: &'a VoteTree,
set_type: SetType,
seed: u32,
}
impl<'a> Alternative<'a> {
pub fn new(tally: &'a VoteTree) -> Self {
Self {
tally,
seed: 21,
set_type: SetType::Smith,
}
}
pub fn seed(&mut self, seed: u32) -> &mut Self {
self.seed = seed;
self
}
pub fn set_type(&mut self, set_type: SetType) -> &mut Self {
self.set_type = set_type;
self
}
pub fn run(&self) -> Result<GenericOutcome<'a>, ElectionError> {
let candidates = self.tally.get_candidates();
let mut vm: HashMap<(u32, u32), u64> = HashMap::new();
self.tally.stream_ballots(&mut |repeat: u64, vote: &[u32]| {
let mut ballot: HashMap<u32, usize> = HashMap::new();
for (pref, &c) in vote.iter().enumerate() {
ballot.insert(c, pref);
}
for c in 0..candidates {
ballot.entry(c).or_insert(usize::MAX);
}
for (&runner, &r_pref) in &ballot {
for (&opponent, &o_pref) in &ballot {
if r_pref < o_pref {
*vm.entry((runner, opponent)).or_insert(0) += repeat;
}
}
}
});
let matrix: VoteMatrix = vm.iter().map(|(&(r, o), &s)| (r, o, s)).collect();
let members = smith_schwartz_condensation(&matrix, self.set_type).get_max();
if members.is_empty() {
Err(ElectionError::NotEnoughCandidates)
} else if members.len() == 1 {
Ok(GenericOutcome::new(
Some(members[0]),
None,
candidates,
true,
self.tally.get_meta(),
))
} else {
let mut not_members: HashSet<u32> = (0..candidates).collect();
for retain in members {
not_members.remove(&retain);
}
let irv_out = Irv::new(self.tally)
.withdraw(not_members.iter().cloned())
.seed(self.seed)
.run()?;
Ok(GenericOutcome::new(
Some(irv_out.winner().unwrap()),
None,
candidates,
irv_out.deterministic(),
self.tally.get_meta(),
))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Outcome;
#[test]
fn alt_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();
for st in [SetType::Smith, SetType::Schwartz].iter() {
let ans = Alternative::new(&x).set_type(*st).run().unwrap();
assert_eq!(ans.winner().unwrap(), 2);
}
}
#[test]
fn alt_deeper() {
let x = [(1, vec![0, 1]), (2, vec![1, 2]), (3, vec![2, 0])]
.iter()
.collect();
let ans = Alternative::new(&x).run().unwrap();
assert_eq!(ans.winner().unwrap(), 2);
}
#[test]
fn alt_empty() {
let x = [].iter().collect();
match Alternative::new(&x).run().unwrap_err() {
ElectionError::NotEnoughCandidates => (),
_ => unreachable!(),
}
}
}