1use super::Numeric;
2use num_traits::Num;
3
4pub enum Quota<C> {
6 Droop,
15
16 Hagenbach,
28
29 Hare,
40
41 Imperiali,
51
52 Static(C),
56}
57
58impl<C: Numeric + Num + Clone> Quota<C> {
59 pub fn threshold(&self, total_votes: C, num_winners: C) -> C {
68 match self {
69 Quota::Droop => (total_votes / (num_winners + C::one())).floor() + C::one(),
70 Quota::Hagenbach => {
71 if !C::fraction() {
72 panic!("tallystick::Quota::Hagenbach cannot be used with an integer count type. Please use a float or a rational.")
73 }
74 total_votes / (num_winners + C::one())
75 }
76 Quota::Hare => total_votes / num_winners,
77 Quota::Imperiali => total_votes / (num_winners + C::one() + C::one()),
78 Quota::Static(x) => x.clone(),
79 }
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 #[allow(clippy::float_cmp)]
89 fn quota_test() {
90 assert!(Quota::Static(50).threshold(100, 1) == 50);
93 assert!(Quota::Static(50).threshold(101, 1) == 50);
94
95 assert!(Quota::Droop.threshold(100, 1) == 51);
98 assert!(Quota::Droop.threshold(101, 1) == 51);
99 assert!(Quota::Droop.threshold(102, 1) == 52);
100
101 assert!(Quota::Droop.threshold(100, 2) == 34);
102 assert!(Quota::Droop.threshold(101, 2) == 34);
103 assert!(Quota::Droop.threshold(102, 2) == 35);
104
105 assert!(Quota::Hare.threshold(100, 1) == 100);
106 assert!(Quota::Hare.threshold(101, 1) == 101);
107 assert!(Quota::Hare.threshold(102, 1) == 102);
108
109 assert!(Quota::Hare.threshold(100, 2) == 50);
110 assert!(Quota::Hare.threshold(101, 2) == 50); assert!(Quota::Hare.threshold(102, 2) == 51);
112
113 assert!(Quota::Imperiali.threshold(100, 1) == 33);
114 assert!(Quota::Imperiali.threshold(101, 1) == 33);
115 assert!(Quota::Imperiali.threshold(102, 1) == 34);
116
117 assert!(Quota::Imperiali.threshold(100, 2) == 25);
118 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);
127 assert!(Quota::Droop.threshold(101.0, 1.0) == 51.0);
128 assert!(Quota::Droop.threshold(102.0, 1.0) == 52.0);
129
130 assert!(Quota::Droop.threshold(100.0, 2.0) == 34.0);
131 assert!(Quota::Droop.threshold(101.0, 2.0) == 34.0);
132 assert!(Quota::Droop.threshold(102.0, 2.0) == 35.0);
133
134 assert!(Quota::Hagenbach.threshold(100.0, 1.0) == 50.0);
135 assert!(Quota::Hagenbach.threshold(101.0, 1.0) == 50.5);
136 assert!(Quota::Hagenbach.threshold(102.0, 1.0) == 51.0);
137
138 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);
141
142 assert!(Quota::Hare.threshold(100.0, 1.0) == 100.0);
143 assert!(Quota::Hare.threshold(101.0, 1.0) == 101.0);
144 assert!(Quota::Hare.threshold(102.0, 1.0) == 102.0);
145
146 assert!(Quota::Hare.threshold(100.0, 2.0) == 50.0);
147 assert!(Quota::Hare.threshold(101.0, 2.0) == 50.5);
148 assert!(Quota::Hare.threshold(102.0, 2.0) == 51.0);
149
150 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);
153
154 assert!(Quota::Imperiali.threshold(100.0, 2.0) == 25.00);
155 assert!(Quota::Imperiali.threshold(101.0, 2.0) == 25.25);
156 assert!(Quota::Imperiali.threshold(102.0, 2.0) == 25.50);
157 }
158
159 #[test]
160 #[should_panic]
161 fn quota_panic_test() {
162 assert!(Quota::Hagenbach.threshold(100, 1) == 50);
164 }
165}