From de19324d2ccaf96fff28289781a1dd0cce7a6972 Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Fri, 10 Sep 2021 01:29:13 +1000 Subject: [PATCH] Report vote required for election in relation to early bulk election --- src/stv/mod.rs | 54 ++++++++++++++++++++++++++++++++++++++++++------- src/stv/wasm.rs | 3 +-- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/stv/mod.rs b/src/stv/mod.rs index 9756b19..bc16897 100644 --- a/src/stv/mod.rs +++ b/src/stv/mod.rs @@ -801,7 +801,7 @@ fn total_to_quota(mut total: N, seats: usize, opts: &STVOptions) -> N } /// Update vote required for election according to ERS97/ERS76 rules -fn update_vre(state: &mut CountState, opts: &STVOptions) { +fn update_vre_ers(state: &mut CountState, opts: &STVOptions) { if opts.quota_mode == QuotaMode::ERS76 && state.num_excluded == 0 && (state.num_elected == 0 || state.candidates.values().all(|cc| !cc.finalised)) { // ERS76 rules: Do not update VRE until a surplus is distributed or candidate is excluded state.vote_required_election = state.quota.clone(); @@ -819,7 +819,6 @@ fn update_vre(state: &mut CountState, opts: &STVOptions) { }); log.push_str(format!("Active vote is {:.dps$}, so the vote required for election is ", active_vote, dps=opts.pp_decimals).as_str()); - // TODO: Calculate according to --quota ? let vote_req = active_vote / N::from(state.election.seats - state.num_elected + 1); if &vote_req < state.quota.as_ref().unwrap() { @@ -906,10 +905,42 @@ fn calculate_quota(state: &mut CountState, opts: &STVOptions) { // Calculate vote required for election if state.num_elected < state.election.seats { - update_vre(state, opts); + update_vre_ers(state, opts); } } else { - // No ERS97/ERS76 rules, so no use of VRE + // No ERS97/ERS76 rules + + // If --early-bulk-elect and one candidate remains, VRE is half of the active vote + // For display purposes only + if opts.early_bulk_elect && (state.election.seats - state.num_elected) == 1 { + //let mut log = String::new(); + + // Calculate active vote + let active_vote = state.candidates.values().fold(N::zero(), |acc, cc| { + match cc.state { + CandidateState::Elected => { if !cc.finalised && &cc.votes > state.quota.as_ref().unwrap() { acc + &cc.votes - state.quota.as_ref().unwrap() } else { acc } } + _ => { acc + &cc.votes } + } + }); + //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); + + if &vote_req < state.quota.as_ref().unwrap() { + // VRE is less than the quota + //if let Some(v) = &state.vote_required_election { + // if &vote_req != v { + //log.push_str(format!("{:.dps$}.", vote_req, dps=opts.pp_decimals).as_str()); + state.vote_required_election = Some(vote_req); + //state.logger.log_literal(log); + // } + //} else { + //log.push_str(format!("{:.dps$}.", vote_req, dps=opts.pp_decimals).as_str()); + // state.vote_required_election = Some(vote_req); + //state.logger.log_literal(log); + //} + } + } } } @@ -927,10 +958,11 @@ fn cmp_quota_criterion(quota: &N, count_card: &CountCard, opts: &S /// Determine if the given candidate meets the vote required to be elected, according to [STVOptions::quota_criterion] and [STVOptions::quota_mode] fn meets_vre(state: &CountState, count_card: &CountCard, opts: &STVOptions) -> bool { - if let Some(vre) = &state.vote_required_election { + if opts.quota_mode == QuotaMode::ERS97 || opts.quota_mode == QuotaMode::ERS76 { // VRE is set because ERS97/ERS76 rules - return cmp_quota_criterion(vre, count_card, opts); + return cmp_quota_criterion(state.vote_required_election.as_ref().unwrap(), count_card, opts); } else { + // VRE is set (if at all) for display purposes only so ignore it here return cmp_quota_criterion(state.quota.as_ref().unwrap(), count_card, opts); } } @@ -986,7 +1018,15 @@ fn elect_sure_winners<'a, N: Number>(state: &mut CountState<'a, N>, opts: &STVOp Err(_) => { return Ok(false); } // Bulk election conflicts with constraints } - // Bulk elect all leading candidates + // Bulk election is possible! + // Elect all leading candidates + + if num_vacancies > 1 { + // Update VRE + // (If num_vacancies == 1, this has already been done in calculate_quota) + state.vote_required_election = Some(total_trailing); + } + while !leading_hopefuls.is_empty() && state.num_elected < state.election.seats { let max_cands = ties::multiple_max_by(&leading_hopefuls, |c| &state.candidates[c].votes); let candidate = if max_cands.len() > 1 { diff --git a/src/stv/wasm.rs b/src/stv/wasm.rs index 674c519..afe4969 100644 --- a/src/stv/wasm.rs +++ b/src/stv/wasm.rs @@ -318,8 +318,7 @@ fn describe_count(filename: String, election: &Election, opts: &st #[inline] fn should_show_vre(opts: &stv::STVOptions) -> bool { - //return opts.quota_mode == stv::QuotaMode::ERS97 || opts.quota_mode == stv::QuotaMode::ERS76 || opts.early_bulk_elect; - return opts.quota_mode == stv::QuotaMode::ERS97 || opts.quota_mode == stv::QuotaMode::ERS76; + return opts.quota_mode == stv::QuotaMode::ERS97 || opts.quota_mode == stv::QuotaMode::ERS76 || opts.early_bulk_elect; } /// Generate the first column of the HTML results table