use super::Numeric;
use num_traits::Num;
pub enum Quota<C> {
Droop,
Hagenbach,
Hare,
Imperiali,
Static(C),
}
impl<C: Numeric + Num + Clone> Quota<C> {
pub fn threshold(&self, total_votes: C, num_winners: C) -> C {
match self {
Quota::Droop => (total_votes / (num_winners + C::one())).floor() + C::one(),
Quota::Hagenbach => {
if !C::fraction() {
panic!("tallystick::Quota::Hagenbach cannot be used with an integer count type. Please use a float or a rational.")
}
total_votes / (num_winners + C::one())
}
Quota::Hare => total_votes / num_winners,
Quota::Imperiali => total_votes / (num_winners + C::one() + C::one()),
Quota::Static(x) => x.clone(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[allow(clippy::float_cmp)]
fn quota_test() {
assert!(Quota::Static(50).threshold(100, 1) == 50);
assert!(Quota::Static(50).threshold(101, 1) == 50);
assert!(Quota::Droop.threshold(100, 1) == 51);
assert!(Quota::Droop.threshold(101, 1) == 51);
assert!(Quota::Droop.threshold(102, 1) == 52);
assert!(Quota::Droop.threshold(100, 2) == 34);
assert!(Quota::Droop.threshold(101, 2) == 34);
assert!(Quota::Droop.threshold(102, 2) == 35);
assert!(Quota::Hare.threshold(100, 1) == 100);
assert!(Quota::Hare.threshold(101, 1) == 101);
assert!(Quota::Hare.threshold(102, 1) == 102);
assert!(Quota::Hare.threshold(100, 2) == 50);
assert!(Quota::Hare.threshold(101, 2) == 50); assert!(Quota::Hare.threshold(102, 2) == 51);
assert!(Quota::Imperiali.threshold(100, 1) == 33);
assert!(Quota::Imperiali.threshold(101, 1) == 33);
assert!(Quota::Imperiali.threshold(102, 1) == 34);
assert!(Quota::Imperiali.threshold(100, 2) == 25);
assert!(Quota::Imperiali.threshold(101, 2) == 25); assert!(Quota::Imperiali.threshold(102, 2) == 25);
let thirty_three_point_threes = 33.0 + (1.0 / 3.0); let thirty_three_point_sixes = 33.0 + (2.0 / 3.0);
assert!(Quota::Droop.threshold(100.0, 1.0) == 51.0);
assert!(Quota::Droop.threshold(101.0, 1.0) == 51.0);
assert!(Quota::Droop.threshold(102.0, 1.0) == 52.0);
assert!(Quota::Droop.threshold(100.0, 2.0) == 34.0);
assert!(Quota::Droop.threshold(101.0, 2.0) == 34.0);
assert!(Quota::Droop.threshold(102.0, 2.0) == 35.0);
assert!(Quota::Hagenbach.threshold(100.0, 1.0) == 50.0);
assert!(Quota::Hagenbach.threshold(101.0, 1.0) == 50.5);
assert!(Quota::Hagenbach.threshold(102.0, 1.0) == 51.0);
assert!(Quota::Hagenbach.threshold(100.0, 2.0) == thirty_three_point_threes); assert!(Quota::Hagenbach.threshold(101.0, 2.0) == thirty_three_point_sixes); assert!(Quota::Hagenbach.threshold(102.0, 2.0) == 34.0);
assert!(Quota::Hare.threshold(100.0, 1.0) == 100.0);
assert!(Quota::Hare.threshold(101.0, 1.0) == 101.0);
assert!(Quota::Hare.threshold(102.0, 1.0) == 102.0);
assert!(Quota::Hare.threshold(100.0, 2.0) == 50.0);
assert!(Quota::Hare.threshold(101.0, 2.0) == 50.5);
assert!(Quota::Hare.threshold(102.0, 2.0) == 51.0);
assert!(Quota::Imperiali.threshold(100.0, 1.0) == thirty_three_point_threes); assert!(Quota::Imperiali.threshold(101.0, 1.0) == thirty_three_point_sixes); assert!(Quota::Imperiali.threshold(102.0, 1.0) == 34.0);
assert!(Quota::Imperiali.threshold(100.0, 2.0) == 25.00);
assert!(Quota::Imperiali.threshold(101.0, 2.0) == 25.25);
assert!(Quota::Imperiali.threshold(102.0, 2.0) == 25.50);
}
#[test]
#[should_panic]
fn quota_panic_test() {
assert!(Quota::Hagenbach.threshold(100, 1) == 50);
}
}