|
|
|
@ -80,13 +80,18 @@ impl<N: Number> Election<N> { |
|
|
|
|
|
|
|
|
|
/// Convert ballots with equal rankings to strict-preference "minivoters"
|
|
|
|
|
pub fn realise_equal_rankings(&mut self) { |
|
|
|
|
if !self.ballots.iter().any(|b| b.has_equal_rankings()) { |
|
|
|
|
// No equal rankings
|
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Record total_votes so loss by fraction can be calculated
|
|
|
|
|
self.total_votes = Some(self.ballots.iter().fold(N::new(), |acc, b| acc + &b.orig_value)); |
|
|
|
|
// See crate::stv::gregory::distribute_first_preferences, etc.
|
|
|
|
|
self.total_votes = Some(self.ballots.iter().fold(N::new(), |mut acc, b| { acc += &b.orig_value; acc })); |
|
|
|
|
|
|
|
|
|
let mut realised_ballots = Vec::new(); |
|
|
|
|
for ballot in self.ballots.iter() { |
|
|
|
|
let mut b = ballot.realise_equal_rankings(); |
|
|
|
|
realised_ballots.append(&mut b); |
|
|
|
|
let mut realised_ballots = Vec::with_capacity(self.ballots.len()); |
|
|
|
|
for ballot in self.ballots.drain(..) { |
|
|
|
|
ballot.realise_equal_rankings_into(&mut realised_ballots); |
|
|
|
|
} |
|
|
|
|
self.ballots = realised_ballots; |
|
|
|
|
} |
|
|
|
@ -509,8 +514,18 @@ pub struct Ballot<N> { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl<N: Number> Ballot<N> { |
|
|
|
|
/// Check if this ballot has any equal rankings
|
|
|
|
|
pub fn has_equal_rankings(&self) -> bool { |
|
|
|
|
return self.preferences.iter().any(|p| p.len() > 1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Convert ballot with equal rankings to strict-preference "minivoters"
|
|
|
|
|
pub fn realise_equal_rankings(&self) -> Vec<Ballot<N>> { |
|
|
|
|
pub fn realise_equal_rankings_into(self, dest: &mut Vec<Ballot<N>>) { |
|
|
|
|
if !self.has_equal_rankings() { |
|
|
|
|
dest.push(self); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Preferences for each minivoter
|
|
|
|
|
let mut minivoters = vec![Vec::new()]; |
|
|
|
|
|
|
|
|
@ -541,10 +556,13 @@ impl<N: Number> Ballot<N> { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let weight_each = self.orig_value.clone() / N::from(minivoters.len()); |
|
|
|
|
let ballots = minivoters.into_iter() |
|
|
|
|
.map(|p| Ballot { orig_value: weight_each.clone(), preferences: p }) |
|
|
|
|
.collect(); |
|
|
|
|
return ballots; |
|
|
|
|
|
|
|
|
|
for minivoter in minivoters { |
|
|
|
|
dest.push(Ballot { |
|
|
|
|
orig_value: weight_each.clone(), |
|
|
|
|
preferences: minivoter |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|