From 35104055d9ed8be0bff12d53f0cb9f24f888d58a Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Thu, 18 Aug 2022 23:55:39 +1000 Subject: [PATCH] Avoid excess allocations during fold operations --- src/election.rs | 4 ++-- src/stv/gregory/mod.rs | 4 ++-- src/stv/gregory/transfers.rs | 18 ++++++++++++++---- src/stv/meek.rs | 8 ++++---- src/stv/mod.rs | 15 +++++++-------- src/stv/sample.rs | 2 +- 6 files changed, 30 insertions(+), 21 deletions(-) diff --git a/src/election.rs b/src/election.rs index 66521fc..a99954f 100644 --- a/src/election.rs +++ b/src/election.rs @@ -450,7 +450,7 @@ impl<'a, N: Number> CountCard<'a, N> { /// Return the number of ballots across all parcels pub fn num_ballots(&self) -> N { - return self.parcels.iter().fold(N::new(), |acc, p| acc + p.num_ballots()); + return self.parcels.iter().fold(N::new(), |mut acc, p| { acc += p.num_ballots(); acc }); } } @@ -468,7 +468,7 @@ pub struct Parcel<'a, N> { impl<'a, N: Number> Parcel<'a, N> { /// Return the number of ballots in this parcel pub fn num_ballots(&self) -> N { - return self.votes.iter().fold(N::new(), |acc, v| acc + &v.ballot.orig_value); + return self.votes.iter().fold(N::new(), |mut acc, v| { acc += &v.ballot.orig_value; acc }); } /// Return the value of the votes in this parcel diff --git a/src/stv/gregory/mod.rs b/src/stv/gregory/mod.rs index 288ce28..8fc2f98 100644 --- a/src/stv/gregory/mod.rs +++ b/src/stv/gregory/mod.rs @@ -82,7 +82,7 @@ where // Calculate loss by fraction - if minivoters used if let Some(orig_total) = &state.election.total_votes { - let mut total_votes = state.candidates.values().fold(N::new(), |acc, cc| acc + &cc.votes); + let mut total_votes = state.candidates.values().fold(N::new(), |mut acc, cc| { acc += &cc.votes; acc }); total_votes += &state.exhausted.votes; let lbf = orig_total - &total_votes; @@ -115,7 +115,7 @@ where if !has_surplus.is_empty() { let total_surpluses = has_surplus.iter() - .fold(N::new(), |acc, c| acc + &state.candidates[c].votes - quota); + .fold(N::new(), |mut acc, c| { acc += &state.candidates[c].votes; acc -= quota; acc }); // Determine if surplues can be deferred if opts.defer_surpluses { diff --git a/src/stv/gregory/transfers.rs b/src/stv/gregory/transfers.rs index ce30032..2c14447 100644 --- a/src/stv/gregory/transfers.rs +++ b/src/stv/gregory/transfers.rs @@ -254,11 +254,16 @@ impl<'e, N: Number> TransferTable<'e, N> { // Sum total votes_out per candidate for (candidate, cell) in self.total.cells.iter_mut() { - cell.votes_out = self.columns.iter().fold(N::new(), |acc, col| if let Some(cell) = col.cells.get(candidate) { acc + &cell.votes_out } else { acc }); + cell.votes_out = self.columns.iter().fold(N::new(), |mut acc, col| { + if let Some(cell) = col.cells.get(candidate) { + acc += &cell.votes_out + } + acc + }); } // Sum total exhausted votes - self.total.exhausted.votes_out = self.columns.iter().fold(N::new(), |acc, col| acc + &col.exhausted.votes_out); + self.total.exhausted.votes_out = self.columns.iter().fold(N::new(), |mut acc, col| { acc += &col.exhausted.votes_out; acc }); } RoundSubtransfersMode::PerBallot => { // Calculate votes_out for each column @@ -316,11 +321,16 @@ impl<'e, N: Number> TransferTable<'e, N> { // Sum total votes_out per candidate for (candidate, cell) in self.total.cells.iter_mut() { - cell.votes_out = self.columns.iter().fold(N::new(), |acc, col| if let Some(cell) = col.cells.get(candidate) { acc + &cell.votes_out } else { acc }); + cell.votes_out = self.columns.iter().fold(N::new(), |mut acc, col| { + if let Some(cell) = col.cells.get(candidate) { + acc += &cell.votes_out; + } + acc + }); } // Sum total exhausted votes - self.total.exhausted.votes_out = self.columns.iter().fold(N::new(), |acc, col| acc + &col.exhausted.votes_out); + self.total.exhausted.votes_out = self.columns.iter().fold(N::new(), |mut acc, col| { acc += &col.exhausted.votes_out; acc }); } } } diff --git a/src/stv/meek.rs b/src/stv/meek.rs index 5a8e21f..f73deba 100644 --- a/src/stv/meek.rs +++ b/src/stv/meek.rs @@ -130,7 +130,7 @@ where // Calculate loss by fraction - if minivoters used if let Some(orig_total) = &state.election.total_votes { - let mut total_votes = state.candidates.values().fold(N::new(), |acc, cc| acc + &cc.votes); + let mut total_votes = state.candidates.values().fold(N::new(), |mut acc, cc| { acc += &cc.votes; acc }); total_votes += &state.exhausted.votes; let lbf = orig_total - &total_votes; @@ -241,7 +241,7 @@ where // Distribute if the total surplus exceeds the tolerance let quota_tolerance = N::parse(&opts.meek_surplus_tolerance); let total_surpluses = has_surplus.iter() - .fold(N::new(), |acc, c| acc + &state.candidates[c].votes - state.quota.as_ref().unwrap()); + .fold(N::new(), |mut acc, c| { acc += &state.candidates[c].votes; acc -= state.quota.as_ref().unwrap(); acc }); return total_surpluses > quota_tolerance; } } @@ -266,7 +266,7 @@ where // Determine if surplues can be deferred if opts.defer_surpluses { let total_surpluses = has_surplus.iter() - .fold(N::new(), |acc, c| acc + &state.candidates[c].votes - quota); + .fold(N::new(), |mut acc, c| { acc += &state.candidates[c].votes; acc -= quota; acc }); if super::can_defer_surpluses(state, opts, &total_surpluses) { state.logger.log_literal(format!("Distribution of surpluses totalling {:.dps$} votes will be deferred.", total_surpluses, dps=opts.pp_decimals)); return Ok(false); @@ -314,7 +314,7 @@ where // Determine if surplues can be deferred if should_distribute && opts.defer_surpluses { let total_surpluses = has_surplus.iter() - .fold(N::new(), |acc, c| acc + &state.candidates[c].votes - quota); + .fold(N::new(), |mut acc, c| { acc += &state.candidates[c].votes; acc -= quota; acc }); if super::can_defer_surpluses(state, opts, &total_surpluses) { surpluses_deferred = Some(total_surpluses); break; diff --git a/src/stv/mod.rs b/src/stv/mod.rs index 7371097..e73ed09 100644 --- a/src/stv/mod.rs +++ b/src/stv/mod.rs @@ -1093,15 +1093,14 @@ fn elect_sure_winners<'a, N: Number>(state: &mut CountState<'a, N>, opts: &STVOp let mut total_trailing = N::new(); // For leading candidates, count only untransferred surpluses - total_trailing += hopefuls.iter().take(num_vacancies).fold(N::new(), |acc, (_, cc)| { + total_trailing += hopefuls.iter().take(num_vacancies).fold(N::new(), |mut acc, (_, cc)| { if &cc.votes > state.quota.as_ref().unwrap() { - acc + &cc.votes - state.quota.as_ref().unwrap() - } else { - acc + acc += &cc.votes; acc -= state.quota.as_ref().unwrap(); } + acc }); // For trailing candidates, count all votes - total_trailing += hopefuls.iter().skip(num_vacancies).fold(N::new(), |acc, (_, cc)| acc + &cc.votes); + total_trailing += hopefuls.iter().skip(num_vacancies).fold(N::new(), |mut acc, (_, cc)| { acc += &cc.votes; acc }); // Add finally any votes awaiting transfer total_trailing += state.candidates.values().fold(N::zero(), |acc, cc| { match cc.state { @@ -1298,7 +1297,7 @@ where let num_to_exclude = to_exclude.len(); if num_to_exclude > 0 { let total_excluded = to_exclude.into_iter() - .fold(N::new(), |acc, c| acc + &state.candidates[c].votes); + .fold(N::new(), |mut acc, c| { acc += &state.candidates[c].votes; acc }); if total_surpluses >= &(&hopefuls[num_to_exclude].1.votes - &total_excluded) { return false; } @@ -1497,7 +1496,7 @@ fn hopefuls_to_bulk_exclude<'a, N: Number>(state: &CountState<'a, N>, _opts: &ST let total_surpluses = state.candidates.iter() .filter(|(_, cc)| !cc.finalised && &cc.votes > state.quota.as_ref().unwrap()) - .fold(N::new(), |agg, (_, cc)| agg + &cc.votes - state.quota.as_ref().unwrap()); + .fold(N::new(), |mut acc, (_, cc)| { acc += &cc.votes; acc -= state.quota.as_ref().unwrap(); acc }); // Attempt to exclude as many candidates as possible for i in 0..hopefuls.len() { @@ -1509,7 +1508,7 @@ fn hopefuls_to_bulk_exclude<'a, N: Number>(state: &CountState<'a, N>, _opts: &ST } // Do not exclude if this could change the order of exclusion - let total_votes = try_exclude.iter().fold(N::new(), |agg, (_, cc)| agg + &cc.votes); + let total_votes = try_exclude.iter().fold(N::new(), |mut acc, (_, cc)| { acc += &cc.votes; acc }); if i != 0 && total_votes + &total_surpluses >= hopefuls[hopefuls.len()-i].1.votes { continue; } diff --git a/src/stv/sample.rs b/src/stv/sample.rs index 68db912..065282a 100644 --- a/src/stv/sample.rs +++ b/src/stv/sample.rs @@ -186,7 +186,7 @@ where if opts.sample == SampleMethod::StratifyLR { // Round remainders to remove loss by fraction - let transferred = candidate_transfers_remainders.values().fold(N::new(), |acc, (t, _)| acc + t); + let transferred = candidate_transfers_remainders.values().fold(N::new(), |mut acc, (t, _)| { acc += t; acc }); let loss_fraction = &surplus - &transferred; if !loss_fraction.is_zero() && surplus_fraction.is_some() { let n_to_round: usize = format!("{:.0}", loss_fraction).parse().expect("Loss by fraction overflows usize");