use self::ElectionError::*;
use self::Quota::*;
use crate::common::{ElectionError, Quota, Transfer};
use crate::outcome::GenericOutcome;
use crate::prng::Prng;
use crate::tally::{Tally, VoteTree};
use std::collections::{HashMap, HashSet};
pub struct Meek<'a> {
tally: &'a VoteTree,
seats: Option<u32>,
withdrawn: Vec<u32>,
transfer: Transfer,
quota: Quota,
digits: u32,
seed: u32,
}
impl<'a> Meek<'a> {
pub fn new(tally: &'a VoteTree) -> Self {
let mut withdrawn: Vec<u32> = Vec::new();
tally.map_withdrawn(|c| {
withdrawn.push(c);
});
Self {
tally,
quota: HagenbachBischoff,
transfer: Transfer::Meek,
withdrawn,
seats: Some(tally.get_seats()),
digits: 6,
seed: 21,
}
}
pub fn seed(&mut self, seed: u32) -> &mut Self {
self.seed = seed;
self
}
pub fn seats(&mut self, seats: u32) -> &mut Self {
self.seats = Some(seats);
self
}
pub fn digits(&mut self, digits: u32) -> &mut Self {
self.digits = digits;
self
}
pub fn quota(&mut self, quota: Quota) -> &mut Self {
self.quota = quota;
self
}
pub fn transfer(&mut self, transfer: Transfer) -> &mut Self {
self.transfer = transfer;
self
}
pub fn withdraw<I>(&mut self, withdraw: I) -> &mut Self
where
I: IntoIterator<Item = u32>,
{
self.withdrawn.clear();
for e in withdraw.into_iter() {
self.withdrawn.push(e);
}
self
}
pub fn run(&self) -> Result<GenericOutcome<'a>, ElectionError> {
let seats = self.seats.unwrap_or(1);
let candidates = self.tally.get_candidates();
if self.digits > 17 {
return Err(FixedPointDigitsTooMany);
}
let base = 10_u64.pow(self.digits);
if self.tally.ballot_count().checked_mul(base).is_none() {
return Err(FixedPointDigitsTooMany);
}
let mut elected: Vec<u32> = Vec::new();
let mut resolved: HashSet<u32> = HashSet::new();
let mut weights: HashMap<u32, u64> = (0..candidates).map(|c| (c, base)).collect();
for c in &self.withdrawn {
weights.remove(c);
resolved.insert(*c);
}
if weights.len() < seats as usize {
return Err(NotEnoughCandidates);
}
let mut rnd = Prng::new(self.seed);
loop {
let (_excess, mut score, quota) = loop {
let (excess, score) = self.tally.transfer_votes_fp(&weights, base, Transfer::Meek);
let useful_votes = score.values().sum();
let quota = match self.quota {
Hare => (useful_votes, u64::from(seats)),
HareInt => (((useful_votes / u64::from(seats)) / base) * base, 1),
Droop => (
((useful_votes / (u64::from(seats) + 1)) / base + 1) * base,
1,
),
HagenbachBischoff => (useful_votes, u64::from(seats) + 1),
};
let mut stable = true;
for ec in &elected {
if quota.0 > quota.1 * score[ec] {
continue;
}
let new_weight = (weights[ec] * quota.0) / (quota.1 * score[ec]);
stable = stable
&& weights
.insert(*ec, new_weight)
.map_or(false, |old_weight| old_weight == new_weight)
}
if stable {
break (excess, score, quota);
}
};
score.retain(|c, _| !resolved.contains(c));
let mut electable: Vec<u32> = score
.iter()
.filter_map(|(&c, &s)| {
if s * quota.1 >= quota.0 {
Some(c)
} else {
None
}
})
.collect();
if !electable.is_empty() {
while electable.len() + elected.len() > seats as usize {
let to_remove = rnd.random_usindex(electable.len());
electable.swap_remove(to_remove);
}
for &e in &electable {
elected.push(e);
resolved.insert(e);
}
if elected.len() == seats as usize {
return Ok(GenericOutcome::new(
elected.iter().cloned(),
self.withdrawn.iter().cloned(),
candidates,
rnd.sealed(),
self.tally.get_meta(),
));
}
} else {
let min_score = score.values().min();
let mut worse: Vec<u32> = score
.iter()
.filter_map(|(c, &s)| {
min_score.and_then(|&ms| if s == ms { Some(*c) } else { None })
})
.collect();
if worse.is_empty() {
return Err(NotEnoughCandidates);
} else {
worse.sort_unstable(); let looser = rnd.only_or_random(&worse).unwrap();
weights.remove(&looser);
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Outcome;
#[test]
fn meek_withdrawn() {
let x = [
(3, vec![0, 2, 3]),
(4, vec![0, 2, 1]),
(2, vec![3, 0, 2]),
(1, vec![1]),
(2, vec![1, 3, 2, 0]),
(1, vec![2, 3, 1]),
]
.iter()
.collect();
assert_eq!(
Meek::new(&x)
.seats(2)
.withdraw(vec![1])
.quota(Quota::Hare)
.run()
.unwrap()
.elected()
.collect::<Vec<u32>>(),
vec![0, 3]
);
}
#[test]
fn meek_alts() {
let x = [
(3, vec![0, 2, 3]),
(4, vec![0, 2, 1]),
(2, vec![3, 0, 2]),
(1, vec![1]),
(2, vec![1, 3, 2, 0]),
(1, vec![2, 3, 1]),
]
.iter()
.collect();
let mut stub = Meek::new(&x);
stub.seats(2).digits(6).seed(97);
assert_eq!(
stub.run().unwrap().elected().collect::<Vec<u32>>(),
vec![0, 2]
);
assert_eq!(
stub.quota(Quota::Hare)
.run()
.unwrap()
.elected()
.collect::<Vec<u32>>(),
vec![0, 1]
);
assert_eq!(
stub.transfer(Transfer::Warren)
.run()
.unwrap()
.elected()
.collect::<Vec<u32>>(),
vec![0, 1]
);
}
#[test]
fn meek_alts_more() {
let x = [
(3, vec![0, 2, 3]),
(4, vec![0, 2, 1]),
(2, vec![3, 0, 2]),
(1, vec![1]),
(2, vec![1, 3, 2, 0]),
(1, vec![2, 3, 1]),
]
.iter()
.collect();
let mut stub = Meek::new(&x);
stub.seats(2).digits(6).seed(97);
use Quota::*;
for quota in vec![Hare, HareInt, Droop, HagenbachBischoff] {
for transfer in vec![Transfer::Warren, Transfer::Meek] {
assert!(stub.quota(quota).transfer(transfer).run().is_ok());
}
}
}
#[test]
fn meek_empty() {
let x = [].iter().collect();
match Meek::new(&x).run().unwrap_err() {
ElectionError::NotEnoughCandidates => (),
_ => unreachable!(),
};
}
}