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 DefaultScoreTally<T> = ScoreTally<T, u64>;
pub struct ScoreTally<T, C = u64>
where
T: Eq + Clone + Hash, C: Copy + PartialOrd + AddAssign + Num + NumCast, {
plurality: PluralityTally<T, C>,
}
impl<T, C> ScoreTally<T, C>
where
T: Eq + Clone + Hash, C: Copy + PartialOrd + AddAssign + Num + NumCast, {
pub fn new(num_winners: u32) -> Self {
ScoreTally {
plurality: PluralityTally::new(num_winners),
}
}
pub fn with_capacity(num_winners: u32, expected_candidates: usize) -> Self {
ScoreTally {
plurality: PluralityTally::with_capacity(num_winners, expected_candidates),
}
}
pub fn add(&mut self, mut selection: Vec<(T, C)>) {
for (vote, score) in selection.drain(0..) {
self.plurality.add_weighted(vote, score);
}
}
pub fn add_ref(&mut self, selection: &[(T, C)]) {
for (vote, score) in selection {
self.plurality.add_weighted_ref(vote, *score);
}
}
pub fn add_weighted(&mut self, mut selection: Vec<(T, C)>, weight: C) {
for (vote, score) in selection.drain(0..) {
self.plurality.add_weighted(vote, weight * score);
}
}
pub fn add_weighted_ref(&mut self, selection: &[(T, C)], weight: C) {
for (vote, score) in selection {
self.plurality.add_weighted_ref(vote, weight * *score);
}
}
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 score_basic() {
let mut tally = ScoreTally::new(1);
tally.add(vec![("Alice", 10), ("Bob", 4)]);
tally.add_ref(&vec![("Alice", 2), ("Bob", 2)]);
tally.add_weighted_ref(&vec![("Alice", 1), ("Bob", 1)], 5);
let candidates = tally.candidates();
assert_eq!(candidates.len(), 2);
let totals = tally.totals();
assert_eq!(totals, vec![("Alice", 17), ("Bob", 11)]);
let ranked = tally.ranked();
assert_eq!(ranked, vec![("Alice", 0), ("Bob", 1)]);
let winners = tally.winners();
assert_eq!(winners.is_empty(), false);
assert_eq!(winners.check_overflow(), false);
assert_eq!(winners.overflow(), Option::None);
assert_eq!(winners.all(), vec!["Alice"]);
}
#[test]
fn score_wikipedia() {
let mut tally = ScoreTally::with_capacity(1, 4);
tally.add_weighted(vec![("Memphis", 10), ("Nashville", 4), ("Chattanooga", 2), ("Knoxville", 0)], 42);
tally.add_weighted(vec![("Memphis", 0), ("Nashville", 10), ("Chattanooga", 4), ("Knoxville", 2)], 26);
tally.add_weighted(vec![("Memphis", 0), ("Nashville", 6), ("Chattanooga", 10), ("Knoxville", 6)], 15);
tally.add_weighted(vec![("Memphis", 0), ("Nashville", 5), ("Chattanooga", 7), ("Knoxville", 10)], 17);
let candidates = tally.candidates();
assert_eq!(candidates.len(), 4);
let totals = tally.totals();
assert_eq!(
totals,
vec![("Nashville", 603), ("Chattanooga", 457), ("Memphis", 420), ("Knoxville", 312)]
);
let ranked = tally.ranked();
assert_eq!(ranked, vec![("Nashville", 0), ("Chattanooga", 1), ("Memphis", 2), ("Knoxville", 3)]);
let winners = tally.winners();
assert_eq!(winners.is_empty(), false);
assert_eq!(winners.check_overflow(), false);
assert_eq!(winners.overflow(), Option::None);
assert_eq!(winners.all(), vec!["Nashville"]);
}
}