From ee7ac064c7c6016565db6d48de91196e9ae6b382 Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Thu, 18 Aug 2022 00:15:44 +1000 Subject: [PATCH] Improve performance of realise_equal_rankings --- src/election.rs | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/election.rs b/src/election.rs index 877a3bd..66521fc 100644 --- a/src/election.rs +++ b/src/election.rs @@ -80,13 +80,18 @@ impl Election { /// Convert ballots with equal rankings to strict-preference "minivoters" pub fn realise_equal_rankings(&mut self) { - // 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)); + if !self.ballots.iter().any(|b| b.has_equal_rankings()) { + // No equal rankings + return; + } - let mut realised_ballots = Vec::new(); - for ballot in self.ballots.iter() { - let mut b = ballot.realise_equal_rankings(); - realised_ballots.append(&mut b); + // Record total_votes so loss by fraction can be calculated + // 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::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 { } impl Ballot { + /// 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> { + pub fn realise_equal_rankings_into(self, dest: &mut Vec>) { + if !self.has_equal_rankings() { + dest.push(self); + return; + } + // Preferences for each minivoter let mut minivoters = vec![Vec::new()]; @@ -541,10 +556,13 @@ impl Ballot { } 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 + }); + } } }