Complete implementation of Cambridge STV

Implement --min-threshold
Add test
This commit is contained in:
RunasSudo 2021-08-03 23:22:52 +10:00
parent f182ca02bd
commit 0efc1e6eab
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
19 changed files with 20290 additions and 10 deletions

View File

@ -15,6 +15,7 @@ The preset dropdown allows you to choose from a hardcoded list of preloaded STV
| Australian Senate STV | Rules from the [*Commonwealth Electoral Act 1918*](https://www.legislation.gov.au/Details/C2020C00400/Html/Text#_Toc59107700), using the unweighted inclusive Gregory method. | [E2] [E3] [E4] | ✓ |
| Western Australia STV | Rules from the [*Electoral Act 1907* (WA)](https://www.legislation.wa.gov.au/legislation/prod/filestore.nsf/FileURL/mrdoc_29498.pdf/$FILE/Electoral%20Act%201907%20-%20[17-a0-06].pdf), using the weighted inclusive Gregory method. | [E2] [E3] | |
| Australian Capital Territory STV | Rules from the [*Electoral Act 1992* (ACT)](https://www.legislation.act.gov.au/View/a/1992-71/current/PDF/1992-71.PDF), using the exclusive Gregory method. | | ✓ |
| Cambridge STV | Rules from the former [chapter 54A of the *Massachusetts General Laws*](https://www.cambridgema.gov/-/media/Files/electioncommission/massachusettsgenerallawschapter54a.pdf), as modified and in effect in Cambridge, Massachusetts. See also [here](https://web.archive.org/web/20081118104049/http://www.fairvote.org/media/1993countmanual.pdf). | | ✓ |
| [Wright STV](https://www.aph.gov.au/Parliamentary_Business/Committees/House_of_Representatives_Committees?url=em/elect07/subs/sub051.1.pdf) | Rules proposed by Anthony van der Craats designed for computer counting, involving reset and re-iteration of the count after each candidate exclusion. | | ✓ |
| [PRSA 1977](https://www.prsa.org.au/rule1977.htm) | Simple rules designed for hand counting, using the exclusive Gregory method, with counting performed in thousandths of a vote. | | ✓ |
| [ERS97](https://www.electoral-reform.org.uk/latest-news-and-research/publications/how-to-conduct-an-election-by-the-single-transferable-vote-3rd-edition/) | More complex rules designed for hand counting, using the exclusive Gregory method. | | ✓ |
@ -207,6 +208,10 @@ When *Surplus method* is set to *Meek method*, this option controls when candida
* When immediate election is disabled (default), all current surpluses are distributed and keep values finalised, before any candidates exceeding the quota are then declared elected. This is the method specified in the 1987 Meek rules.
* When immediate election is enabled, a candidate meeting the quota interrupts a surplus distribution. The candidate is immediately declared elected, before the distribution of all surpluses of all now-elected candidates continues. This is the method specified in the 2006 Meek rules.
### Minimum threshold (--min-threshold)
In the first stage when candidates are excluded, all candidates with votes less than or equal to this threshold are excluded at once. The default value is 0, i.e. all candidates with no votes are excluded at once.
## Rounding
### Round quota/votes/surplus fractions/ballot values to [n] d.p. (--round-quota, --round-votes, --round-surplus-fractions, --round-values)

View File

@ -4,16 +4,17 @@ STV-counting software is frequently validated empirically by comparing the resul
| Method | Election | Comparator | Included test case |
|-|-|-|-|
| Scottish STV | [2007 Glasgow council Linn ward election](https://web.archive.org/web/20121004213938/http://www.glasgow.gov.uk/en/YourCouncil/Elections_Voting/Election_Results/ElectionScotland2007/LGWardResults.htm?ward=1&wardname=1%20-%20Linn) | eSTV 2.0.16 | ✓ |
| Scottish STV | [2007 Glasgow council Linn ward election](https://web.archive.org/web/20121004213938/http://www.glasgow.gov.uk/en/YourCouncil/Elections_Voting/Election_Results/ElectionScotland2007/LGWardResults.htm?ward=1&wardname=1%20-%20Linn) | eSTV 2.0.16 (official) | ✓ |
| OpenTally Meek | [Ballot papers derived from the ERS97 model election](https://yingtongli.me/blog/2021/01/04/ers97.html) | [Algorithm 123](https://www.dia.govt.nz/diawebsite.NSF/Files/meekm/%24file/meekm.pdf) | ✓ |
| Meek STV (2006) | Ballot papers derived from the ERS97 model election | [OpenSTV 1.7](https://github.com/Conservatory/openstv) | ✓ |
| Meek STV (New Zealand) | Ballot papers derived from the ERS97 model election | OpenSTV 1.7, [Hill's nzmeek 6.7.7](https://yingtongli.me/blog/2021/07/08/nzmeek.html) | ✓ |
| Australian Senate STV | [2019 Tasmanian Senate election](https://results.aec.gov.au/24310/Website/SenateDownloadsMenu-24310-Csv.htm) | EasyCount | ✓ |
| Australian Senate STV | [2019 NSW Senate election](https://results.aec.gov.au/24310/Website/SenateDownloadsMenu-24310-Csv.htm) | EasyCount | |
| Australian Capital Territory STV | [2020 Kurrajong Legislative Assembly election](https://www.elections.act.gov.au/elections_and_voting/2020_legislative_assembly_election/ballot-paper-preference-data-2020-election) | [eVACS 2020](https://www.elections.act.gov.au/elections_and_voting/electronic_voting_and_counting) | ✓ |
| Wright STV | [EVE Online CSM 15 election](https://www.eveonline.com/news/view/meet-the-new-council) | [ccp-wright-stv](https://github.com/ccpgames/ccp-wright-stv) | ✓ |
| PRSA 1977 | [*Proportional Representation Manual*](https://www.prsa.org.au/publicat.htm#p2) [example 1](https://www.prsa.org.au/utopiatc.pdf) | [Model result](https://www.prsa.org.au/example1.pdf) | ✓ |
| ERS97 | Ballot papers derived from the ERS97 model election | [Model result](https://www.electoral-reform.org.uk/latest-news-and-research/publications/how-to-conduct-an-election-by-the-single-transferable-vote-3rd-edition/#sub-section-24) | ✓ |
| Australian Senate STV | [2019 Tasmanian Senate election](https://results.aec.gov.au/24310/Website/SenateDownloadsMenu-24310-Csv.htm) | EasyCount (official) | ✓ |
| Australian Senate STV | [2019 NSW Senate election](https://results.aec.gov.au/24310/Website/SenateDownloadsMenu-24310-Csv.htm) | EasyCount (official) | |
| Australian Capital Territory STV | [2020 Kurrajong Legislative Assembly election](https://www.elections.act.gov.au/elections_and_voting/2020_legislative_assembly_election/ballot-paper-preference-data-2020-election) | [eVACS 2020](https://www.elections.act.gov.au/elections_and_voting/electronic_voting_and_counting) (official) | ✓ |
| Cambridge STV | [2003 Cambridge City Council election](https://web.archive.org/web/20070204083508/http://stv.sourceforge.net/) | OpenSTV 1.7, [ChoicePlus Pro 2.1](https://www.votingsolutions.com/cpdetail.htm) (official) | ✓ |
| Wright STV | [EVE Online CSM 15 election](https://www.eveonline.com/news/view/meet-the-new-council) | [ccp-wright-stv](https://github.com/ccpgames/ccp-wright-stv) (official) | ✓ |
| PRSA 1977 | [*Proportional Representation Manual*](https://www.prsa.org.au/publicat.htm#p2) [example 1](https://www.prsa.org.au/utopiatc.pdf) | [Model result](https://www.prsa.org.au/example1.pdf) (official) | ✓ |
| ERS97 | Ballot papers derived from the ERS97 model election | [Model result](https://www.electoral-reform.org.uk/latest-news-and-research/publications/how-to-conduct-an-election-by-the-single-transferable-vote-3rd-edition/#sub-section-24) (official) | ✓ |
# References

View File

@ -213,6 +213,10 @@
<span class="pill-grey" title="This option has effect only if “Method” is set to “Meek method”">Meek</span>
Immediate election
</label>
<label class="col-12">
Minimum threshold:
<input type="number" id="txtMinThreshold" value="0" min="0" style="width: 3em;">
</label>
<div class="col-12 subheading">
Rounding:
</div>

View File

@ -153,6 +153,7 @@ async function clickCount() {
document.getElementById('chkBulkExclusion').checked,
document.getElementById('chkDeferSurpluses').checked,
document.getElementById('chkMeekImmediateElect').checked,
document.getElementById('txtMinThreshold').value,
conPath,
"guard_doom",
parseInt(document.getElementById('txtPPDP').value),
@ -365,6 +366,7 @@ function changePreset() {
document.getElementById('chkBulkElection').checked = true;
document.getElementById('chkBulkExclusion').checked = false;
document.getElementById('chkDeferSurpluses').checked = false;
document.getElementById('txtMinThreshold').value = '0';
document.getElementById('selNumbers').value = 'rational';
document.getElementById('txtPPDP').value = '2';
document.getElementById('chkNormaliseBallots').checked = false;
@ -385,6 +387,7 @@ function changePreset() {
document.getElementById('chkBulkElection').checked = true;
document.getElementById('chkBulkExclusion').checked = false;
document.getElementById('chkDeferSurpluses').checked = false;
document.getElementById('txtMinThreshold').value = '0';
document.getElementById('selNumbers').value = 'fixed';
document.getElementById('txtDP').value = '5';
document.getElementById('txtPPDP').value = '5';
@ -410,6 +413,7 @@ function changePreset() {
document.getElementById('chkDeferSurpluses').checked = false;
document.getElementById('chkMeekImmediateElect').checked = false;
document.getElementById('chkMeekNZExclusion').checked = false;
document.getElementById('txtMinThreshold').value = '0';
document.getElementById('selNumbers').value = 'fixed';
document.getElementById('txtDP').value = '5';
document.getElementById('txtPPDP').value = '2';
@ -434,6 +438,7 @@ function changePreset() {
document.getElementById('chkDeferSurpluses').checked = true;
document.getElementById('chkMeekImmediateElect').checked = true;
document.getElementById('chkMeekNZExclusion').checked = false;
document.getElementById('txtMinThreshold').value = '0';
document.getElementById('selNumbers').value = 'fixed';
document.getElementById('txtDP').value = '12';
document.getElementById('txtPPDP').value = '2';
@ -462,6 +467,7 @@ function changePreset() {
document.getElementById('chkDeferSurpluses').checked = true;
document.getElementById('chkMeekImmediateElect').checked = true;
document.getElementById('chkMeekNZExclusion').checked = true;
document.getElementById('txtMinThreshold').value = '0';
document.getElementById('selNumbers').value = 'fixed';
document.getElementById('txtDP').value = '12';
document.getElementById('txtPPDP').value = '2';
@ -488,6 +494,7 @@ function changePreset() {
document.getElementById('chkBulkElection').checked = true;
document.getElementById('chkBulkExclusion').checked = false; // Senate "bulk exclusion" does not permit quota to be exceeded
document.getElementById('chkDeferSurpluses').checked = false;
document.getElementById('txtMinThreshold').value = '0';
document.getElementById('selNumbers').value = 'rational';
document.getElementById('txtPPDP').value = '0';
document.getElementById('chkNormaliseBallots').checked = false;
@ -510,6 +517,7 @@ function changePreset() {
document.getElementById('chkBulkElection').checked = true;
document.getElementById('chkBulkExclusion').checked = false;
document.getElementById('chkDeferSurpluses').checked = false;
document.getElementById('txtMinThreshold').value = '0';
document.getElementById('selNumbers').value = 'rational';
document.getElementById('txtPPDP').value = '0';
document.getElementById('chkNormaliseBallots').checked = false;
@ -532,6 +540,7 @@ function changePreset() {
document.getElementById('chkBulkElection').checked = true;
document.getElementById('chkBulkExclusion').checked = false;
document.getElementById('chkDeferSurpluses').checked = false;
document.getElementById('txtMinThreshold').value = '0';
document.getElementById('selNumbers').value = 'rational';
document.getElementById('txtPPDP').value = '2';
document.getElementById('chkNormaliseBallots').checked = false;
@ -554,6 +563,7 @@ function changePreset() {
document.getElementById('chkBulkElection').checked = true;
document.getElementById('chkBulkExclusion').checked = false; // TODO: Cambridge-style bulk exclusion
document.getElementById('chkDeferSurpluses').checked = false;
document.getElementById('txtMinThreshold').value = '49';
document.getElementById('selNumbers').value = 'rational';
document.getElementById('txtPPDP').value = '0';
document.getElementById('chkNormaliseBallots').checked = true;
@ -571,6 +581,7 @@ function changePreset() {
document.getElementById('chkBulkElection').checked = true;
document.getElementById('chkBulkExclusion').checked = true;
document.getElementById('chkDeferSurpluses').checked = false;
document.getElementById('txtMinThreshold').value = '0';
document.getElementById('selNumbers').value = 'fixed';
document.getElementById('txtDP').value = '5';
document.getElementById('txtPPDP').value = '2';
@ -593,6 +604,7 @@ function changePreset() {
document.getElementById('chkBulkElection').checked = true;
document.getElementById('chkBulkExclusion').checked = false;
document.getElementById('chkDeferSurpluses').checked = true;
document.getElementById('txtMinThreshold').value = '0';
document.getElementById('selNumbers').value = 'fixed';
document.getElementById('txtDP').value = '6';
document.getElementById('txtPPDP').value = '3';
@ -618,6 +630,7 @@ function changePreset() {
document.getElementById('chkBulkElection').checked = true;
document.getElementById('chkBulkExclusion').checked = true;
document.getElementById('chkDeferSurpluses').checked = true;
document.getElementById('txtMinThreshold').value = '0';
document.getElementById('selNumbers').value = 'fixed';
document.getElementById('txtDP').value = '5';
document.getElementById('txtPPDP').value = '2';
@ -643,6 +656,7 @@ function changePreset() {
document.getElementById('chkBulkElection').checked = true;
document.getElementById('chkBulkExclusion').checked = true;
document.getElementById('chkDeferSurpluses').checked = true;
document.getElementById('txtMinThreshold').value = '0';
document.getElementById('selNumbers').value = 'fixed';
document.getElementById('txtDP').value = '5';
document.getElementById('txtPPDP').value = '2';
@ -668,6 +682,7 @@ function changePreset() {
document.getElementById('chkBulkElection').checked = true;
document.getElementById('chkBulkExclusion').checked = true;
document.getElementById('chkDeferSurpluses').checked = true;
document.getElementById('txtMinThreshold').value = '0';
document.getElementById('selNumbers').value = 'fixed';
document.getElementById('txtDP').value = '5';
document.getElementById('txtPPDP').value = '2';
@ -693,6 +708,7 @@ function changePreset() {
document.getElementById('chkBulkElection').checked = true;
document.getElementById('chkBulkExclusion').checked = false;
document.getElementById('chkDeferSurpluses').checked = true;
document.getElementById('txtMinThreshold').value = '0';
document.getElementById('selNumbers').value = 'fixed';
document.getElementById('txtDP').value = '5';
document.getElementById('txtPPDP').value = '2';

View File

@ -69,11 +69,11 @@ struct STV {
// -- Rounding settings --
/// Round surplus fractions to specified decimal places
#[clap(help_heading=Some("ROUNDING"), long, alias="round_tvs", value_name="dps")]
#[clap(help_heading=Some("ROUNDING"), long, alias="round-tvs", value_name="dps")]
round_surplus_fractions: Option<usize>,
/// Round ballot values to specified decimal places
#[clap(help_heading=Some("ROUNDING"), long, alias="round_weights", value_name="dps")]
#[clap(help_heading=Some("ROUNDING"), long, alias="round-weights", value_name="dps")]
round_values: Option<usize>,
/// Round votes to specified decimal places
@ -157,6 +157,10 @@ struct STV {
#[clap(help_heading=Some("COUNT OPTIMISATIONS"), long)]
meek_immediate_elect: bool,
/// On exclusion, exclude any candidate with fewer than this many votes
#[clap(help_heading=Some("COUNT OPTIMISATIONS"), long, default_value="0", value_name="votes")]
min_threshold: String,
// -----------------
// -- Constraints --
@ -274,6 +278,7 @@ where
cmd_opts.bulk_exclude,
cmd_opts.defer_surpluses,
cmd_opts.meek_immediate_elect,
cmd_opts.min_threshold,
cmd_opts.constraints.as_deref(),
&cmd_opts.constraint_mode,
cmd_opts.hide_excluded,

View File

@ -83,6 +83,8 @@ pub struct STVOptions {
pub defer_surpluses: bool,
/// (Meek STV) Immediately elect candidates even if keep values have not converged
pub meek_immediate_elect: bool,
/// On exclusion, exclude any candidate with this many votes or fewer
pub min_threshold: String,
/// Path to constraints file (used only for [STVOptions::describe])
pub constraints_path: Option<String>,
/// Mode of handling constraints
@ -119,6 +121,7 @@ impl STVOptions {
bulk_exclude: bool,
defer_surpluses: bool,
meek_immediate_elect: bool,
min_threshold: String,
constraints_path: Option<&str>,
constraint_mode: &str,
hide_excluded: bool,
@ -191,6 +194,7 @@ impl STVOptions {
bulk_exclude,
defer_surpluses,
meek_immediate_elect,
min_threshold,
constraints_path: match constraints_path {
Some(p) => Some(p.to_string()),
None => None,
@ -234,6 +238,7 @@ impl STVOptions {
if self.bulk_exclude { flags.push("--bulk-exclude".to_string()); }
if self.defer_surpluses { flags.push("--defer-surpluses".to_string()); }
if self.surplus == SurplusMethod::Meek && self.meek_immediate_elect { flags.push("--meek-immediate-elect".to_string()); }
if self.min_threshold != "1" { flags.push(format!("--min-threshold {}", self.min_threshold)); }
if let Some(path) = &self.constraints_path {
flags.push(format!("--constraints {}", path));
if self.constraint_mode != ConstraintMode::GuardDoom { flags.push(self.constraint_mode.describe()); }
@ -255,6 +260,7 @@ impl STVOptions {
if self.quota_criterion != QuotaCriterion::GreaterOrEqual { return Err(STVError::InvalidOptions("--surplus cincinnati and --surplus hare require --quota-criterion geq")); }
if !self.normalise_ballots { return Err(STVError::InvalidOptions("--surplus cincinnati and --surplus hare require --normalise-ballots")); }
}
if self.min_threshold != "0" && self.defer_surpluses { return Err(STVError::InvalidOptions("--min-threshold is incompatible with --defer-surpluses")); } // TODO: Permit this
return Ok(());
}
}
@ -1135,6 +1141,26 @@ where
return Ok(false);
}
/// Determine which continuing candidates have votes equal to or below the minimum threshold
fn hopefuls_below_threshold<'a, N: Number>(state: &CountState<'a, N>, opts: &STVOptions) -> Vec<&'a Candidate> {
let min_threshold = N::parse(&opts.min_threshold);
let excluded_candidates: Vec<&Candidate> = state.candidates.iter()
.filter_map(|(c, cc)|
if cc.state == CandidateState::Hopeful && cc.votes <= min_threshold {
Some(*c)
} else {
None
})
.collect();
// Do not exclude if this violates constraints
match constraints::try_constraints(state, &excluded_candidates, CandidateState::Excluded) {
Ok(_) => { return excluded_candidates; }
Err(_) => { return Vec::new(); } // Bulk exclusion conflicts with constraints
}
}
/// Determine which continuing candidates could be excluded in a bulk exclusion
///
/// The value of [STVOptions::bulk_exclude] is not taken into account and must be handled by the caller
@ -1192,8 +1218,13 @@ where
{
let mut excluded_candidates: Vec<&Candidate> = Vec::new();
// Exclude candidates below min threshold
if state.num_excluded == 0 {
excluded_candidates = hopefuls_below_threshold(state, opts);
}
// Attempt a bulk exclusion
if opts.bulk_exclude {
if excluded_candidates.is_empty() && opts.bulk_exclude {
excluded_candidates = hopefuls_to_bulk_exclude(state, opts);
}

View File

@ -232,6 +232,7 @@ impl STVOptions {
bulk_exclude: bool,
defer_surpluses: bool,
meek_immediate_elect: bool,
min_threshold: String,
constraints_path: Option<String>,
constraint_mode: &str,
pp_decimals: usize,
@ -258,6 +259,7 @@ impl STVOptions {
bulk_exclude,
defer_surpluses,
meek_immediate_elect,
min_threshold,
constraints_path.as_deref(),
constraint_mode,
false,

View File

@ -43,6 +43,7 @@ fn act_kurrajong20_rational() {
bulk_exclude: false,
defer_surpluses: false,
meek_immediate_elect: false,
min_threshold: "0".to_string(),
constraints_path: None,
constraint_mode: stv::ConstraintMode::GuardDoom,
hide_excluded: false,

View File

@ -80,6 +80,7 @@ fn aec_tas19_rational() {
bulk_exclude: true,
defer_surpluses: false,
meek_immediate_elect: false,
min_threshold: "0".to_string(),
constraints_path: None,
constraint_mode: stv::ConstraintMode::GuardDoom,
hide_excluded: false,

54
tests/cambridge.rs Normal file
View File

@ -0,0 +1,54 @@
/* OpenTally: Open-source election vote counting
* Copyright © 2021 Lee Yingtong Li (RunasSudo)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
mod utils;
use opentally::numbers::Rational;
use opentally::stv;
#[test]
fn cambridge_cc03_rational() {
let stv_opts = stv::STVOptions {
round_surplus_fractions: None,
round_values: None,
round_votes: None,
round_quota: Some(0),
sum_surplus_transfers: stv::SumSurplusTransfersMode::SingleStep,
meek_surplus_tolerance: String::new(),
normalise_ballots: true,
quota: stv::QuotaType::Droop,
quota_criterion: stv::QuotaCriterion::GreaterOrEqual,
quota_mode: stv::QuotaMode::Static,
ties: vec![],
surplus: stv::SurplusMethod::Cincinnati,
surplus_order: stv::SurplusOrder::ByOrder,
transferable_only: true,
exclusion: stv::ExclusionMethod::SingleStage,
meek_nz_exclusion: false,
early_bulk_elect: false,
bulk_exclude: false,
defer_surpluses: false,
meek_immediate_elect: false,
min_threshold: "49".to_string(),
constraints_path: None,
constraint_mode: stv::ConstraintMode::GuardDoom,
hide_excluded: false,
sort_votes: false,
pp_decimals: 2,
};
utils::read_validate_election::<Rational>("tests/data/CambCC2003.csv", "tests/data/CambCC2003.blt", stv_opts, None, &["exhausted"]);
}

View File

@ -59,6 +59,7 @@ fn prsa1_constr1_rational() {
bulk_exclude: false,
defer_surpluses: false,
meek_immediate_elect: false,
min_threshold: "0".to_string(),
constraints_path: Some("tests/data/prsa1_constr1.con".to_string()),
constraint_mode: stv::ConstraintMode::GuardDoom,
hide_excluded: false,
@ -122,6 +123,7 @@ fn prsa1_constr2_rational() {
bulk_exclude: false,
defer_surpluses: false,
meek_immediate_elect: false,
min_threshold: "0".to_string(),
constraints_path: Some("tests/data/prsa1_constr2.con".to_string()),
constraint_mode: stv::ConstraintMode::GuardDoom,
hide_excluded: false,
@ -185,6 +187,7 @@ fn prsa1_constr3_rational() {
bulk_exclude: false,
defer_surpluses: false,
meek_immediate_elect: false,
min_threshold: "0".to_string(),
constraints_path: Some("tests/data/prsa1_constr2.con".to_string()),
constraint_mode: stv::ConstraintMode::GuardDoom,
hide_excluded: false,
@ -260,6 +263,7 @@ fn ers97_cantbulkexclude_rational() {
bulk_exclude: true,
defer_surpluses: true,
meek_immediate_elect: false,
min_threshold: "0".to_string(),
constraints_path: Some("tests/data/ers97_cantbulkexclude".to_string()),
constraint_mode: stv::ConstraintMode::GuardDoom,
hide_excluded: false,

View File

@ -43,6 +43,7 @@ fn csm15_float64() {
bulk_exclude: true,
defer_surpluses: false,
meek_immediate_elect: false,
min_threshold: "0".to_string(),
constraints_path: None,
constraint_mode: stv::ConstraintMode::GuardDoom,
hide_excluded: false,

20116
tests/data/CambCC2003.blt Normal file

File diff suppressed because it is too large Load Diff

32
tests/data/CambCC2003.csv Normal file
View File

@ -0,0 +1,32 @@
Stage:,1,,2,,3,,4,,5,,6,,7,,8,,9,,10,,11,,12,,13,
Comment:,First preferences,,"Surplus of Galluccio, Anthony D.",,,,"Exclusion of Dixon, Vincent Lawrence",,"Exclusion of Hall, Robert L., Sr.",,"Exclusion of LaTremouille, Robert J.",,"Exclusion of Taymorberry, Laurie",,"Exclusion of King, Ethridge A., Jr.",,"Exclusion of Smith, Aimee Louise",,"Exclusion of Bellew, Carole K.",,"Exclusion of Kelley, Craig A.",,"Exclusion of Pitkin, John",,"Exclusion of DeBergalis, Matt S.",
"Bellew, Carole K.",735,H,759,H,761,H,762,H,767,H,771,H,788,H,811,H,861,H,0,EX,0,EX,0,EX,0,EX
"Davis, Henrietta",1846,H,1896,H,1901,H,1904,H,1909,H,1920,H,1950,H,1977,H,2009,EL,2009,EL,2009,EL,2009,EL,2009,EL
"DeBergalis, Matt S.",1206,H,1216,H,1220,H,1227,H,1229,H,1233,H,1242,H,1283,H,1342,H,1441,H,1494,H,1640,H,0,EX
"Decker, Marjorie C.",1378,H,1425,H,1428,H,1432,H,1437,H,1442,H,1463,H,1486,H,1528,H,1641,H,1787,H,2009,EL,2009,EL
"Dixon, Vincent Lawrence",64,H,64,H,65,H,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX
"Galluccio, Anthony D.",2994,EL,2009,EL,2009,EL,2009,EL,2009,EL,2009,EL,2009,EL,2009,EL,2009,EL,2009,EL,2009,EL,2009,EL,2009,EL
"Greenwood, Dan J.",39,H,41,H,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX
"Hall, Robert L., Sr.",96,H,101,H,114,H,123,H,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX
"Kelley, Craig A.",992,H,1013,H,1016,H,1021,H,1051,H,1062,H,1070,H,1094,H,1118,H,1181,H,0,EX,0,EX,0,EX
"King, Ethridge A., Jr.",361,H,372,H,377,H,381,H,387,H,399,H,406,H,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX
"LaTremouille, Robert J.",126,H,131,H,133,H,136,H,142,H,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX
"Maher, David P.",1190,H,1430,H,1433,H,1435,H,1444,H,1448,H,1453,H,1499,H,1505,H,1573,H,1698,H,1839,H,2009,EL
"Murphy, Brian",1362,H,1396,H,1398,H,1401,H,1403,H,1410,H,1423,H,1446,H,1479,H,1595,H,1742,H,2009,EL,2009,EL
"Pitkin, John",1010,H,1032,H,1034,H,1037,H,1044,H,1057,H,1066,H,1091,H,1130,H,1223,H,1493,H,0,EX,0,EX
"Reeves, Kenneth E.",1525,H,1602,H,1607,H,1611,H,1618,H,1630,H,1637,H,1667,H,1695,H,1735,H,1807,H,1898,H,2009,EL
"Simmons, Denise",1181,H,1232,H,1233,H,1239,H,1250,H,1259,H,1274,H,1299,H,1408,H,1479,H,1569,H,1777,H,2009,EL
"Smith, Aimee Louise",480,H,484,H,489,H,490,H,491,H,499,H,517,H,527,H,0,EX,0,EX,0,EX,0,EX,0,EX
"Sullivan, Michael A.",1656,H,1893,H,1895,H,1900,H,1905,H,1910,H,1918,H,1953,H,1976,H,2009,EL,2009,EL,2009,EL,2009,EL
"Taymorberry, Laurie",188,H,193,H,193,H,194,H,197,H,208,H,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX
"Toomey, Timothy J., Jr.",1613,H,1753,H,1753,H,1755,H,1757,H,1764,H,1776,H,1790,H,1805,H,1859,H,1921,H,2009,EL,2009,EL
Write-In 1,36,H,36,H,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX
Write-In 2,2,H,2,H,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX
Write-In 3,0,H,0,H,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX
Write-In 4,0,H,0,H,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX
Write-In 5,0,H,0,H,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX
Write-In 6,0,H,0,H,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX
Write-In 7,0,H,0,H,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX
Write-In 8,0,H,0,H,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX
Write-In 9,0,H,0,H,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX,0,EX
Exhausted,0,,0,,21,,23,,40,,59,,88,,148,,215,,326,,542,,872,,1999,
1 Stage: 1 2 3 4 5 6 7 8 9 10 11 12 13
2 Comment: First preferences Surplus of Galluccio, Anthony D. Exclusion of Dixon, Vincent Lawrence Exclusion of Hall, Robert L., Sr. Exclusion of LaTremouille, Robert J. Exclusion of Taymorberry, Laurie Exclusion of King, Ethridge A., Jr. Exclusion of Smith, Aimee Louise Exclusion of Bellew, Carole K. Exclusion of Kelley, Craig A. Exclusion of Pitkin, John Exclusion of DeBergalis, Matt S.
3 Bellew, Carole K. 735 H 759 H 761 H 762 H 767 H 771 H 788 H 811 H 861 H 0 EX 0 EX 0 EX 0 EX
4 Davis, Henrietta 1846 H 1896 H 1901 H 1904 H 1909 H 1920 H 1950 H 1977 H 2009 EL 2009 EL 2009 EL 2009 EL 2009 EL
5 DeBergalis, Matt S. 1206 H 1216 H 1220 H 1227 H 1229 H 1233 H 1242 H 1283 H 1342 H 1441 H 1494 H 1640 H 0 EX
6 Decker, Marjorie C. 1378 H 1425 H 1428 H 1432 H 1437 H 1442 H 1463 H 1486 H 1528 H 1641 H 1787 H 2009 EL 2009 EL
7 Dixon, Vincent Lawrence 64 H 64 H 65 H 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX
8 Galluccio, Anthony D. 2994 EL 2009 EL 2009 EL 2009 EL 2009 EL 2009 EL 2009 EL 2009 EL 2009 EL 2009 EL 2009 EL 2009 EL 2009 EL
9 Greenwood, Dan J. 39 H 41 H 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX
10 Hall, Robert L., Sr. 96 H 101 H 114 H 123 H 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX
11 Kelley, Craig A. 992 H 1013 H 1016 H 1021 H 1051 H 1062 H 1070 H 1094 H 1118 H 1181 H 0 EX 0 EX 0 EX
12 King, Ethridge A., Jr. 361 H 372 H 377 H 381 H 387 H 399 H 406 H 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX
13 LaTremouille, Robert J. 126 H 131 H 133 H 136 H 142 H 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX
14 Maher, David P. 1190 H 1430 H 1433 H 1435 H 1444 H 1448 H 1453 H 1499 H 1505 H 1573 H 1698 H 1839 H 2009 EL
15 Murphy, Brian 1362 H 1396 H 1398 H 1401 H 1403 H 1410 H 1423 H 1446 H 1479 H 1595 H 1742 H 2009 EL 2009 EL
16 Pitkin, John 1010 H 1032 H 1034 H 1037 H 1044 H 1057 H 1066 H 1091 H 1130 H 1223 H 1493 H 0 EX 0 EX
17 Reeves, Kenneth E. 1525 H 1602 H 1607 H 1611 H 1618 H 1630 H 1637 H 1667 H 1695 H 1735 H 1807 H 1898 H 2009 EL
18 Simmons, Denise 1181 H 1232 H 1233 H 1239 H 1250 H 1259 H 1274 H 1299 H 1408 H 1479 H 1569 H 1777 H 2009 EL
19 Smith, Aimee Louise 480 H 484 H 489 H 490 H 491 H 499 H 517 H 527 H 0 EX 0 EX 0 EX 0 EX 0 EX
20 Sullivan, Michael A. 1656 H 1893 H 1895 H 1900 H 1905 H 1910 H 1918 H 1953 H 1976 H 2009 EL 2009 EL 2009 EL 2009 EL
21 Taymorberry, Laurie 188 H 193 H 193 H 194 H 197 H 208 H 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX
22 Toomey, Timothy J., Jr. 1613 H 1753 H 1753 H 1755 H 1757 H 1764 H 1776 H 1790 H 1805 H 1859 H 1921 H 2009 EL 2009 EL
23 Write-In 1 36 H 36 H 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX
24 Write-In 2 2 H 2 H 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX
25 Write-In 3 0 H 0 H 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX
26 Write-In 4 0 H 0 H 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX
27 Write-In 5 0 H 0 H 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX
28 Write-In 6 0 H 0 H 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX
29 Write-In 7 0 H 0 H 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX
30 Write-In 8 0 H 0 H 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX
31 Write-In 9 0 H 0 H 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX 0 EX
32 Exhausted 0 0 21 23 40 59 88 148 215 326 542 872 1999

BIN
tests/data/CambCC2003.ods Normal file

Binary file not shown.

View File

@ -43,6 +43,7 @@ fn ers97_rational() {
bulk_exclude: true,
defer_surpluses: true,
meek_immediate_elect: false,
min_threshold: "0".to_string(),
constraints_path: None,
constraint_mode: stv::ConstraintMode::GuardDoom,
hide_excluded: false,

View File

@ -45,6 +45,7 @@ fn meek87_ers97_float64() {
bulk_exclude: false,
defer_surpluses: false,
meek_immediate_elect: false,
min_threshold: "0".to_string(),
constraints_path: None,
constraint_mode: stv::ConstraintMode::GuardDoom,
hide_excluded: false,
@ -78,6 +79,7 @@ fn meek06_ers97_fixed12() {
bulk_exclude: false,
defer_surpluses: true,
meek_immediate_elect: true,
min_threshold: "0".to_string(),
constraints_path: None,
constraint_mode: stv::ConstraintMode::GuardDoom,
hide_excluded: false,
@ -152,6 +154,7 @@ fn meeknz_ers97_fixed12() {
bulk_exclude: false,
defer_surpluses: true,
meek_immediate_elect: true,
min_threshold: "0".to_string(),
constraints_path: None,
constraint_mode: stv::ConstraintMode::GuardDoom,
hide_excluded: false,

View File

@ -43,6 +43,7 @@ fn prsa1_rational() {
bulk_exclude: false,
defer_surpluses: false,
meek_immediate_elect: false,
min_threshold: "0".to_string(),
constraints_path: None,
constraint_mode: stv::ConstraintMode::GuardDoom,
hide_excluded: false,

View File

@ -47,6 +47,7 @@ fn scotland_linn07_fixed5() {
bulk_exclude: false,
defer_surpluses: false,
meek_immediate_elect: false,
min_threshold: "0".to_string(),
constraints_path: None,
constraint_mode: stv::ConstraintMode::GuardDoom,
hide_excluded: false,
@ -80,6 +81,7 @@ fn scotland_linn07_gfixed5() {
bulk_exclude: false,
defer_surpluses: false,
meek_immediate_elect: false,
min_threshold: "0".to_string(),
constraints_path: None,
constraint_mode: stv::ConstraintMode::GuardDoom,
hide_excluded: false,