1#![forbid(unsafe_code, unstable_features, missing_docs)]
2#![deny(
3 warnings,
4 unused_extern_crates,
5 missing_copy_implementations,
6 missing_debug_implementations
7)]
8
9use rand::seq::SliceRandom;
14use std::error;
15use std::fmt;
16
17#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
19pub enum DistributionError {
20 Tied,
22
23 InvalidSeatCount,
25
26 NegativeVotes,
28
29 NoVotes,
31}
32
33impl fmt::Display for DistributionError {
34 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
35 match self {
36 &DistributionError::Tied => write!(
37 f,
38 "Tie detected, could only be resolved by randomly awarding a seat to one party."
39 ),
40 &DistributionError::InvalidSeatCount => {
41 write!(f, "Invalid seat count, must be an integer larger than 0.")
42 }
43 &DistributionError::NegativeVotes => write!(
44 f,
45 "Invalid votes, all parties must have at least zero votes."
46 ),
47 &DistributionError::NoVotes => {
48 write!(f, "Invalid votes, one party must have at least one vote.")
49 }
50 }
51 }
52}
53
54impl error::Error for DistributionError {}
55
56#[derive(Clone)]
57struct PartyQuotient {
58 party: usize,
59 quotient: f64,
60}
61
62pub fn distribute(
106 votes: &[f64],
107 seat_count: &usize,
108 draw_on_tie: &bool,
109) -> Result<Vec<usize>, DistributionError> {
110 if seat_count < &1 {
115 return Err(DistributionError::InvalidSeatCount);
116 }
117 let has_negative_votes = votes.iter().any(|v| v < &0.0);
118 if has_negative_votes {
119 return Err(DistributionError::NegativeVotes);
120 }
121 let total_votes: f64 = votes.iter().sum();
122 if total_votes == 0.0 {
123 return Err(DistributionError::NoVotes);
124 }
125
126 let mut party_quotients: Vec<PartyQuotient> = votes
127 .iter()
128 .enumerate()
129 .flat_map(|(i, v)| {
130 let divisors = (1..=(seat_count.clone() as i64)).map(|d| (d as f64) - 0.5);
131 return divisors.map(move |d| PartyQuotient {
132 party: i,
133 quotient: v / d,
134 });
135 })
136 .collect();
137
138 party_quotients.sort_by(|a, b| {
139 b.quotient
140 .partial_cmp(&a.quotient)
141 .unwrap_or(std::cmp::Ordering::Equal)
142 });
143
144 let last_winning_quotient = party_quotients
145 .get(seat_count.clone() - 1)
146 .map(|pq| pq.quotient)
147 .unwrap_or(0.0);
148 let mut winners: Vec<PartyQuotient> = party_quotients
149 .iter()
150 .filter(|pq| pq.quotient > last_winning_quotient)
151 .cloned()
152 .collect();
153 let mut possible_winners: Vec<PartyQuotient> = party_quotients
154 .iter()
155 .filter(|pq| pq.quotient == last_winning_quotient)
156 .cloned()
157 .collect();
158
159 let seats_too_many =
162 (winners.len() as i64) + (possible_winners.len() as i64) - (seat_count.clone() as i64);
163
164 if seats_too_many > 0 {
165 if !draw_on_tie {
166 return Err(DistributionError::Tied);
167 }
168 let number_of_draws = (possible_winners.len() as i64) - seats_too_many;
169 let mut drawn_winners: Vec<PartyQuotient> = (&possible_winners)
170 .choose_multiple(&mut rand::thread_rng(), number_of_draws.max(0) as usize)
171 .cloned()
172 .collect();
173 winners.append(&mut drawn_winners);
174 } else {
175 winners.append(&mut possible_winners);
176 }
177
178 let mut distribution: Vec<usize> = vec![0; votes.len()];
179 for pq in winners.iter() {
180 distribution[pq.party] += 1 }
182
183 return Ok(distribution);
184}
185
186#[cfg(test)]
187mod tests {
188 use super::distribute;
189 use super::DistributionError;
190
191 #[test]
192 fn german_bundestag_2013() {
193 let votes = [41.5, 25.7, 8.6, 8.4];
194 let seats = 631;
195
196 let distribution = distribute(&votes, &seats, &false);
197 let parliament = vec![311, 193, 64, 63];
198 assert_eq!(distribution, Ok(parliament));
199 }
200
201 #[test]
202 fn rhineland_palatinate_201x() {
203 let votes = [362.0, 318.0, 126.0, 62.0, 53.0];
204 let seats = 101;
205
206 let distribution = distribute(&votes, &seats, &false);
207 let parliament = vec![39, 35, 14, 7, 6];
208 assert_eq!(distribution, Ok(parliament));
209 }
210
211 #[test]
212 fn schleswig_holstein_201x() {
213 let votes = [308.0, 304.0, 132.0, 82.0, 82.0, 46.0];
214 let seats = 69;
215
216 let distribution = distribute(&votes, &seats, &false);
217 let parliament = vec![22, 22, 10, 6, 6, 3];
218 assert_eq!(distribution, Ok(parliament));
219 }
220
221 #[test]
222 fn equal_but_no_draw_required() {
223 let votes = [415.0, 257.0, 85.0, 85.0];
224 let seats = 631;
225
226 let distribution = distribute(&votes, &seats, &false);
227 let parliament = vec![311, 192, 64, 64];
228 assert_eq!(distribution, Ok(parliament));
229 }
230
231 #[test]
232 fn equal_and_draw_required() {
233 let votes = [3.0, 3.0, 1.0];
234 let seats = 8;
235
236 let distribution_without_draw = distribute(&votes, &seats, &false);
237 assert_eq!(distribution_without_draw, Err(DistributionError::Tied));
238
239 let distribution_with_draw = distribute(&votes, &seats, &true);
240 let parliament_draw_a: Vec<usize> = vec![4, 3, 1];
241 let parliament_draw_b: Vec<usize> = vec![3, 4, 1];
242 assert_eq!(
243 [Ok(parliament_draw_a), Ok(parliament_draw_b)]
244 .iter()
245 .any(|x| x == &distribution_with_draw),
246 true
247 );
248 }
249
250 #[test]
251 fn small_parliament_no_draw_required() {
252 let votes = [2.0, 2.0];
253 let seats = 2;
254
255 let distribution_without_draw = distribute(&votes, &seats, &false);
256 assert_eq!(distribution_without_draw, Ok(vec![1, 1]));
257
258 let distribution_with_draw = distribute(&votes, &seats, &true);
259 assert_eq!(distribution_with_draw, Ok(vec![1, 1]));
260 }
261
262 #[test]
263 fn only_one_party() {
264 let votes = [3.0];
265 let seats = 10;
266
267 let distribution = distribute(&votes, &seats, &false);
268 let parliament = vec![10];
269 assert_eq!(distribution, Ok(parliament));
270 }
271
272 #[test]
273 fn invalid_seat_count() {
274 let votes = [3.0];
275 let seats = 0;
276
277 let distribution = distribute(&votes, &seats, &false);
278 assert_eq!(distribution, Err(DistributionError::InvalidSeatCount));
279 }
280
281 #[test]
282 fn no_votes() {
283 let seats = 50;
284
285 let distribution_empty_votes = distribute(&[], &seats, &false);
286 assert_eq!(distribution_empty_votes, Err(DistributionError::NoVotes));
287
288 let distribution_zero_votes = distribute(&[0.0, 0.0], &seats, &false);
289 assert_eq!(distribution_zero_votes, Err(DistributionError::NoVotes));
290
291 let distribution_valid_votes = distribute(&[0.0, 3.0], &seats, &false);
292 assert_eq!(distribution_valid_votes, Ok(vec![0, seats]));
293 }
294
295 #[test]
296 fn negative_votes() {
297 let seats = 50;
298
299 let distribution_negative_votes = distribute(&[-3.0], &seats, &false);
300 assert_eq!(
301 distribution_negative_votes,
302 Err(DistributionError::NegativeVotes)
303 );
304
305 let distribution_negative_votes_sum_zero = distribute(&[4.0, -4.0], &seats, &false);
306 assert_eq!(
307 distribution_negative_votes_sum_zero,
308 Err(DistributionError::NegativeVotes)
309 );
310 }
311}