Prepare for dynamic quota: independent flag for completion of surplus transfers/exclusions

This commit is contained in:
RunasSudo 2021-08-08 19:11:15 +10:00
parent 0581571440
commit ee1008b509
No known key found for this signature in database
GPG Key ID: 7234E476BF21C61A
5 changed files with 25 additions and 14 deletions

View File

@ -294,10 +294,11 @@ pub struct CountCard<'a, N> {
pub state: CandidateState,
/// Order of election or exclusion
///
/// Positive integers represent order of election; negative integers represent order of exclusion
/// Positive integers represent order of election; negative integers represent order of exclusion.
pub order_elected: isize,
/// Whether distribution of this candidate's surpluses/transfer of excluded candidate's votes is complete
pub finalised: bool,
//pub orig_votes: N,
/// Net votes transferred to this candidate in this stage
pub transfers: N,
/// Votes of the candidate at the end of this stage
@ -316,7 +317,7 @@ impl<'a, N: Number> CountCard<'a, N> {
return CountCard {
state: CandidateState::Hopeful,
order_elected: 0,
//orig_votes: N::new(),
finalised: false,
transfers: N::new(),
votes: N::new(),
parcels: Vec::new(),

View File

@ -75,7 +75,7 @@ where
let has_surplus: Vec<&Candidate> = state.election.candidates.iter() // Present in order in case of tie
.filter(|c| {
let cc = &state.candidates[c];
&cc.votes > quota && cc.parcels.iter().any(|p| !p.votes.is_empty())
&cc.votes > quota && !cc.finalised
})
.collect();
@ -388,7 +388,7 @@ where
count_card.votes.assign(state.quota.as_ref().unwrap());
checksum -= surplus;
count_card.parcels.clear(); // Mark surpluses as done
count_card.finalised = true; // Mark surpluses as done
// Update loss by fraction
state.loss_fraction.transfer(&-checksum);
@ -426,7 +426,7 @@ where
for excluded_candidate in excluded_candidates.iter() {
let count_card = state.candidates.get_mut(excluded_candidate).unwrap();
votes.append(&mut count_card.concat_parcels());
count_card.parcels.clear();
count_card.finalised = true;
// Update votes
let votes_transferred = votes.iter().fold(N::new(), |acc, v| acc + &v.value);
@ -437,7 +437,7 @@ where
}
ExclusionMethod::ByValue => {
// Exclude by value
let excluded_with_votes: Vec<&&Candidate> = excluded_candidates.iter().filter(|c| !state.candidates[*c].parcels.is_empty()).collect();
let excluded_with_votes: Vec<&&Candidate> = excluded_candidates.iter().filter(|c| !state.candidates[*c].finalised).collect();
if excluded_with_votes.is_empty() {
votes_remain = false;
@ -487,7 +487,7 @@ where
}
ExclusionMethod::BySource => {
// Exclude by source candidate
let excluded_with_votes: Vec<&&Candidate> = excluded_candidates.iter().filter(|c| !state.candidates[*c].parcels.is_empty()).collect();
let excluded_with_votes: Vec<&&Candidate> = excluded_candidates.iter().filter(|c| !state.candidates[*c].finalised).collect();
if excluded_with_votes.is_empty() {
votes_remain = false;
@ -617,6 +617,7 @@ where
checksum -= &count_card.votes;
count_card.transfers -= &count_card.votes;
count_card.votes = N::new();
count_card.finalised = true;
}
if let ExclusionMethod::SingleStage = opts.exclusion {
@ -665,6 +666,12 @@ where
CandidateState::Excluded => CandidateState::Excluded,
_ => CandidateState::Hopeful,
};
if count_card.state == CandidateState::Excluded {
count_card.finalised = true;
} else {
count_card.finalised = false;
}
}
state.exhausted.votes = N::new();

View File

@ -389,6 +389,7 @@ where
count_card.state = CandidateState::Excluded;
state.num_excluded += 1;
count_card.order_elected = -(order_excluded as isize);
count_card.finalised = true;
}
}

View File

@ -783,7 +783,7 @@ fn update_vre<N: Number>(state: &mut CountState<N>, opts: &STVOptions) {
// Calculate total active vote
let total_active_vote = state.candidates.values().fold(N::zero(), |acc, cc| {
match cc.state {
CandidateState::Elected => { if &cc.votes > state.quota.as_ref().unwrap() { acc + &cc.votes - state.quota.as_ref().unwrap() } else { acc } }
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 }
}
});
@ -923,7 +923,7 @@ fn elect_sure_winners<'a, N: Number>(state: &mut CountState<'a, N>, opts: &STVOp
// Add finally any votes awaiting transfer
total_trailing += state.candidates.values().fold(N::zero(), |acc, cc| {
match cc.state {
CandidateState::Elected => { if &cc.votes > state.quota.as_ref().unwrap() { acc + &cc.votes - state.quota.as_ref().unwrap() } else { acc } }
CandidateState::Elected => { if !cc.finalised && &cc.votes > state.quota.as_ref().unwrap() { acc + &cc.votes - state.quota.as_ref().unwrap() } else { acc } }
CandidateState::Hopeful | CandidateState::Guarded | CandidateState::Withdrawn => { acc }
CandidateState::Excluded | CandidateState::Doomed => { acc + &cc.votes }
}
@ -1275,7 +1275,7 @@ fn hopefuls_to_bulk_exclude<'a, N: Number>(state: &CountState<'a, N>, _opts: &ST
hopefuls.sort_unstable_by(|a, b| a.1.votes.cmp(&b.1.votes));
let total_surpluses = state.candidates.iter()
.filter(|(_, cc)| &cc.votes > state.quota.as_ref().unwrap())
.filter(|(_, cc)| !cc.finalised && &cc.votes > state.quota.as_ref().unwrap())
.fold(N::new(), |agg, (_, cc)| agg + &cc.votes - state.quota.as_ref().unwrap());
// Attempt to exclude as many candidates as possible
@ -1387,7 +1387,7 @@ where
{
// Cannot filter by raw vote count, as candidates may have 0.00 votes but still have recorded ballot papers
let mut excluded_with_votes: Vec<(&&Candidate, &CountCard<N>)> = state.candidates.iter()
.filter(|(_, cc)| cc.state == CandidateState::Excluded && cc.parcels.iter().any(|p| !p.votes.is_empty()))
.filter(|(_, cc)| cc.state == CandidateState::Excluded && !cc.finalised)
.collect();
if !excluded_with_votes.is_empty() {

View File

@ -181,7 +181,7 @@ where
count_card.votes.assign(state.quota.as_ref().unwrap());
checksum -= surplus;
count_card.parcels.clear(); // Mark surpluses as done
count_card.finalised = true; // Mark surpluses as done
// Update loss by fraction
state.loss_fraction.transfer(&-checksum);
@ -386,7 +386,6 @@ where
for excluded_candidate in excluded_candidates.iter() {
let count_card = state.candidates.get_mut(excluded_candidate).unwrap();
let votes = count_card.concat_parcels();
count_card.parcels.clear();
for vote in votes {
transfer_ballot(state, opts, excluded_candidate, vote, false)?;
@ -394,6 +393,9 @@ where
return Ok(());
}
}
let count_card = state.candidates.get_mut(excluded_candidate).unwrap();
count_card.finalised = true;
}
return Ok(());