From 6bb127a12401e51baf85b6fef5ca2514a6cd75a2 Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Thu, 25 Aug 2022 21:49:50 +1000 Subject: [PATCH] Precompute Ballot::has_equal_rankings at parse time Before: 5.3943 +- 0.0287 After: 5.27661 +- 0.00472 --- src/election.rs | 15 +++++++-------- src/parser/blt.rs | 9 +++++++++ src/parser/csp.rs | 9 +++++++-- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/election.rs b/src/election.rs index 4c0a54f..2876dfd 100644 --- a/src/election.rs +++ b/src/election.rs @@ -71,6 +71,7 @@ impl Election { let new_ballot = Ballot { orig_value: N::one(), preferences: ballot.preferences.clone(), + has_equal_rankings: ballot.has_equal_rankings, }; normalised_ballots.push(new_ballot); n += &one; @@ -81,7 +82,7 @@ impl Election { /// 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()) { + if !self.ballots.iter().any(|b| b.has_equal_rankings) { // No equal rankings return; } @@ -582,17 +583,14 @@ pub struct Ballot { pub orig_value: N, /// Indexes of candidates preferenced at each level on the ballot pub preferences: Vec>, + /// Whether multiple candidates are preferenced at the same level + pub has_equal_rankings: bool, } 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_into(self, dest: &mut Vec>) { - if !self.has_equal_rankings() { + if !self.has_equal_rankings { dest.push(self); return; } @@ -631,7 +629,8 @@ impl Ballot { for minivoter in minivoters { dest.push(Ballot { orig_value: weight_each.clone(), - preferences: minivoter + preferences: minivoter, + has_equal_rankings: false, }); } } diff --git a/src/parser/blt.rs b/src/parser/blt.rs index f0446a4..e01018f 100644 --- a/src/parser/blt.rs +++ b/src/parser/blt.rs @@ -38,6 +38,9 @@ pub struct BLTParser> { /// Temporary buffer for parsing ballot values ballot_value_buf: String, + /// Whether the current ballot has equal preferences + ballot_has_equal_rankings: bool, + /// Current line number line_no: u32, /// Current column number @@ -139,6 +142,8 @@ impl> BLTParser { /// Parse a ballot fn ballot(&mut self) -> Result<(), ParseError> { + self.ballot_has_equal_rankings = false; + self.ballot_value()?; self.delimiter_not_nl(); @@ -152,6 +157,8 @@ impl> BLTParser { break; } else if self.lookahead() == '=' { // Equal preference + self.ballot_has_equal_rankings = true; + self.accept(); preferences.last_mut().unwrap().push(self.usize()? - 1); self.delimiter_not_nl(); @@ -167,6 +174,7 @@ impl> BLTParser { let ballot = Ballot { orig_value: N::parse(&self.ballot_value_buf), preferences, + has_equal_rankings: self.ballot_has_equal_rankings, }; self.election.ballots.push(ballot); @@ -376,6 +384,7 @@ impl> BLTParser { Self { chars, ballot_value_buf: String::new(), + ballot_has_equal_rankings: false, line_no: 1, col_no: 1, num_candidates: 0, diff --git a/src/parser/csp.rs b/src/parser/csp.rs index a621909..640a5db 100644 --- a/src/parser/csp.rs +++ b/src/parser/csp.rs @@ -95,6 +95,7 @@ pub fn parse_reader(reader: R, require_1: bool, require_sequ ballots.push(Ballot { orig_value: value, preferences: vec![], + has_equal_rankings: false, }); continue; } @@ -102,17 +103,20 @@ pub fn parse_reader(reader: R, require_1: bool, require_sequ let mut sorted_preferences = Vec::with_capacity(preferences.len()); let mut last_ranking = None; + let mut has_equal_rankings = false; + for ranking in unique_rankings { // Filter for preferences at this ranking let prefs_this_ranking: Vec = preferences.iter() .filter_map(|(r, i)| if *r == ranking { Some(**i) } else { None }) .collect(); - if require_strict_order { - if prefs_this_ranking.len() != 1 { + if prefs_this_ranking.len() != 1 { + if require_strict_order { // Duplicate rankings break; } + has_equal_rankings = true; } if require_sequential { @@ -131,6 +135,7 @@ pub fn parse_reader(reader: R, require_1: bool, require_sequ ballots.push(Ballot { orig_value: value, preferences: sorted_preferences, + has_equal_rankings, }); }