From ab3067566d5c245f9a56a25f6a5515f0bd24b6f0 Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Thu, 9 Sep 2021 13:36:27 +1000 Subject: [PATCH] Fix bug attempting to defer surplus with 0 or 1 continuing candidates Add regression test --- src/stv/mod.rs | 5 +++ tests/data/A34.blt | 70 ++++++++++++++++++++++++++++++++++++++++++ tests/special_cases.rs | 37 +++++++++++++++++++++- 3 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 tests/data/A34.blt diff --git a/src/stv/mod.rs b/src/stv/mod.rs index 409fb06..9756b19 100644 --- a/src/stv/mod.rs +++ b/src/stv/mod.rs @@ -1114,6 +1114,11 @@ where let mut hopefuls: Vec<(&&Candidate, &CountCard)> = state.candidates.iter() .filter(|(_, cc)| cc.state == CandidateState::Hopeful || cc.state == CandidateState::Guarded) .collect(); + + if hopefuls.len() < 2 { + return true; + } + hopefuls.sort_unstable_by(|(_, cc1), (_, cc2)| cc1.votes.cmp(&cc2.votes)); if total_surpluses > &(&hopefuls[1].1.votes - &hopefuls[0].1.votes) { return false; diff --git a/tests/data/A34.blt b/tests/data/A34.blt new file mode 100644 index 0000000..ac17ff9 --- /dev/null +++ b/tests/data/A34.blt @@ -0,0 +1,70 @@ +# Comment: BY STV +# Comment: Number of truncated papers: 0 +# Comment: AKA R039 in Wichmann's database +# Source: Nicolaus Tideman via Warren D Smith +14 12 +1 9 8 4 5 0 +1 9 12 1 14 4 10 0 +1 14 1 2 10 5 7 11 13 3 4 12 0 +1 2 7 10 6 0 +1 7 6 0 +1 7 11 10 1 5 6 14 2 8 4 12 3 0 +1 7 8 10 6 12 14 4 11 0 +1 11 4 10 3 13 8 14 2 12 7 6 1 0 +1 11 3 7 10 4 13 2 1 6 14 8 12 0 +1 11 1 13 12 9 10 6 5 4 3 2 7 0 +1 11 14 12 10 1 6 7 8 4 0 +1 14 3 4 5 12 13 6 9 0 +1 2 3 11 7 8 14 9 13 0 +1 3 2 7 11 6 5 14 1 12 8 4 10 0 +1 3 8 12 1 13 2 14 4 11 10 7 6 0 +1 3 11 4 5 8 12 10 13 0 +1 3 11 10 13 4 1 2 5 14 12 7 8 6 0 +1 5 2 1 8 12 10 0 +1 5 4 1 2 6 12 10 8 13 14 3 7 0 +1 5 8 6 7 10 12 1 4 11 0 +1 2 6 8 4 5 10 12 14 7 1 11 3 0 +1 2 6 14 10 4 1 8 13 5 12 0 +1 6 2 10 8 7 11 12 1 14 3 0 +1 6 14 8 4 5 12 1 0 +1 6 11 14 7 1 13 8 2 3 10 12 4 5 9 0 +1 5 10 13 12 2 6 4 11 7 1 8 0 +1 10 8 13 4 1 7 14 11 6 12 5 2 3 0 +1 13 8 12 5 14 4 0 +1 13 12 8 4 0 +1 13 10 6 4 14 8 11 3 1 7 2 12 5 0 +1 13 10 6 4 14 8 11 3 1 7 2 12 5 0 +1 14 12 13 10 5 4 6 2 11 8 7 1 3 9 0 +1 14 8 10 2 1 12 11 6 5 7 3 4 0 +1 14 12 2 5 8 10 1 11 6 13 4 3 0 +1 5 8 14 2 12 6 4 0 +1 5 12 6 2 1 4 7 10 11 9 8 3 14 13 0 +1 5 8 14 2 12 4 6 0 +1 10 12 8 13 3 7 6 5 4 11 14 1 0 +1 10 2 14 12 8 6 5 11 13 1 4 3 7 9 0 +1 2 5 12 13 14 8 1 7 10 11 3 6 0 +1 12 10 13 4 1 11 14 6 0 +1 12 10 6 13 11 3 2 1 4 14 8 0 +1 10 1 8 11 7 6 4 13 3 14 2 12 5 9 0 +1 10 2 8 1 4 5 11 0 +1 10 14 1 4 5 8 12 6 13 2 3 7 9 11 0 +1 2 10 1 5 14 4 7 9 12 3 6 0 +1 1 12 2 10 11 13 3 4 6 7 8 14 0 +1 1 7 6 10 11 2 9 4 8 12 0 +1 1 2 13 4 3 10 9 5 11 12 14 8 6 7 0 +1 5 4 12 10 2 0 +1 10 4 1 6 8 14 11 12 13 2 7 3 0 +1 10 4 1 2 14 5 6 13 8 12 0 +1 2 4 5 1 9 10 12 14 0 +1 2 4 10 13 14 1 5 11 12 7 3 8 6 0 +1 4 8 12 5 2 10 0 +1 4 10 13 9 5 12 8 2 1 14 11 7 6 3 0 +1 4 10 1 14 11 6 2 5 8 7 13 12 0 +1 8 10 5 1 6 7 11 4 14 13 3 12 2 9 0 +1 8 14 13 11 7 12 4 10 1 3 2 6 0 +1 8 10 14 4 1 11 6 5 12 7 2 0 +1 8 10 5 6 11 7 3 12 13 4 0 +1 8 6 7 5 11 13 3 0 +1 2 0 +0 +"A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" "L" "M" "N" "f:a34" diff --git a/tests/special_cases.rs b/tests/special_cases.rs index 753430f..bd8c086 100644 --- a/tests/special_cases.rs +++ b/tests/special_cases.rs @@ -86,7 +86,7 @@ fn tideman_a33_ers97_rational() { .quota(stv::QuotaType::DroopExact) .quota_criterion(stv::QuotaCriterion::GreaterOrEqual) .quota_mode(stv::QuotaMode::ERS97) - .ties(vec![TieStrategy::Random(String::new("20210908"))]) + .ties(vec![TieStrategy::Random(String::from("20210908"))]) .surplus(stv::SurplusMethod::EG) .transferable_only(true) .exclusion(stv::ExclusionMethod::ByValue) @@ -113,3 +113,38 @@ fn tideman_a33_ers97_rational() { let num_winners = state.candidates.values().filter(|cc| cc.state == CandidateState::Elected).count(); assert_eq!(num_winners, 3); } + +/// Tideman A34 election: surplus deferred with only 1 hopeful +#[test] +fn tideman_a34_prsa1977_rational() { + let stv_opts = stv::STVOptionsBuilder::default() + .round_surplus_fractions(Some(3)) + .round_values(Some(3)) + .round_votes(Some(3)) + .round_quota(Some(3)) + .quota_criterion(stv::QuotaCriterion::GreaterOrEqual) + .ties(vec![TieStrategy::Backwards, TieStrategy::Random(String::from("0"))]) + .surplus(stv::SurplusMethod::EG) + .surplus_order(stv::SurplusOrder::ByOrder) + .transferable_only(true) + .exclusion(stv::ExclusionMethod::ParcelsByOrder) + .early_bulk_elect(false) + .build().unwrap(); + + let mut election: Election = blt::parse_path("tests/data/A34.blt").expect("Syntax Error"); + stv::preprocess_election(&mut election, &stv_opts); + + let mut state = CountState::new(&election); + + stv::count_init(&mut state, &stv_opts).unwrap(); + + loop { + let result = stv::count_one_stage::(&mut state, &stv_opts); + match result { + Ok(done) => { if done { break; } } + Err(err) => { panic!("{}", err); } + } + } + + // Assert count completes +}