use crate::{
interface::{WithSeats, WithVotes},
utils::{clear_results, compute_total_votes},
};
use std::cmp::min;
struct RemainderResult {
pub integer: u16,
pub remainder: f32,
}
pub fn compute_remainder_method<'a, T>(
results: &'a mut Vec<T>,
seats: u16,
quota_fn: impl Fn(u32, u16) -> f32,
) -> Result<(), &'a str>
where
T: WithSeats + WithVotes,
{
clear_results(results);
let total_votes = compute_total_votes(results);
let quota = quota_fn(total_votes, seats);
let mut seats_left = seats;
let mut remainders: Vec<RemainderResult> = results
.iter()
.map(|r| {
let votes = r.get_votes() as f32;
let integer = (votes / quota).floor() as u16;
let remainder = votes / quota - integer as f32;
RemainderResult { integer, remainder }
})
.collect();
remainders.iter().enumerate().for_each(|(idx, r)| {
results[idx].set_seats(r.integer);
seats_left -= min(r.integer, seats_left);
});
for _ in 0..seats_left {
let better_idx = remainders
.iter()
.enumerate()
.max_by(|(_, a), (_, b)| a.remainder.total_cmp(&b.remainder));
match better_idx {
Some((idx, _)) => {
results[idx].increase_seats(1);
remainders[idx].integer += 1;
remainders[idx].remainder -= 1.0;
}
None => return Err("EMPTY_RESULTS"),
}
}
Ok(())
}
pub fn compute_hare<T>(results: &mut Vec<T>, seats: u16) -> Result<(), &str>
where
T: WithSeats + WithVotes,
{
compute_remainder_method(results, seats, |total_votes, seats| {
total_votes as f32 / seats as f32
})
}
#[allow(dead_code)]
pub fn compute_droop<T>(results: &mut Vec<T>, seats: u16) -> Result<(), &str>
where
T: WithSeats + WithVotes,
{
compute_remainder_method(results, seats, |total_votes, seats| {
(total_votes as f32 / (seats + 1) as f32).floor() + 1.0
})
}
#[allow(dead_code)]
pub fn compute_hagenbach_bischoff<T>(results: &mut Vec<T>, seats: u16) -> Result<(), &str>
where
T: WithSeats + WithVotes,
{
compute_remainder_method(results, seats, |total_votes, seats| {
total_votes as f32 / (seats + 1) as f32
})
}
#[allow(dead_code)]
pub fn compute_imperiali_quotient<T>(results: &mut Vec<T>, seats: u16) -> Result<(), &str>
where
T: WithSeats + WithVotes,
{
compute_remainder_method(results, seats, |total_votes, seats| {
(total_votes as f32 / (seats + 2) as f32).floor() + 1.0
})
}
#[cfg(test)]
pub mod tests {
use crate::*;
#[test]
fn test_bug_hagenbach_seats() {
let candidacies = vec![
Candidacy::new(600, 9),
Candidacy::new(31, 4),
Candidacy::new(32, 0),
];
let seats = 1000;
let method = Method::HAGENBASCHBISCHOFF;
let cutoff = 0.1;
let mut ele = election![candidacies, seats, method, cutoff];
ele.compute().expect("Can not compute method");
ele.results.iter().for_each(|c| println!("{:?}", c));
}
}