use num_traits::cast::NumCast;
use num_traits::Num;
use std::hash::Hash;
use std::ops::AddAssign;
use super::plurality::PluralityTally;
use super::result::RankedWinners;
pub type DefaultApprovalTally<T> = ApprovalTally<T, u64>;
pub struct ApprovalTally<T, C = u64>
where
T: Eq + Clone + Hash, C: Copy + PartialOrd + AddAssign + Num + NumCast, {
plurality: PluralityTally<T, C>,
}
impl<T, C> ApprovalTally<T, C>
where
T: Eq + Clone + Hash, C: Copy + PartialOrd + AddAssign + Num + NumCast, {
pub fn new(num_winners: u32) -> Self {
ApprovalTally {
plurality: PluralityTally::new(num_winners),
}
}
pub fn with_capacity(num_winners: u32, expected_candidates: usize) -> Self {
ApprovalTally {
plurality: PluralityTally::with_capacity(num_winners, expected_candidates),
}
}
pub fn add(&mut self, mut selection: Vec<T>) {
for vote in selection.drain(0..) {
self.plurality.add(vote);
}
}
pub fn add_ref(&mut self, selection: &[T]) {
for vote in selection {
self.plurality.add_ref(vote);
}
}
pub fn add_weighted(&mut self, mut selection: Vec<T>, weight: C) {
for vote in selection.drain(0..) {
self.plurality.add_weighted(vote, weight);
}
}
pub fn add_weighted_ref(&mut self, selection: &[T], weight: C) {
for vote in selection {
self.plurality.add_weighted_ref(vote, weight);
}
}
pub fn candidates(&self) -> Vec<T> {
self.plurality.candidates()
}
pub fn winners(&self) -> RankedWinners<T> {
self.plurality.winners()
}
pub fn totals(&self) -> Vec<(T, C)> {
self.plurality.totals()
}
pub fn ranked(&self) -> Vec<(T, u32)> {
self.plurality.ranked()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn approval_basic() {
let mut tally = DefaultApprovalTally::new(1);
tally.add_ref(&vec!["Alice"]);
tally.add_weighted_ref(&vec!["Alice", "Bob"], 2);
let winners = tally.winners().into_unranked();
assert_eq!(winners, vec!["Alice"]);
}
#[test]
fn approval_lumen() {
let matrix = "The Matrix";
let scream = "Scream";
let titanic = "Titanic";
let mut tally = DefaultApprovalTally::with_capacity(1, 3);
tally.add_weighted(vec![scream, matrix], 3);
tally.add_weighted(vec![titanic, matrix], 2);
tally.add(vec![titanic, scream, matrix]);
tally.add(vec![matrix]);
tally.add(vec![titanic, scream]);
tally.add(vec![titanic]);
tally.add(vec![scream]);
let candidates = tally.candidates();
assert_eq!(candidates.len(), 3);
let totals = tally.totals();
assert_eq!(totals, vec![(matrix, 7), (scream, 6), (titanic, 5)]);
let ranked = tally.ranked();
assert_eq!(ranked, vec![(matrix, 0), (scream, 1), (titanic, 2)]);
let winners = tally.winners();
assert_eq!(winners.contains(&matrix), true);
assert_eq!(winners.contains(&scream), false);
assert_eq!(winners.contains(&titanic), false);
}
}