Prepare for dynamic quota: independent flag for completion of surplus transfers/exclusions
This commit is contained in:
parent
0581571440
commit
ee1008b509
@ -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(),
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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() {
|
||||
|
@ -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(());
|
||||
|
Loading…
Reference in New Issue
Block a user