From 7ef5ae99b6ec854efe26db4ec91d31c187161620 Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Wed, 9 Jun 2021 20:09:20 +1000 Subject: [PATCH] Add ERS97 test case --- src/numbers/fixed.rs | 4 +- src/numbers/native.rs | 14 ++-- src/numbers/rational_num.rs | 14 ++-- src/numbers/rational_rug.rs | 4 +- tests/data/ers97.blt | 51 ++++++++++++++ tests/data/ers97.csv | 15 ++++ tests/data/ers97.ods | Bin 0 -> 14323 bytes tests/ers97.rs | 135 ++++++++++++++++++++++++++++++++++++ tests/utils/mod.rs | 3 +- 9 files changed, 216 insertions(+), 24 deletions(-) create mode 100644 tests/data/ers97.blt create mode 100644 tests/data/ers97.csv create mode 100644 tests/data/ers97.ods create mode 100644 tests/ers97.rs diff --git a/src/numbers/fixed.rs b/src/numbers/fixed.rs index 968004d..8b6e9d0 100644 --- a/src/numbers/fixed.rs +++ b/src/numbers/fixed.rs @@ -274,9 +274,7 @@ impl ops::Neg for &Fixed { impl ops::Add for &Fixed { type Output = Fixed; - fn add(self, _rhs: Self) -> Self::Output { - todo!() - } + fn add(self, rhs: Self) -> Self::Output { Fixed(&self.0 + &rhs.0) } } impl ops::Sub for &Fixed { diff --git a/src/numbers/native.rs b/src/numbers/native.rs index 534b6b0..e3d72ce 100644 --- a/src/numbers/native.rs +++ b/src/numbers/native.rs @@ -207,31 +207,29 @@ impl ops::Neg for &NativeFloat64 { fn neg(self) -> Self::Output { NativeFloat64(-&self.0) } } -impl ops::Add<&NativeFloat64> for &NativeFloat64 { +impl ops::Add for &NativeFloat64 { type Output = NativeFloat64; - fn add(self, _rhs: &NativeFloat64) -> Self::Output { - todo!() - } + fn add(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(&self.0 + &rhs.0) } } -impl ops::Sub<&NativeFloat64> for &NativeFloat64 { +impl ops::Sub for &NativeFloat64 { type Output = NativeFloat64; fn sub(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(&self.0 - &rhs.0) } } -impl ops::Mul<&NativeFloat64> for &NativeFloat64 { +impl ops::Mul for &NativeFloat64 { type Output = NativeFloat64; fn mul(self, _rhs: &NativeFloat64) -> Self::Output { todo!() } } -impl ops::Div<&NativeFloat64> for &NativeFloat64 { +impl ops::Div for &NativeFloat64 { type Output = NativeFloat64; fn div(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(&self.0 / &rhs.0) } } -impl ops::Rem<&NativeFloat64> for &NativeFloat64 { +impl ops::Rem for &NativeFloat64 { type Output = NativeFloat64; fn rem(self, _rhs: &NativeFloat64) -> Self::Output { todo!() diff --git a/src/numbers/rational_num.rs b/src/numbers/rational_num.rs index 2643269..a7b4e5d 100644 --- a/src/numbers/rational_num.rs +++ b/src/numbers/rational_num.rs @@ -254,31 +254,29 @@ impl ops::Neg for &Rational { fn neg(self) -> Self::Output { Rational(-&self.0) } } -impl ops::Add<&Rational> for &Rational { +impl ops::Add for &Rational { type Output = Rational; - fn add(self, _rhs: &Rational) -> Self::Output { - todo!() - } + fn add(self, rhs: &Rational) -> Self::Output { Rational(&self.0 + &rhs.0) } } -impl ops::Sub<&Rational> for &Rational { +impl ops::Sub for &Rational { type Output = Rational; fn sub(self, rhs: &Rational) -> Self::Output { Rational(&self.0 - &rhs.0) } } -impl ops::Mul<&Rational> for &Rational { +impl ops::Mul for &Rational { type Output = Rational; fn mul(self, _rhs: &Rational) -> Self::Output { todo!() } } -impl ops::Div<&Rational> for &Rational { +impl ops::Div for &Rational { type Output = Rational; fn div(self, rhs: &Rational) -> Self::Output { Rational(&self.0 / &rhs.0) } } -impl ops::Rem<&Rational> for &Rational { +impl ops::Rem for &Rational { type Output = Rational; fn rem(self, _rhs: &Rational) -> Self::Output { todo!() diff --git a/src/numbers/rational_rug.rs b/src/numbers/rational_rug.rs index 8e92944..9cfa977 100644 --- a/src/numbers/rational_rug.rs +++ b/src/numbers/rational_rug.rs @@ -254,9 +254,7 @@ impl ops::Neg for &Rational { impl ops::Add for &Rational { type Output = Rational; - fn add(self, _rhs: Self) -> Self::Output { - todo!() - } + fn add(self, rhs: Self) -> Self::Output { Rational(rug::Rational::from(&self.0 + &rhs.0)) } } impl ops::Sub for &Rational { diff --git a/tests/data/ers97.blt b/tests/data/ers97.blt new file mode 100644 index 0000000..ab2800a --- /dev/null +++ b/tests/data/ers97.blt @@ -0,0 +1,51 @@ +11 6 +35 1 2 +3 1 3 9 +11 1 3 6 +5 1 3 7 +6 1 3 8 +20 1 4 0 +7 1 4 9 +4 1 4 11 +8 1 5 +4 1 8 +1 1 10 5 7 +1 1 10 9 +18 1 11 +11 1 0 +81 2 +23 3 1 2 0 +2 3 1 2 7 +1 3 1 2 8 +1 3 1 0 +3 4 1 3 7 +1 4 8 +11 4 5 6 +3 4 2 0 +5 4 2 11 +1 4 3 0 +105 5 +91 6 +64 7 +5 8 +42 8 9 +12 8 7 +55 9 +2 10 5 +5 10 8 +14 10 9 +2 10 1 11 +90 11 +0 +"Smith" +"Carpenter" +"Wright" +"Glazier" +"Duke" +"Prince" +"Baron" +"Abbot" +"Vicar" +"Monk" +"Freeman" +"ERS97 Model Election" diff --git a/tests/data/ers97.csv b/tests/data/ers97.csv new file mode 100644 index 0000000..0b2c321 --- /dev/null +++ b/tests/data/ers97.csv @@ -0,0 +1,15 @@ +Stage:,1,,2,,3,,4,,5,,6,,7,,8, +Comment:,First preferences,,Surplus of Smith,,Exclusion of Monk,,Exclusion of Monk,,"Exclusion of Glazier, Wright",,"Exclusion of Glazier, Wright",,Surplus of Carpenter,,Exclusion of Abbot, +Smith,134,PEL,107.58,EL,107.58,EL,107.58,EL,107.58,EL,107.58,EL,107.58,EL,107.58,EL +Carpenter,81,,88.35,,88.35,,88.35,,122.35,PEL,122.35,PEL,107.58,EL,107.58,EL +Wright,27,,32.25,,32.25,,32.25,,5.25,EXCLUDING,0,EX,0,EX,0,EX +Glazier,24,,30.51,,30.51,,30.51,,6.51,EXCLUDING,0,EX,0,EX,0,EX +Duke,105,,106.68,,108.68,PEL,108.68,PEL,108.68,PEL,108.68,PEL,108.68,PEL,108.68,PEL +Prince,91,,91,,91,,91,,102,,104.31,,104.31,PEL,104.31,PEL +Baron,64,,64,,64,,64.21,,67.21,,68.26,,70.26,,82.26, +Abbot,59,,59.84,,64.84,,64.84,,65.84,,67.1,,68.1,,2.1,EXCLUDING +Vicar,55,,55,,69,,69.21,,69.21,,71.31,,71.31,,113.31,PEL +Monk,23,,23.42,,0.42,EXCLUDING,0,EX,0,EX,0,EX,0,EX,0,EX +Freeman,90,,93.78,,95.78,,95.78,,95.78,,96.62,,101.62,,101.62,PEL +Non-transferable,0,,0.59,,0.59,,0.59,,2.59,,6.79,,13.56,,25.56, +Votes required,,,,,,,,,,,105.87,,104.13,,96.41, diff --git a/tests/data/ers97.ods b/tests/data/ers97.ods new file mode 100644 index 0000000000000000000000000000000000000000..cda95761da9ec1930f51da858d95a741313749b6 GIT binary patch literal 14323 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 literal 0 HcmV?d00001 diff --git a/tests/ers97.rs b/tests/ers97.rs new file mode 100644 index 0000000..373fa99 --- /dev/null +++ b/tests/ers97.rs @@ -0,0 +1,135 @@ +/* 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 . + */ + +mod utils; + +use opentally::election::{CandidateState, CountState, Election}; +use opentally::numbers::Rational; +use opentally::stv; + +use csv::StringRecord; + +use std::fs::File; +use std::io::{self, BufRead}; + +#[test] +fn ers97_rational() { + let stv_opts = stv::STVOptions { + round_tvs: Some(2), + round_weights: Some(2), + round_votes: Some(2), + round_quota: Some(2), + quota: stv::QuotaType::DroopExact, + quota_criterion: stv::QuotaCriterion::GreaterOrEqual, + quota_mode: stv::QuotaMode::ERS97, + surplus: stv::SurplusMethod::EG, + surplus_order: stv::SurplusOrder::BySize, + transferable_only: true, + exclusion: stv::ExclusionMethod::ByValue, + bulk_exclude: true, + defer_surpluses: true, + pp_decimals: 2, + }; + + // --------------------------------------------- + // Custom implementation due to nontransferables + // and vote required for election + + // Read CSV file + let reader = csv::ReaderBuilder::new() + .has_headers(false) + .from_path("tests/data/ers97.csv") + .expect("IO Error"); + let records: Vec = reader.into_records().map(|r| r.expect("Syntax Error")).collect(); + + let mut candidates: Vec<&str> = records.iter().skip(2).map(|r| &r[0]).collect(); + // Remove NT/VRE rows + candidates.truncate(candidates.len() - 2); + + // TODO: Validate candidate names + + let stages: Vec = records.first().unwrap().iter().skip(1).step_by(2).map(|s| s.parse().unwrap()).collect(); + + // 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); + + // Distribute first preferences + stv::count_init(&mut state, &stv_opts); + let mut stage_num = 1; + + for (idx, stage) in stages.into_iter().enumerate() { + while stage_num < stage { + // Step through stages + // Assert count not yet done + assert_eq!(stv::count_one_stage(&mut state, &stv_opts), false); + stage_num += 1; + } + + println!("Col at idx {}", idx); + + let mut candidate_votes: Vec> = records.iter().skip(2) + .map(|r| + if r[idx*2 + 1].len() > 0 { + Some(opentally::numbers::From::from(r[idx*2 + 1].parse::().expect("Syntax Error"))) + } else { + None + }) + .collect(); + + // Validate NT/VRE + let vre_votes = candidate_votes.pop().unwrap(); + let nt_votes = candidate_votes.pop().unwrap(); + + assert!(&state.exhausted.votes + &state.loss_fraction.votes == nt_votes.unwrap()); + if let Some(v) = vre_votes { + assert!(state.vote_required_election.as_ref().unwrap() == &v); + } + + // Remove NT/VRE rows + candidate_votes.truncate(candidate_votes.len() - 2); + + // Validate candidate votes + for (candidate, votes) in state.election.candidates.iter().zip(candidate_votes) { + let count_card = state.candidates.get(candidate).unwrap(); + assert!(&count_card.votes == votes.as_ref().unwrap(), "Failed to validate votes for candidate {}. Expected {:}, got {:}", candidate.name, votes.unwrap(), count_card.votes); + } + + // Validate candidate states + let mut candidate_states: Vec<&str> = records.iter().skip(2).map(|r| &r[idx*2 + 2]).collect(); + candidate_states.truncate(candidate_states.len() - 2); + + for (candidate, candidate_state) in state.election.candidates.iter().zip(candidate_states) { + let count_card = state.candidates.get(candidate).unwrap(); + if candidate_state == "" { + assert!(count_card.state == CandidateState::HOPEFUL); + } else if candidate_state == "EL" || candidate_state == "PEL" { + assert!(count_card.state == CandidateState::ELECTED); + } else if candidate_state == "EX" || candidate_state == "EXCLUDING" { + assert!(count_card.state == CandidateState::EXCLUDED); + } else { + panic!("Unknown state descriptor {}", candidate_state); + } + } + } +} diff --git a/tests/utils/mod.rs b/tests/utils/mod.rs index 9953ba2..19b9397 100644 --- a/tests/utils/mod.rs +++ b/tests/utils/mod.rs @@ -42,12 +42,11 @@ pub fn read_validate_election(csv_file: &str, blt_file: &str, stv_opt let stages: Vec = records.first().unwrap().iter().skip(1).step_by(2).map(|s| s.parse().unwrap()).collect(); - // Decompress BLT + // Read BLT let file = File::open(blt_file).expect("IO Error"); let file_reader = io::BufReader::new(file); let lines = file_reader.lines(); - // Read BLT let election: Election = Election::from_blt(lines.map(|r| r.expect("IO Error").to_string()).into_iter()); validate_election(stages, records, election, stv_opts);