From cd42899ba82d771487fe6e84d77fd9ef8533cbcc Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Sun, 20 Jun 2021 01:37:51 +1000 Subject: [PATCH] Add test case for NZ Meek STV --- docs/options.md | 3 +- tests/aec.rs | 1 + tests/ers97.rs | 1 + tests/meek.rs | 77 ++++++++++++++++++++++++++++++++++++++++++++++- tests/prsa.rs | 1 + tests/scotland.rs | 2 ++ 6 files changed, 83 insertions(+), 2 deletions(-) diff --git a/docs/options.md b/docs/options.md index 4f1b184..76ac60b 100644 --- a/docs/options.md +++ b/docs/options.md @@ -8,7 +8,8 @@ The preset dropdown allows you to choose from a hardcoded list of preloaded STV * *Scottish STV*: Rules from the [*Scottish Local Government Elections Order 2011*](https://www.legislation.gov.uk/ssi/2011/399/schedule/1/made), using the weighted inclusive Gregory method. Validated against the [2007 Scottish local government election result for Linn ward](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). * [*Meek STV*](http://www.dia.govt.nz/diawebsite.NSF/Files/meekm/%24file/meekm.pdf): Advanced STV rules designed for computer counting, recognised by the Proportional Representation Society of Australia (Victoria–Tasmania) as the superior STV system. * *Meek STV (1987)* operates according to the original [Hill–Wichmann–Woodall specification](https://www.dia.govt.nz/diawebsite.NSF/Files/meekm/%24file/meekm.pdf) of Meek STV, with the modifications, relevant only in exceptional cases, that (a) fixed-point arithmetic with 5 decimal places is used, and (b) candidates are elected on strictly exceeding the quota. Validated against the Hill–Wichmann–Woodall implementation for the [ERS97 model election](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). - * *Meek STV (2006)* operates according to [Hill's 2006 revisions](http://www.votingmatters.org.uk/ISSUE22/I22P2.pdf). This is the algorithm referred to in OpenSTV/OpaVote as ‘Meek STV’, and forms the basis of New Zealand's Meek STV rules. Validated against OpenSTV for the ERS97 model election. + * *Meek STV (2006)* operates according to [Hill's 2006 revisions](http://www.votingmatters.org.uk/ISSUE22/I22P2.pdf). This is the algorithm referred to in OpenSTV/OpaVote as ‘Meek STV’, and forms the basis of New Zealand's Meek STV rules. Validated against OpenSTV 1.7 for the ERS97 model election. + * *Meek STV (New Zealand)* operates according to Schedule 1A of the [*Local Electoral Regulations 2001*](https://www.legislation.govt.nz/regulation/public/2001/0145/latest/DLM57125.html). Validated against OpenSTV 1.7 for the ERS97 model election. * *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. Validated against the [2019 Australian Senate election result for Tasmania](https://results.aec.gov.au/24310/Website/SenateDownloadsMenu-24310-Csv.htm). * [*PRSA 1977*](https://www.prsa.org.au/rule1977.htm): Simple rules designed for hand counting, using the exclusive Gregory method, with counting automatically performed in thousandths of a vote. Validated against [example 1](https://www.prsa.org.au/example1.pdf) of the PRSA's [*Proportional Representation Manual*](https://www.prsa.org.au/publicat.htm#p2). * [*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. Validated against the ERS97 model election. diff --git a/tests/aec.rs b/tests/aec.rs index 3839970..8ac4b09 100644 --- a/tests/aec.rs +++ b/tests/aec.rs @@ -70,6 +70,7 @@ fn aec_tas19_rational() { surplus_order: stv::SurplusOrder::ByOrder, transferable_only: false, exclusion: stv::ExclusionMethod::ByValue, + meek_nz_exclusion: false, bulk_exclude: true, defer_surpluses: false, meek_immediate_elect: false, diff --git a/tests/ers97.rs b/tests/ers97.rs index 4822f05..053570e 100644 --- a/tests/ers97.rs +++ b/tests/ers97.rs @@ -38,6 +38,7 @@ fn ers97_rational() { surplus_order: stv::SurplusOrder::BySize, transferable_only: true, exclusion: stv::ExclusionMethod::ByValue, + meek_nz_exclusion: false, bulk_exclude: true, defer_surpluses: true, meek_immediate_elect: false, diff --git a/tests/meek.rs b/tests/meek.rs index 8abd2a2..a763a29 100644 --- a/tests/meek.rs +++ b/tests/meek.rs @@ -43,6 +43,7 @@ fn meek87_ers97_float64() { surplus_order: stv::SurplusOrder::BySize, transferable_only: false, exclusion: stv::ExclusionMethod::SingleStage, + meek_nz_exclusion: false, bulk_exclude: false, defer_surpluses: false, meek_immediate_elect: false, @@ -51,7 +52,7 @@ fn meek87_ers97_float64() { utils::read_validate_election::("tests/data/ers97_meek.csv", "tests/data/ers97.blt", stv_opts, Some(2), &["exhausted", "quota"]); } -// Compare ers97.blt count with result produced by OpenSTV "Meek STV" +// Compare ers97.blt count with result produced by OpenSTV 1.7 "Meek STV" #[test] fn meek06_ers97_fixed12() { let stv_opts = stv::STVOptions { @@ -70,6 +71,7 @@ fn meek06_ers97_fixed12() { surplus_order: stv::SurplusOrder::BySize, transferable_only: false, exclusion: stv::ExclusionMethod::SingleStage, + meek_nz_exclusion: false, bulk_exclude: false, defer_surpluses: true, meek_immediate_elect: true, @@ -122,3 +124,76 @@ fn meek06_ers97_fixed12() { } } } + +// Compare ers97.blt count with result produced by OpenSTV 1.7 "New Zealand Meek STV" +#[test] +fn meeknz_ers97_fixed12() { + let stv_opts = stv::STVOptions { + round_tvs: Some(9), + round_weights: Some(9), + round_votes: Some(9), + round_quota: Some(9), + sum_surplus_transfers: stv::SumSurplusTransfersMode::SingleStep, + meek_surplus_tolerance: String::from("0.0001"), + normalise_ballots: false, + quota: stv::QuotaType::Droop, + quota_criterion: stv::QuotaCriterion::GreaterOrEqual, + quota_mode: stv::QuotaMode::Static, + ties: vec![], + surplus: stv::SurplusMethod::Meek, + surplus_order: stv::SurplusOrder::BySize, + transferable_only: false, + exclusion: stv::ExclusionMethod::SingleStage, + meek_nz_exclusion: true, + bulk_exclude: false, + defer_surpluses: true, + meek_immediate_elect: true, + pp_decimals: 2, + }; + Fixed::set_dps(12); + + // Read BLT + let file = File::open("tests/data/ers97.blt").expect("IO Error"); + let file_reader = io::BufReader::new(file); + let lines = file_reader.lines(); + + let election: Election = Election::from_blt(lines.map(|r| r.expect("IO Error").to_string()).into_iter()); + + // Initialise count state + let mut state = CountState::new(&election); + + // Count to completion + stv::count_init(&mut state, &stv_opts); + while !stv::count_one_stage(&mut state, &stv_opts).unwrap() {} + + // Check states and keep values + for (candidate, count_card) in state.candidates.iter() { + match candidate.name.as_str() { + "Smith" => { + assert!(count_card.state == CandidateState::Elected); + assert_eq!(count_card.keep_value, Some(Fixed::parse("0.452154292"))); + } + "Carpenter" => { + assert!(count_card.state == CandidateState::Elected); + assert_eq!(count_card.keep_value, Some(Fixed::parse("0.614574172"))); + } + "Duke" => { + assert!(count_card.state == CandidateState::Elected); + assert_eq!(count_card.keep_value, Some(Fixed::parse("0.610780881"))); + } + "Prince" => { + assert!(count_card.state == CandidateState::Elected); + assert_eq!(count_card.keep_value, Some(Fixed::parse("0.741808717"))); + } + "Freeman" => { + assert!(count_card.state == CandidateState::Elected); + assert_eq!(count_card.keep_value, Some(Fixed::parse("0.715384286"))); + } + "Vicar" => { + assert!(count_card.state == CandidateState::Elected); + assert_eq!(count_card.keep_value, Some(Fixed::parse("1.0"))); + } + _ => {} + } + } +} diff --git a/tests/prsa.rs b/tests/prsa.rs index 9c00a9d..751c32e 100644 --- a/tests/prsa.rs +++ b/tests/prsa.rs @@ -38,6 +38,7 @@ fn prsa1_rational() { surplus_order: stv::SurplusOrder::ByOrder, transferable_only: true, exclusion: stv::ExclusionMethod::ParcelsByOrder, + meek_nz_exclusion: false, bulk_exclude: false, defer_surpluses: false, meek_immediate_elect: false, diff --git a/tests/scotland.rs b/tests/scotland.rs index d9bbfdf..5dbce94 100644 --- a/tests/scotland.rs +++ b/tests/scotland.rs @@ -45,6 +45,7 @@ fn scotland_linn07_fixed5() { surplus_order: stv::SurplusOrder::BySize, transferable_only: false, exclusion: stv::ExclusionMethod::SingleStage, + meek_nz_exclusion: false, bulk_exclude: false, defer_surpluses: false, meek_immediate_elect: false, @@ -72,6 +73,7 @@ fn scotland_linn07_gfixed5() { surplus_order: stv::SurplusOrder::BySize, transferable_only: false, exclusion: stv::ExclusionMethod::SingleStage, + meek_nz_exclusion: false, bulk_exclude: false, defer_surpluses: false, meek_immediate_elect: false,