Implement --sum-surplus-transfers
This commit is contained in:
parent
9d4cac2e89
commit
96a3eaec84
|
@ -151,6 +151,12 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.20"
|
||||
|
@ -233,6 +239,15 @@ dependencies = [
|
|||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.7"
|
||||
|
@ -338,6 +353,7 @@ dependencies = [
|
|||
"flate2",
|
||||
"git-version",
|
||||
"ibig",
|
||||
"itertools",
|
||||
"js-sys",
|
||||
"num-bigint",
|
||||
"num-rational",
|
||||
|
|
|
@ -11,6 +11,7 @@ crate-type = ["lib", "cdylib"]
|
|||
derive_more = "0.99.14"
|
||||
git-version = "0.3.4"
|
||||
ibig = "0.3.2"
|
||||
itertools = "0.10.1"
|
||||
num-traits = "0.2"
|
||||
wasm-bindgen = "0.2.74"
|
||||
|
||||
|
|
|
@ -35,7 +35,8 @@
|
|||
<label>
|
||||
Preset:
|
||||
<select id="selPreset" onchange="changePreset()">
|
||||
<option value="scottish" selected>Scottish STV</option>
|
||||
<option value="wigm" selected>Recommended WIGM</option>
|
||||
<option value="scottish">Scottish STV</option>
|
||||
<option value="senate">Australian Senate STV</option>
|
||||
<!--<option value="meek">Meek STV</option>
|
||||
<option value="wright">Wright STV</option>-->
|
||||
|
@ -58,14 +59,14 @@
|
|||
<label>
|
||||
Quota:
|
||||
<select id="selQuotaCriterion">
|
||||
<option value="geq" selected>>=</option>
|
||||
<option value="gt">></option>
|
||||
<option value="geq">≥</option>
|
||||
<option value="gt" selected>></option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<select id="selQuota">
|
||||
<option value="droop" selected>Droop</option>
|
||||
<option value="droop_exact">Droop (exact)</option>
|
||||
<option value="droop">Droop</option>
|
||||
<option value="droop_exact" selected>Droop (exact)</option>
|
||||
<option value="hare">Hare</option>
|
||||
<option value="hare_exact">Hare (exact)</option>
|
||||
</select>
|
||||
|
@ -146,8 +147,8 @@
|
|||
<label>
|
||||
Numbers:
|
||||
<select id="selNumbers">
|
||||
<option value="rational">Rational</option>
|
||||
<option value="fixed" selected>Fixed</option>
|
||||
<option value="rational" selected>Rational</option>
|
||||
<option value="fixed">Fixed</option>
|
||||
<!--<option value="gfixed">Fixed (guarded)</option>-->
|
||||
<option value="float64">Float (64-bit)</option>
|
||||
</select>
|
||||
|
@ -184,7 +185,7 @@
|
|||
</div>
|
||||
<div class="col-6">
|
||||
<label>
|
||||
<input type="checkbox" id="chkRoundQuota" checked>
|
||||
<input type="checkbox" id="chkRoundQuota">
|
||||
Quota:
|
||||
</label>
|
||||
<label>
|
||||
|
@ -205,7 +206,7 @@
|
|||
<div class="col-6">
|
||||
<label>
|
||||
<input type="checkbox" id="chkRoundTVs">
|
||||
Transfer values:
|
||||
Surplus fractions:
|
||||
</label>
|
||||
<label>
|
||||
<input type="number" id="txtRoundTVs" value="0" min="0" style="width: 3em;">
|
||||
|
@ -222,6 +223,14 @@
|
|||
d.p.
|
||||
</label>
|
||||
</div>
|
||||
<label class="col-12">
|
||||
Sum surplus transfers:
|
||||
<select id="selSumTransfers">
|
||||
<option value="single_step" selected>Single step</option>
|
||||
<option value="by_value">By value</option>
|
||||
<option value="per_ballot">Per ballot</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -95,6 +95,7 @@ async function clickCount() {
|
|||
document.getElementById('chkRoundWeights').checked ? parseInt(document.getElementById('txtRoundWeights').value) : null,
|
||||
document.getElementById('chkRoundVotes').checked ? parseInt(document.getElementById('txtRoundVotes').value) : null,
|
||||
document.getElementById('chkRoundQuota').checked ? parseInt(document.getElementById('txtRoundQuota').value) : null,
|
||||
document.getElementById('selSumTransfers').value,
|
||||
document.getElementById('selQuota').value,
|
||||
document.getElementById('selQuotaCriterion').value,
|
||||
document.getElementById('selQuotaMode').value,
|
||||
|
@ -291,7 +292,26 @@ async function printResult() {
|
|||
// Presets
|
||||
|
||||
function changePreset() {
|
||||
if (document.getElementById('selPreset').value === 'scottish') {
|
||||
if (document.getElementById('selPreset').value === 'wigm') {
|
||||
document.getElementById('selQuotaCriterion').value = 'gt';
|
||||
document.getElementById('selQuota').value = 'droop_exact';
|
||||
document.getElementById('selQuotaMode').value = 'static';
|
||||
document.getElementById('chkBulkElection').checked = true;
|
||||
document.getElementById('chkBulkExclusion').checked = false;
|
||||
document.getElementById('chkDeferSurpluses').checked = false;
|
||||
document.getElementById('selNumbers').value = 'rational';
|
||||
document.getElementById('txtPPDP').value = '2';
|
||||
document.getElementById('chkRoundQuota').checked = false;
|
||||
document.getElementById('chkRoundVotes').checked = false;
|
||||
document.getElementById('chkRoundTVs').checked = false;
|
||||
document.getElementById('chkRoundWeights').checked = false;
|
||||
document.getElementById('selSumTransfers').value = 'single_step';
|
||||
document.getElementById('selSurplus').value = 'by_size';
|
||||
document.getElementById('selTransfers').value = 'wig';
|
||||
document.getElementById('selPapers').value = 'both';
|
||||
document.getElementById('selExclusion').value = 'single_stage';
|
||||
//document.getElementById('selTies').value = 'backwards_random';
|
||||
} else if (document.getElementById('selPreset').value === 'scottish') {
|
||||
document.getElementById('selQuotaCriterion').value = 'geq';
|
||||
document.getElementById('selQuota').value = 'droop';
|
||||
document.getElementById('selQuotaMode').value = 'static';
|
||||
|
@ -300,12 +320,14 @@ function changePreset() {
|
|||
document.getElementById('chkDeferSurpluses').checked = false;
|
||||
document.getElementById('selNumbers').value = 'fixed';
|
||||
document.getElementById('txtDP').value = '5';
|
||||
document.getElementById('txtPPDP').value = '2';
|
||||
document.getElementById('txtPPDP').value = '5';
|
||||
document.getElementById('chkRoundQuota').checked = true;
|
||||
document.getElementById('txtRoundQuota').value = '0';
|
||||
document.getElementById('chkRoundVotes').checked = false;
|
||||
document.getElementById('chkRoundTVs').checked = false;
|
||||
document.getElementById('chkRoundTVs').checked = true;
|
||||
document.getElementById('txtRoundTVs').value = '5';
|
||||
document.getElementById('chkRoundWeights').checked = false;
|
||||
document.getElementById('selSumTransfers').value = 'per_ballot';
|
||||
document.getElementById('selSurplus').value = 'by_size';
|
||||
document.getElementById('selTransfers').value = 'wig';
|
||||
document.getElementById('selPapers').value = 'both';
|
||||
|
@ -327,6 +349,7 @@ function changePreset() {
|
|||
document.getElementById('txtRoundVotes').value = '0';
|
||||
document.getElementById('chkRoundTVs').checked = false;
|
||||
document.getElementById('chkRoundWeights').checked = false;
|
||||
document.getElementById('selSumTransfers').value = 'single_step';
|
||||
document.getElementById('selSurplus').value = 'by_order';
|
||||
document.getElementById('selTransfers').value = 'uig';
|
||||
document.getElementById('selPapers').value = 'both';
|
||||
|
@ -350,6 +373,7 @@ function changePreset() {
|
|||
document.getElementById('txtRoundTVs').value = '3';
|
||||
document.getElementById('chkRoundWeights').checked = true;
|
||||
document.getElementById('txtRoundWeights').value = '3';
|
||||
document.getElementById('selSumTransfers').value = 'single_step';
|
||||
document.getElementById('selSurplus').value = 'by_order';
|
||||
document.getElementById('selTransfers').value = 'eg';
|
||||
document.getElementById('selPapers').value = 'transferable';
|
||||
|
@ -373,6 +397,7 @@ function changePreset() {
|
|||
document.getElementById('txtRoundTVs').value = '2';
|
||||
document.getElementById('chkRoundWeights').checked = true;
|
||||
document.getElementById('txtRoundWeights').value = '2';
|
||||
document.getElementById('selSumTransfers').value = 'single_step';
|
||||
document.getElementById('selSurplus').value = 'by_size';
|
||||
document.getElementById('selTransfers').value = 'eg';
|
||||
document.getElementById('selPapers').value = 'transferable';
|
||||
|
|
|
@ -78,6 +78,9 @@ struct STV {
|
|||
#[clap(help_heading=Some("ROUNDING"), long, value_name="dps")]
|
||||
round_quota: Option<usize>,
|
||||
|
||||
#[clap(help_heading=Some("ROUNDING"), long, possible_values=&["single_step", "by_value", "per_ballot"], default_value="single_step", value_name="mode")]
|
||||
sum_surplus_transfers: String,
|
||||
|
||||
// -----------
|
||||
// -- Quota --
|
||||
|
||||
|
@ -173,6 +176,7 @@ where
|
|||
cmd_opts.round_weights,
|
||||
cmd_opts.round_votes,
|
||||
cmd_opts.round_quota,
|
||||
&cmd_opts.sum_surplus_transfers,
|
||||
&cmd_opts.quota,
|
||||
&cmd_opts.quota_criterion,
|
||||
&cmd_opts.quota_mode,
|
||||
|
|
133
src/stv/mod.rs
133
src/stv/mod.rs
|
@ -23,6 +23,7 @@ pub mod wasm;
|
|||
use crate::numbers::Number;
|
||||
use crate::election::{Candidate, CandidateState, CountCard, CountState, Parcel, Vote};
|
||||
|
||||
use itertools::Itertools;
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
|
||||
use std::cmp::max;
|
||||
|
@ -35,6 +36,7 @@ pub struct STVOptions {
|
|||
pub round_weights: Option<usize>,
|
||||
pub round_votes: Option<usize>,
|
||||
pub round_quota: Option<usize>,
|
||||
pub sum_surplus_transfers: SumSurplusTransfersMode,
|
||||
pub quota: QuotaType,
|
||||
pub quota_criterion: QuotaCriterion,
|
||||
pub quota_mode: QuotaMode,
|
||||
|
@ -54,6 +56,7 @@ impl STVOptions {
|
|||
round_weights: Option<usize>,
|
||||
round_votes: Option<usize>,
|
||||
round_quota: Option<usize>,
|
||||
sum_transfers: &str,
|
||||
quota: &str,
|
||||
quota_criterion: &str,
|
||||
quota_mode: &str,
|
||||
|
@ -70,6 +73,12 @@ impl STVOptions {
|
|||
round_weights,
|
||||
round_votes,
|
||||
round_quota,
|
||||
sum_surplus_transfers: match sum_transfers {
|
||||
"single_step" => SumSurplusTransfersMode::SingleStep,
|
||||
"by_value" => SumSurplusTransfersMode::ByValue,
|
||||
"per_ballot" => SumSurplusTransfersMode::PerBallot,
|
||||
_ => panic!("Invalid --sum-transfers"),
|
||||
},
|
||||
quota: match quota {
|
||||
"droop" => QuotaType::Droop,
|
||||
"hare" => QuotaType::Hare,
|
||||
|
@ -134,6 +143,15 @@ impl STVOptions {
|
|||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(PartialEq)]
|
||||
pub enum SumSurplusTransfersMode {
|
||||
SingleStep,
|
||||
ByValue,
|
||||
PerBallot,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(PartialEq)]
|
||||
|
@ -581,6 +599,7 @@ where
|
|||
fn distribute_surpluses<N: Number>(state: &mut CountState<N>, opts: &STVOptions) -> bool
|
||||
where
|
||||
for<'r> &'r N: ops::Sub<&'r N, Output=N>,
|
||||
for<'r> &'r N: ops::Div<&'r N, Output=N>,
|
||||
for<'r> &'r N: ops::Neg<Output=N>
|
||||
{
|
||||
let quota = state.quota.as_ref().unwrap();
|
||||
|
@ -620,7 +639,7 @@ where
|
|||
}
|
||||
|
||||
/// Return the denominator of the transfer value
|
||||
fn calculate_transfer_denom<N: Number>(surplus: &N, result: &NextPreferencesResult<N>, transferable_votes: &N, weighted: bool, transferable_only: bool) -> Option<N>
|
||||
fn calculate_surplus_denom<N: Number>(surplus: &N, result: &NextPreferencesResult<N>, transferable_votes: &N, weighted: bool, transferable_only: bool) -> Option<N>
|
||||
where
|
||||
for<'r> &'r N: ops::Sub<&'r N, Output=N>
|
||||
{
|
||||
|
@ -648,21 +667,21 @@ fn reweight_vote<N: Number>(
|
|||
num_ballots: &N,
|
||||
surplus: &N,
|
||||
weighted: bool,
|
||||
transfer_value: &Option<N>,
|
||||
transfer_denom: &Option<N>,
|
||||
surplus_fraction: &Option<N>,
|
||||
surplus_denom: &Option<N>,
|
||||
round_tvs: Option<usize>,
|
||||
rounding: Option<usize>) -> N
|
||||
{
|
||||
let mut result;
|
||||
|
||||
match transfer_denom {
|
||||
match surplus_denom {
|
||||
Some(v) => {
|
||||
if let Some(_) = round_tvs {
|
||||
// Rounding requested: use the rounded transfer value
|
||||
if weighted {
|
||||
result = num_votes.clone() * transfer_value.as_ref().unwrap();
|
||||
result = num_votes.clone() * surplus_fraction.as_ref().unwrap();
|
||||
} else {
|
||||
result = num_ballots.clone() * transfer_value.as_ref().unwrap();
|
||||
result = num_ballots.clone() * surplus_fraction.as_ref().unwrap();
|
||||
}
|
||||
} else {
|
||||
// Avoid unnecessary rounding error by first multiplying by the surplus
|
||||
|
@ -686,11 +705,57 @@ fn reweight_vote<N: Number>(
|
|||
return result;
|
||||
}
|
||||
|
||||
fn sum_surplus_transfers<N: Number>(entry: &NextPreferencesEntry<N>, surplus: &N, is_weighted: bool, surplus_fraction: &Option<N>, surplus_denom: &Option<N>, _state: &mut CountState<N>, opts: &STVOptions) -> N
|
||||
where
|
||||
for<'r> &'r N: ops::Div<&'r N, Output=N>,
|
||||
{
|
||||
match opts.sum_surplus_transfers {
|
||||
SumSurplusTransfersMode::SingleStep => {
|
||||
// Calculate transfer across all votes
|
||||
//state.logger.log_literal(format!("Transferring {:.0} ballot papers, totalling {:.dps$} votes.", entry.num_ballots, entry.num_votes, dps=opts.pp_decimals));
|
||||
return reweight_vote(&entry.num_votes, &entry.num_ballots, surplus, is_weighted, surplus_fraction, surplus_denom, opts.round_tvs, opts.round_votes);
|
||||
}
|
||||
SumSurplusTransfersMode::ByValue => {
|
||||
// Sum transfers by value
|
||||
let mut result = N::new();
|
||||
|
||||
// Sort into parcels by value
|
||||
let mut votes: Vec<&Vote<N>> = entry.votes.iter().collect();
|
||||
votes.sort_unstable_by(|a, b| (&a.value / &a.ballot.orig_value).cmp(&(&b.value / &b.ballot.orig_value)));
|
||||
for (_value, parcel) in &votes.into_iter().group_by(|v| &v.value / &v.ballot.orig_value) {
|
||||
let mut num_votes = N::new();
|
||||
let mut num_ballots = N::new();
|
||||
for vote in parcel {
|
||||
num_votes += &vote.value;
|
||||
num_ballots += &vote.ballot.orig_value;
|
||||
}
|
||||
//state.logger.log_literal(format!("Transferring {:.0} ballot papers, totalling {:.dps$} votes, received at value {:.dps2$}.", num_ballots, num_votes, value, dps=opts.pp_decimals, dps2=max(opts.pp_decimals, 2)));
|
||||
result += reweight_vote(&num_votes, &num_ballots, surplus, is_weighted, surplus_fraction, surplus_denom, opts.round_tvs, opts.round_votes);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
SumSurplusTransfersMode::PerBallot => {
|
||||
// Sum transfer per each individual ballot
|
||||
// TODO: This could be moved to distribute_surplus to avoid looping over the votes and calculating transfer values twice
|
||||
let mut result = N::new();
|
||||
for vote in entry.votes.iter() {
|
||||
result += reweight_vote(&vote.value, &vote.ballot.orig_value, surplus, is_weighted, surplus_fraction, surplus_denom, opts.round_tvs, opts.round_votes);
|
||||
}
|
||||
//state.logger.log_literal(format!("Transferring {:.0} ballot papers, totalling {:.dps$} votes.", entry.num_ballots, entry.num_votes, dps=opts.pp_decimals));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn distribute_surplus<N: Number>(state: &mut CountState<N>, opts: &STVOptions, elected_candidate: &Candidate)
|
||||
where
|
||||
for<'r> &'r N: ops::Sub<&'r N, Output=N>,
|
||||
for<'r> &'r N: ops::Div<&'r N, Output=N>,
|
||||
for<'r> &'r N: ops::Neg<Output=N>
|
||||
{
|
||||
state.logger.log_literal(format!("Surplus of {} distributed.", elected_candidate.name));
|
||||
|
||||
let count_card = state.candidates.get(elected_candidate).unwrap();
|
||||
let surplus = &count_card.votes - state.quota.as_ref().unwrap();
|
||||
|
||||
|
@ -725,47 +790,54 @@ where
|
|||
};
|
||||
|
||||
let transferable_votes = &result.total_votes - &result.exhausted.num_votes;
|
||||
let transfer_denom = calculate_transfer_denom(&surplus, &result, &transferable_votes, is_weighted, opts.transferable_only);
|
||||
let mut transfer_value;
|
||||
match transfer_denom {
|
||||
let surplus_denom = calculate_surplus_denom(&surplus, &result, &transferable_votes, is_weighted, opts.transferable_only);
|
||||
let mut surplus_fraction;
|
||||
match surplus_denom {
|
||||
Some(ref v) => {
|
||||
transfer_value = Some(surplus.clone() / v);
|
||||
surplus_fraction = Some(surplus.clone() / v);
|
||||
|
||||
// Round down if requested
|
||||
if let Some(dps) = opts.round_tvs {
|
||||
transfer_value.as_mut().unwrap().floor_mut(dps);
|
||||
surplus_fraction.as_mut().unwrap().floor_mut(dps);
|
||||
}
|
||||
|
||||
state.logger.log_literal(format!("Surplus of {} distributed at value {:.dps2$}.", elected_candidate.name, transfer_value.as_ref().unwrap(), dps2=max(opts.pp_decimals, 2)));
|
||||
if opts.transferable_only {
|
||||
state.logger.log_literal(format!("Transferring {:.0} transferable ballots, totalling {:.dps$} transferable votes, with surplus fraction {:.dps2$}.", &result.total_ballots - &result.exhausted.num_ballots, transferable_votes, surplus_fraction.as_ref().unwrap(), dps=opts.pp_decimals, dps2=max(opts.pp_decimals, 2)));
|
||||
} else {
|
||||
state.logger.log_literal(format!("Transferring {:.0} ballots, totalling {:.dps$} votes, with surplus fraction {:.dps2$}.", result.total_ballots, result.total_votes, surplus_fraction.as_ref().unwrap(), dps=opts.pp_decimals, dps2=max(opts.pp_decimals, 2)));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
transfer_value = None;
|
||||
state.logger.log_literal(format!("Surplus of {} distributed at values received.", elected_candidate.name));
|
||||
surplus_fraction = None;
|
||||
|
||||
if opts.transferable_only {
|
||||
state.logger.log_literal(format!("Transferring {:.0} transferable ballots, totalling {:.dps$} transferable votes, at values received.", &result.total_ballots - &result.exhausted.num_ballots, transferable_votes, dps=opts.pp_decimals));
|
||||
} else {
|
||||
state.logger.log_literal(format!("Transferring {:.0} ballots, totalling {:.dps$} votes, at values received.", result.total_ballots, result.total_votes, dps=opts.pp_decimals));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut checksum = N::new();
|
||||
|
||||
for (candidate, entry) in result.candidates.into_iter() {
|
||||
// Credit transferred votes
|
||||
let candidate_transfers = sum_surplus_transfers(&entry, &surplus, is_weighted, &surplus_fraction, &surplus_denom, state, opts);
|
||||
let count_card = state.candidates.get_mut(candidate).unwrap();
|
||||
count_card.transfer(&candidate_transfers);
|
||||
checksum += candidate_transfers;
|
||||
|
||||
let mut parcel = entry.votes as Parcel<N>;
|
||||
|
||||
// Reweight votes
|
||||
for vote in parcel.iter_mut() {
|
||||
vote.value = reweight_vote(&vote.value, &vote.ballot.orig_value, &surplus, is_weighted, &transfer_value, &transfer_denom, opts.round_tvs, opts.round_weights);
|
||||
vote.value = reweight_vote(&vote.value, &vote.ballot.orig_value, &surplus, is_weighted, &surplus_fraction, &surplus_denom, opts.round_tvs, opts.round_weights);
|
||||
}
|
||||
|
||||
let count_card = state.candidates.get_mut(candidate).unwrap();
|
||||
count_card.parcels.push(parcel);
|
||||
|
||||
let candidate_transfers = reweight_vote(&entry.num_votes, &entry.num_ballots, &surplus, is_weighted, &transfer_value, &transfer_denom, opts.round_tvs, opts.round_votes);
|
||||
count_card.transfer(&candidate_transfers);
|
||||
checksum += candidate_transfers;
|
||||
}
|
||||
|
||||
// Transfer exhausted votes
|
||||
let parcel = result.exhausted.votes as Parcel<N>;
|
||||
state.exhausted.parcels.push(parcel);
|
||||
|
||||
// Credit exhausted votes
|
||||
let mut exhausted_transfers;
|
||||
if opts.transferable_only {
|
||||
if transferable_votes > surplus {
|
||||
|
@ -773,17 +845,22 @@ where
|
|||
exhausted_transfers = N::new();
|
||||
} else {
|
||||
exhausted_transfers = &surplus - &transferable_votes;
|
||||
}
|
||||
} else {
|
||||
exhausted_transfers = reweight_vote(&result.exhausted.num_votes, &result.exhausted.num_ballots, &surplus, is_weighted, &transfer_value, &transfer_denom, opts.round_tvs, opts.round_votes);
|
||||
}
|
||||
|
||||
if let Some(dps) = opts.round_votes {
|
||||
exhausted_transfers.floor_mut(dps);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
exhausted_transfers = sum_surplus_transfers(&result.exhausted, &surplus, is_weighted, &surplus_fraction, &surplus_denom, state, opts);
|
||||
}
|
||||
|
||||
state.exhausted.transfer(&exhausted_transfers);
|
||||
checksum += exhausted_transfers;
|
||||
|
||||
// Transfer exhausted votes
|
||||
let parcel = result.exhausted.votes as Parcel<N>;
|
||||
state.exhausted.parcels.push(parcel);
|
||||
|
||||
// Finalise candidate votes
|
||||
let count_card = state.candidates.get_mut(elected_candidate).unwrap();
|
||||
count_card.transfers = -&surplus;
|
||||
|
|
|
@ -59,6 +59,7 @@ fn aec_tas19_rational() {
|
|||
round_weights: None,
|
||||
round_votes: Some(0),
|
||||
round_quota: Some(0),
|
||||
sum_surplus_transfers: stv::SumSurplusTransfersMode::SingleStep,
|
||||
quota: stv::QuotaType::Droop,
|
||||
quota_criterion: stv::QuotaCriterion::GreaterOrEqual,
|
||||
quota_mode: stv::QuotaMode::Static,
|
||||
|
|
|
@ -33,6 +33,7 @@ fn ers97_rational() {
|
|||
round_weights: Some(2),
|
||||
round_votes: Some(2),
|
||||
round_quota: Some(2),
|
||||
sum_surplus_transfers: stv::SumSurplusTransfersMode::SingleStep,
|
||||
quota: stv::QuotaType::DroopExact,
|
||||
quota_criterion: stv::QuotaCriterion::GreaterOrEqual,
|
||||
quota_mode: stv::QuotaMode::ERS97,
|
||||
|
|
|
@ -27,6 +27,7 @@ fn prsa1_rational() {
|
|||
round_weights: Some(3),
|
||||
round_votes: Some(3),
|
||||
round_quota: Some(3),
|
||||
sum_surplus_transfers: stv::SumSurplusTransfersMode::SingleStep,
|
||||
quota: stv::QuotaType::Droop,
|
||||
quota_criterion: stv::QuotaCriterion::GreaterOrEqual,
|
||||
quota_mode: stv::QuotaMode::Static,
|
||||
|
|
|
@ -34,6 +34,7 @@ fn scotland_linn07_fixed5() {
|
|||
round_weights: None,
|
||||
round_votes: None,
|
||||
round_quota: Some(0),
|
||||
sum_surplus_transfers: stv::SumSurplusTransfersMode::PerBallot,
|
||||
quota: stv::QuotaType::Droop,
|
||||
quota_criterion: stv::QuotaCriterion::GreaterOrEqual,
|
||||
quota_mode: stv::QuotaMode::Static,
|
||||
|
|
Loading…
Reference in New Issue