Refactor calculation of totals, etc.
This commit is contained in:
parent
6ff111054c
commit
823f06a32b
|
@ -334,6 +334,58 @@ impl<'a, N: Number> CountState<'a, N> {
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
// -------------------
|
||||
// HELPER CALCULATIONS
|
||||
|
||||
/// Get the total vote, viz. the sum votes of all candidates
|
||||
pub fn total_vote(&self) -> N {
|
||||
return self.total_votes_of(self.candidates.values());
|
||||
}
|
||||
|
||||
/// Get the total votes of the given candidates
|
||||
pub fn total_votes_of<I: Iterator<Item=&'a CountCard<'a, N>>>(&self, count_cards: I) -> N {
|
||||
return count_cards.fold(N::new(), |mut acc, cc| { acc += &cc.votes; acc });
|
||||
}
|
||||
|
||||
/// Get the total active vote, viz. the votes of all continuing candidates plus all votes awaiting transfer
|
||||
pub fn active_vote(&self) -> N {
|
||||
return self.candidates.values().fold(N::new(), |mut acc, cc| {
|
||||
match cc.state {
|
||||
CandidateState::Elected => {
|
||||
if !cc.finalised && &cc.votes > self.quota.as_ref().unwrap() {
|
||||
acc += &cc.votes;
|
||||
acc -= self.quota.as_ref().unwrap();
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
acc += &cc.votes;
|
||||
}
|
||||
}
|
||||
acc
|
||||
});
|
||||
}
|
||||
|
||||
/// Get the total surplus
|
||||
///
|
||||
/// A candidate has a surplus if the candidate's votes are not finalised, and the votes exceed the quota.
|
||||
/// The votes of all other candidates are ignored.
|
||||
pub fn total_surplus(&self) -> N {
|
||||
return self.total_surplus_of(self.candidates.values());
|
||||
}
|
||||
|
||||
/// Get the total surpluses of the given candidates
|
||||
///
|
||||
/// See [total_surplus].
|
||||
pub fn total_surplus_of<I: Iterator<Item=&'a CountCard<'a, N>>>(&self, count_cards: I) -> N {
|
||||
return count_cards.fold(N::new(), |mut acc, cc| {
|
||||
if !cc.finalised && &cc.votes > self.quota.as_ref().unwrap() {
|
||||
acc += &cc.votes;
|
||||
acc -= self.quota.as_ref().unwrap();
|
||||
}
|
||||
acc
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// The kind, title, etc. of the stage being counted
|
||||
|
|
|
@ -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(), |mut acc, cc| { acc += &cc.votes; acc });
|
||||
let mut total_votes = state.total_vote();
|
||||
total_votes += &state.exhausted.votes;
|
||||
let lbf = orig_total - &total_votes;
|
||||
|
||||
|
@ -114,8 +114,7 @@ where
|
|||
.collect();
|
||||
|
||||
if !has_surplus.is_empty() {
|
||||
let total_surpluses = has_surplus.iter()
|
||||
.fold(N::new(), |mut acc, c| { acc += &state.candidates[c].votes; acc -= quota; acc });
|
||||
let total_surpluses = state.total_surplus();
|
||||
|
||||
// Determine if surplues can be deferred
|
||||
if opts.defer_surpluses {
|
||||
|
|
|
@ -134,7 +134,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(), |mut acc, cc| { acc += &cc.votes; acc });
|
||||
let mut total_votes = state.total_vote();
|
||||
total_votes += &state.exhausted.votes;
|
||||
let lbf = orig_total - &total_votes;
|
||||
|
||||
|
@ -244,8 +244,7 @@ where
|
|||
} else {
|
||||
// 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(), |mut acc, c| { acc += &state.candidates[c].votes; acc -= state.quota.as_ref().unwrap(); acc });
|
||||
let total_surpluses = state.total_surplus();
|
||||
return total_surpluses > quota_tolerance;
|
||||
}
|
||||
}
|
||||
|
@ -269,8 +268,7 @@ where
|
|||
if should_distribute {
|
||||
// Determine if surplues can be deferred
|
||||
if opts.defer_surpluses {
|
||||
let total_surpluses = has_surplus.iter()
|
||||
.fold(N::new(), |mut acc, c| { acc += &state.candidates[c].votes; acc -= quota; acc });
|
||||
let total_surpluses = state.total_surplus();
|
||||
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);
|
||||
|
@ -317,8 +315,7 @@ where
|
|||
|
||||
// Determine if surplues can be deferred
|
||||
if should_distribute && opts.defer_surpluses {
|
||||
let total_surpluses = has_surplus.iter()
|
||||
.fold(N::new(), |mut acc, c| { acc += &state.candidates[c].votes; acc -= quota; acc });
|
||||
let total_surpluses = state.total_surplus();
|
||||
if super::can_defer_surpluses(state, opts, &total_surpluses) {
|
||||
surpluses_deferred = Some(total_surpluses);
|
||||
break;
|
||||
|
|
|
@ -904,20 +904,7 @@ fn update_vre_ers<N: Number>(state: &mut CountState<N>, opts: &STVOptions) {
|
|||
let mut log = String::new();
|
||||
|
||||
// Calculate active vote
|
||||
let active_vote = state.candidates.values().fold(N::new(), |mut acc, cc| {
|
||||
match cc.state {
|
||||
CandidateState::Elected => {
|
||||
if !cc.finalised && &cc.votes > state.quota.as_ref().unwrap() {
|
||||
acc += &cc.votes;
|
||||
acc -= state.quota.as_ref().unwrap();
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
acc += &cc.votes;
|
||||
}
|
||||
}
|
||||
acc
|
||||
});
|
||||
let active_vote = state.active_vote();
|
||||
log.push_str(format!("Active vote is {:.dps$}, so the vote required for election is ", active_vote, dps=opts.pp_decimals).as_str());
|
||||
|
||||
let vote_req = active_vote / N::from(state.election.seats - state.num_elected + 1);
|
||||
|
@ -952,20 +939,7 @@ fn update_vre_bulk<N: Number>(state: &mut CountState<N>, _opts: &STVOptions) {
|
|||
//let mut log = String::new();
|
||||
|
||||
// Calculate active vote
|
||||
let active_vote = state.candidates.values().fold(N::new(), |mut acc, cc| {
|
||||
match cc.state {
|
||||
CandidateState::Elected => {
|
||||
if !cc.finalised && &cc.votes > state.quota.as_ref().unwrap() {
|
||||
acc += &cc.votes;
|
||||
acc -= state.quota.as_ref().unwrap();
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
acc += &cc.votes;
|
||||
}
|
||||
}
|
||||
acc
|
||||
});
|
||||
let active_vote = state.active_vote();
|
||||
//log.push_str(format!("Active vote is {:.dps$}, so the vote required for election is ", active_vote, dps=opts.pp_decimals).as_str());
|
||||
|
||||
let vote_req = active_vote / N::from(state.election.seats - state.num_elected + 1);
|
||||
|
@ -995,7 +969,7 @@ fn calculate_quota<N: Number>(state: &mut CountState<N>, opts: &STVOptions) {
|
|||
let mut log = String::new();
|
||||
|
||||
// Calculate the total vote
|
||||
let total_vote = state.candidates.values().fold(N::new(), |mut acc, cc| { acc += &cc.votes; acc });
|
||||
let total_vote = state.total_vote();
|
||||
log.push_str(format!("{:.dps$} usable votes, so the quota is ", total_vote, dps=opts.pp_decimals).as_str());
|
||||
|
||||
let quota = total_to_quota(total_vote, state.election.seats, opts);
|
||||
|
@ -1010,20 +984,7 @@ fn calculate_quota<N: Number>(state: &mut CountState<N>, opts: &STVOptions) {
|
|||
let mut log = String::new();
|
||||
|
||||
// Calculate the active vote
|
||||
let active_vote = state.candidates.values().fold(N::new(), |mut acc, cc| {
|
||||
match cc.state {
|
||||
CandidateState::Elected => {
|
||||
if !cc.finalised && &cc.votes > state.quota.as_ref().unwrap() {
|
||||
acc += &cc.votes;
|
||||
acc -= state.quota.as_ref().unwrap();
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
acc += &cc.votes;
|
||||
}
|
||||
}
|
||||
acc
|
||||
});
|
||||
let active_vote = state.active_vote();
|
||||
log.push_str(format!("Active vote is {:.dps$}, so the quota is is ", active_vote, dps=opts.pp_decimals).as_str());
|
||||
|
||||
// TODO: Calculate according to --quota ?
|
||||
|
@ -1044,7 +1005,7 @@ fn calculate_quota<N: Number>(state: &mut CountState<N>, opts: &STVOptions) {
|
|||
let mut log = String::new();
|
||||
|
||||
// Calculate the total vote
|
||||
let total_vote = state.candidates.values().fold(N::new(), |mut acc, cc| { acc += &cc.votes; acc });
|
||||
let total_vote = state.total_vote();
|
||||
log.push_str(format!("{:.dps$} usable votes, so the quota is reduced to ", total_vote, dps=opts.pp_decimals).as_str());
|
||||
|
||||
let quota = total_to_quota(total_vote, state.election.seats, opts);
|
||||
|
@ -1119,14 +1080,9 @@ 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(), |mut acc, (_, cc)| {
|
||||
if &cc.votes > state.quota.as_ref().unwrap() {
|
||||
acc += &cc.votes; acc -= state.quota.as_ref().unwrap();
|
||||
}
|
||||
acc
|
||||
});
|
||||
total_trailing += state.total_surplus_of(hopefuls.iter().take(num_vacancies).map(|(_, cc)| *cc));
|
||||
// For trailing candidates, count all votes
|
||||
total_trailing += hopefuls.iter().skip(num_vacancies).fold(N::new(), |mut acc, (_, cc)| { acc += &cc.votes; acc });
|
||||
total_trailing += state.total_votes_of(hopefuls.iter().skip(num_vacancies).map(|(_, cc)| *cc));
|
||||
// Add finally any votes awaiting transfer
|
||||
total_trailing += state.candidates.values().fold(N::new(), |mut acc, cc| {
|
||||
match cc.state {
|
||||
|
@ -1330,8 +1286,7 @@ where
|
|||
let to_exclude = hopefuls_to_bulk_exclude(state, opts);
|
||||
let num_to_exclude = to_exclude.len();
|
||||
if num_to_exclude > 0 {
|
||||
let total_excluded = to_exclude.into_iter()
|
||||
.fold(N::new(), |mut acc, c| { acc += &state.candidates[c].votes; acc });
|
||||
let total_excluded = state.total_votes_of(to_exclude.into_iter().map(|c| &state.candidates[c]));
|
||||
if total_surpluses >= &(&hopefuls[num_to_exclude].1.votes - &total_excluded) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1528,9 +1483,7 @@ fn hopefuls_to_bulk_exclude<'a, N: Number>(state: &CountState<'a, N>, _opts: &ST
|
|||
// NB: Unnecessary to handle ties, as ties will be rejected at "Do not exclude if this could change the order of exclusion"
|
||||
hopefuls.sort_unstable_by(|a, b| a.1.votes.cmp(&b.1.votes));
|
||||
|
||||
let total_surpluses = state.candidates.iter()
|
||||
.filter(|(_, cc)| !cc.finalised && &cc.votes > state.quota.as_ref().unwrap())
|
||||
.fold(N::new(), |mut acc, (_, cc)| { acc += &cc.votes; acc -= state.quota.as_ref().unwrap(); acc });
|
||||
let total_surpluses = state.total_surplus();
|
||||
|
||||
// Attempt to exclude as many candidates as possible
|
||||
for i in 0..hopefuls.len() {
|
||||
|
@ -1542,7 +1495,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(), |mut acc, (_, cc)| { acc += &cc.votes; acc });
|
||||
let total_votes = state.total_votes_of(try_exclude.iter().map(|(_, cc)| *cc));
|
||||
if i != 0 && total_votes + &total_surpluses >= hopefuls[hopefuls.len()-i].1.votes {
|
||||
continue;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue