Refactor tests specification using builder pattern

This commit is contained in:
RunasSudo 2021-08-05 01:12:53 +10:00
parent 0800701960
commit f3e4071886
No known key found for this signature in database
GPG Key ID: 7234E476BF21C61A
17 changed files with 508 additions and 580 deletions

134
Cargo.lock generated
View File

@ -187,14 +187,81 @@ dependencies = [
]
[[package]]
name = "derive_more"
version = "0.99.14"
name = "darling"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cc7b9cef1e351660e5443924e4f43ab25fbbed3e9a5f052df3677deb4d6b320"
checksum = "5f2c43f534ea4b0b049015d00269734195e6d3f0f6635cb692251aca6f9f8b3c"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e91455b86830a1c21799d94524df0845183fa55bafd9aa137b01c7d1065fa36"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "derive_builder"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d13202debe11181040ae9063d739fa32cfcaaebe2275fe387703460ae2365b30"
dependencies = [
"derive_builder_macro",
]
[[package]]
name = "derive_builder_core"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66e616858f6187ed828df7c64a6d71720d83767a7f19740b2d1b6fe6327b36e5"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "derive_builder_macro"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58a94ace95092c5acb1e97a7e846b310cfbd499652f72297da7493f618a98d73"
dependencies = [
"derive_builder_core",
"syn",
]
[[package]]
name = "derive_more"
version = "0.99.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40eebddd2156ce1bb37b20bbe5151340a31828b1f2d22ba4141f3531710e38df"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"rustc_version",
"syn",
]
@ -246,6 +313,12 @@ dependencies = [
"num-traits",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "generic-array"
version = "0.14.4"
@ -316,6 +389,12 @@ dependencies = [
"static_assertions",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "indexmap"
version = "1.6.2"
@ -480,6 +559,7 @@ dependencies = [
"clap",
"console_error_panic_hook",
"csv",
"derive_builder",
"derive_more",
"flate2",
"git-version",
@ -511,6 +591,15 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf547ad0c65e31259204bd90935776d1c693cec2f4ff7abb7a1bbbd40dfe58"
[[package]]
name = "pest"
version = "2.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
dependencies = [
"ucd-trie",
]
[[package]]
name = "predicates"
version = "1.0.8"
@ -646,12 +735,39 @@ dependencies = [
"libc",
]
[[package]]
name = "rustc_version"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee"
dependencies = [
"semver",
]
[[package]]
name = "ryu"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "semver"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
dependencies = [
"pest",
]
[[package]]
name = "serde"
version = "1.0.126"
@ -677,6 +793,12 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.72"
@ -706,6 +828,12 @@ version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
[[package]]
name = "ucd-trie"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
[[package]]
name = "unicode-segmentation"
version = "1.7.1"

View File

@ -8,6 +8,7 @@ edition = "2018"
crate-type = ["lib", "cdylib"]
[dependencies]
derive_builder = "0.10.2"
derive_more = "0.99.14"
git-version = "0.3.4"
ibig = "0.3.2"

View File

@ -93,7 +93,7 @@ Random sample methods are also supported, but also not recommended:
* *Cincinnati (inclusive sample)*: During surplus transfers, a subset of the elected candidate's ballot papers, equal in size to the surplus, is examined.
* *Hare (exclusive sample)*: During surplus transfers, a subset of the ballot papers received in the last transfer, equal in size to the surplus, is examined.
The use of a random sample method requires *Normalise ballots* to be enabled, and requires the *Quota criterion* to be set to *>=*.
The use of a random sample method requires *Normalise ballots* to be enabled, and will usually be used with a *Quota criterion* set to *>=*.
In both random sample methods, the subset is selected using the deterministic method used in [Cambridge, Massachusetts](https://web.archive.org/web/20081118104049/http://www.fairvote.org/media/1993countmanual.pdf) (derived from Article IX of the former 1938 Cincinnati *Code of Ordinances*). This depends on the order of ballot papers in the BLT file, and is independent of the *Random seed* option.

View File

@ -564,7 +564,7 @@ function changePreset() {
document.getElementById('selQuota').value = 'droop';
document.getElementById('selQuotaMode').value = 'static';
document.getElementById('chkBulkElection').checked = true;
document.getElementById('chkBulkExclusion').checked = false; // TODO: Cambridge-style bulk exclusion
document.getElementById('chkBulkExclusion').checked = false;
document.getElementById('chkDeferSurpluses').checked = false;
document.getElementById('chkSamplePerBallot').checked = true;
document.getElementById('txtMinThreshold').value = '49';

View File

@ -19,6 +19,7 @@ use opentally::constraints::Constraints;
use opentally::election::{CandidateState, CountState, Election};
use opentally::numbers::{Fixed, GuardedFixed, NativeFloat64, Number, Rational};
use opentally::stv::{self, STVOptions};
use opentally::ties;
use clap::{AppSettings, Clap};
@ -265,18 +266,17 @@ where
cmd_opts.round_values,
cmd_opts.round_votes,
cmd_opts.round_quota,
&cmd_opts.sum_surplus_transfers,
&cmd_opts.meek_surplus_tolerance,
cmd_opts.sum_surplus_transfers.into(),
cmd_opts.meek_surplus_tolerance.into(),
cmd_opts.normalise_ballots,
&cmd_opts.quota,
&cmd_opts.quota_criterion,
&cmd_opts.quota_mode,
&cmd_opts.ties,
&cmd_opts.random_seed,
&cmd_opts.surplus,
&cmd_opts.surplus_order,
cmd_opts.quota.into(),
cmd_opts.quota_criterion.into(),
cmd_opts.quota_mode.into(),
ties::from_strs(cmd_opts.ties, cmd_opts.random_seed),
cmd_opts.surplus.into(),
cmd_opts.surplus_order.into(),
cmd_opts.transferable_only,
&cmd_opts.exclusion,
cmd_opts.exclusion.into(),
cmd_opts.meek_nz_exclusion,
cmd_opts.sample_per_ballot,
!cmd_opts.no_early_bulk_elect,
@ -284,8 +284,8 @@ where
cmd_opts.defer_surpluses,
cmd_opts.meek_immediate_elect,
cmd_opts.min_threshold,
cmd_opts.constraints.as_deref(),
&cmd_opts.constraint_mode,
cmd_opts.constraints,
cmd_opts.constraint_mode.into(),
cmd_opts.hide_excluded,
cmd_opts.sort_votes,
cmd_opts.pp_decimals,

View File

@ -34,6 +34,8 @@ use crate::election::{Candidate, CandidateState, CountCard, CountState, Vote};
use crate::sharandom::SHARandom;
use crate::ties::{self, TieStrategy};
use derive_builder::Builder;
use derive_more::Constructor;
use itertools::Itertools;
use wasm_bindgen::prelude::wasm_bindgen;
@ -42,178 +44,118 @@ use std::fmt;
use std::ops;
/// Options for conducting an STV count
#[derive(Builder, Constructor)]
pub struct STVOptions {
/// Round surplus fractions to specified decimal places
#[builder(default="None")]
pub round_surplus_fractions: Option<usize>,
/// Round ballot values to specified decimal places
#[builder(default="None")]
pub round_values: Option<usize>,
/// Round votes to specified decimal places
#[builder(default="None")]
pub round_votes: Option<usize>,
/// Round quota to specified decimal places
#[builder(default="None")]
pub round_quota: Option<usize>,
/// How to calculate votes to credit to candidates in surplus transfers
#[builder(default="SumSurplusTransfersMode::SingleStep")]
pub sum_surplus_transfers: SumSurplusTransfersMode,
/// (Meek STV) Limit for stopping iteration of surplus distribution
#[builder(default)]
pub meek_surplus_tolerance: String,
/// Convert ballots with value >1 to multiple ballots of value 1 (used only for [STVOptions::describe])
#[builder(default="false")]
pub normalise_ballots: bool,
/// Quota type
#[builder(default="QuotaType::DroopExact")]
pub quota: QuotaType,
/// Whether to elect candidates on meeting (geq) or strictly exceeding (gt) the quota
#[builder(default="QuotaCriterion::Greater")]
pub quota_criterion: QuotaCriterion,
/// Whether to apply a form of progressive quota
#[builder(default="QuotaMode::Static")]
pub quota_mode: QuotaMode,
/// Tie-breaking method
#[builder(default)]
pub ties: Vec<TieStrategy>,
/// Method of surplus distributions
#[builder(default="SurplusMethod::WIG")]
pub surplus: SurplusMethod,
/// (Gregory STV) Order to distribute surpluses
#[builder(default="SurplusOrder::BySize")]
pub surplus_order: SurplusOrder,
/// (Gregory STV) Examine only transferable papers during surplus distributions
#[builder(default="false")]
pub transferable_only: bool,
/// (Gregory STV) Method of exclusions
#[builder(default="ExclusionMethod::SingleStage")]
pub exclusion: ExclusionMethod,
/// (Meek STV) NZ Meek STV behaviour: Iterate keep values one round before candidate exclusion
#[builder(default="false")]
pub meek_nz_exclusion: bool,
/// (Cincinnati/Hare) Sample-based methods: Check for candidate election after each individual ballot paper transfer
#[builder(default="false")]
pub sample_per_ballot: bool,
/// Bulk elect as soon as continuing candidates fill all remaining vacancies
#[builder(default="true")]
pub early_bulk_elect: bool,
/// Use bulk exclusion
#[builder(default="false")]
pub bulk_exclude: bool,
/// Defer surplus distributions if possible
#[builder(default="false")]
pub defer_surpluses: bool,
/// (Meek STV) Immediately elect candidates even if keep values have not converged
#[builder(default="false")]
pub meek_immediate_elect: bool,
/// On exclusion, exclude any candidate with this many votes or fewer
#[builder(default="\"0\".to_string()")]
pub min_threshold: String,
/// Path to constraints file (used only for [STVOptions::describe])
#[builder(default="None")]
pub constraints_path: Option<String>,
/// Mode of handling constraints
#[builder(default="ConstraintMode::GuardDoom")]
pub constraint_mode: ConstraintMode,
/// Hide excluded candidates from results report
#[builder(default="false")]
pub hide_excluded: bool,
/// Sort candidates by votes in results report
#[builder(default="false")]
pub sort_votes: bool,
/// Print votes to specified decimal places in results report
#[builder(default="2")]
pub pp_decimals: usize,
}
impl STVOptions {
/// Returns a new [STVOptions] based on arguments given as strings
pub fn new(
round_surplus_fractions: Option<usize>,
round_values: Option<usize>,
round_votes: Option<usize>,
round_quota: Option<usize>,
sum_surplus_transfers: &str,
meek_surplus_tolerance: &str,
normalise_ballots: bool,
quota: &str,
quota_criterion: &str,
quota_mode: &str,
ties: &Vec<String>,
random_seed: &Option<String>,
surplus: &str,
surplus_order: &str,
transferable_only: bool,
exclusion: &str,
meek_nz_exclusion: bool,
sample_per_ballot: bool,
early_bulk_elect: bool,
bulk_exclude: bool,
defer_surpluses: bool,
meek_immediate_elect: bool,
min_threshold: String,
constraints_path: Option<&str>,
constraint_mode: &str,
hide_excluded: bool,
sort_votes: bool,
pp_decimals: usize,
) -> Self {
return STVOptions {
round_surplus_fractions,
round_values,
round_votes,
round_quota,
sum_surplus_transfers: match sum_surplus_transfers {
"single_step" => SumSurplusTransfersMode::SingleStep,
"by_value" => SumSurplusTransfersMode::ByValue,
"per_ballot" => SumSurplusTransfersMode::PerBallot,
_ => panic!("Invalid --sum-transfers"),
},
meek_surplus_tolerance: meek_surplus_tolerance.to_string(),
normalise_ballots,
quota: match quota {
"droop" => QuotaType::Droop,
"hare" => QuotaType::Hare,
"droop_exact" => QuotaType::DroopExact,
"hare_exact" => QuotaType::HareExact,
_ => panic!("Invalid --quota"),
},
quota_criterion: match quota_criterion {
"geq" => QuotaCriterion::GreaterOrEqual,
"gt" => QuotaCriterion::Greater,
_ => panic!("Invalid --quota-criterion"),
},
quota_mode: match quota_mode {
"static" => QuotaMode::Static,
"ers97" => QuotaMode::ERS97,
"ers76" => QuotaMode::ERS76,
_ => panic!("Invalid --quota-mode"),
},
ties: ties.into_iter().map(|t| match t.as_str() {
"forwards" => TieStrategy::Forwards,
"backwards" => TieStrategy::Backwards,
"random" => TieStrategy::Random(random_seed.as_ref().expect("Must provide a --random-seed if using --ties random").clone()),
"prompt" => TieStrategy::Prompt,
_ => panic!("Invalid --ties"),
}).collect(),
surplus: match surplus {
"wig" => SurplusMethod::WIG,
"uig" => SurplusMethod::UIG,
"eg" => SurplusMethod::EG,
"meek" => SurplusMethod::Meek,
"cincinnati" => SurplusMethod::Cincinnati,
"hare" => SurplusMethod::Hare,
_ => panic!("Invalid --surplus"),
},
surplus_order: match surplus_order {
"by_size" => SurplusOrder::BySize,
"by_order" => SurplusOrder::ByOrder,
_ => panic!("Invalid --surplus-order"),
},
transferable_only,
exclusion: match exclusion {
"single_stage" => ExclusionMethod::SingleStage,
"by_value" => ExclusionMethod::ByValue,
"by_source" => ExclusionMethod::BySource,
"parcels_by_order" => ExclusionMethod::ParcelsByOrder,
"wright" => ExclusionMethod::Wright,
_ => panic!("Invalid --exclusion"),
},
meek_nz_exclusion,
sample_per_ballot,
early_bulk_elect,
bulk_exclude,
defer_surpluses,
meek_immediate_elect,
min_threshold,
constraints_path: match constraints_path {
Some(p) => Some(p.to_string()),
None => None,
},
constraint_mode: match constraint_mode {
"guard_doom" => ConstraintMode::GuardDoom,
"rollback" => ConstraintMode::Rollback,
_ => panic!("Invalid --constraint-mode"),
},
hide_excluded,
sort_votes,
pp_decimals,
};
}
/// Converts the [STVOptions] into CLI argument representation
pub fn describe<N: Number>(&self) -> String {
let mut flags = Vec::new();
@ -263,7 +205,7 @@ impl STVOptions {
if self.exclusion != ExclusionMethod::SingleStage { return Err(STVError::InvalidOptions("--surplus meek requires --exclusion single_stage")); }
}
if self.surplus == SurplusMethod::Cincinnati || self.surplus == SurplusMethod::Hare {
if self.quota_criterion != QuotaCriterion::GreaterOrEqual { return Err(STVError::InvalidOptions("--surplus cincinnati and --surplus hare require --quota-criterion geq")); }
if self.round_quota != Some(0) { return Err(STVError::InvalidOptions("--surplus cincinnati and --surplus hare require --round-quota 0")); }
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
@ -295,6 +237,17 @@ impl SumSurplusTransfersMode {
}
}
impl<S: AsRef<str>> From<S> for SumSurplusTransfersMode {
fn from(s: S) -> Self {
match s.as_ref() {
"single_step" => SumSurplusTransfersMode::SingleStep,
"by_value" => SumSurplusTransfersMode::ByValue,
"per_ballot" => SumSurplusTransfersMode::PerBallot,
_ => panic!("Invalid --sum-transfers"),
}
}
}
/// Enum of options for [STVOptions::quota]
#[wasm_bindgen]
#[derive(Clone, Copy)]
@ -322,6 +275,18 @@ impl QuotaType {
}
}
impl<S: AsRef<str>> From<S> for QuotaType {
fn from(s: S) -> Self {
match s.as_ref() {
"droop" => QuotaType::Droop,
"hare" => QuotaType::Hare,
"droop_exact" => QuotaType::DroopExact,
"hare_exact" => QuotaType::HareExact,
_ => panic!("Invalid --quota"),
}
}
}
/// Enum of options for [STVOptions::quota_criterion]
#[wasm_bindgen]
#[derive(Clone, Copy)]
@ -343,6 +308,16 @@ impl QuotaCriterion {
}
}
impl<S: AsRef<str>> From<S> for QuotaCriterion {
fn from(s: S) -> Self {
match s.as_ref() {
"geq" => QuotaCriterion::GreaterOrEqual,
"gt" => QuotaCriterion::Greater,
_ => panic!("Invalid --quota-criterion"),
}
}
}
/// Enum of options for [STVOptions::quota_mode]
#[wasm_bindgen]
#[derive(Clone, Copy)]
@ -367,6 +342,17 @@ impl QuotaMode {
}
}
impl<S: AsRef<str>> From<S> for QuotaMode {
fn from(s: S) -> Self {
match s.as_ref() {
"static" => QuotaMode::Static,
"ers97" => QuotaMode::ERS97,
"ers76" => QuotaMode::ERS76,
_ => panic!("Invalid --quota-mode"),
}
}
}
/// Enum of options for [STVOptions::surplus]
#[wasm_bindgen]
#[derive(Clone, Copy)]
@ -400,6 +386,20 @@ impl SurplusMethod {
}
}
impl<S: AsRef<str>> From<S> for SurplusMethod {
fn from(s: S) -> Self {
match s.as_ref() {
"wig" => SurplusMethod::WIG,
"uig" => SurplusMethod::UIG,
"eg" => SurplusMethod::EG,
"meek" => SurplusMethod::Meek,
"cincinnati" => SurplusMethod::Cincinnati,
"hare" => SurplusMethod::Hare,
_ => panic!("Invalid --surplus"),
}
}
}
/// Enum of options for [STVOptions::surplus_order]
#[wasm_bindgen]
#[derive(Clone, Copy)]
@ -421,6 +421,16 @@ impl SurplusOrder {
}
}
impl<S: AsRef<str>> From<S> for SurplusOrder {
fn from(s: S) -> Self {
match s.as_ref() {
"by_size" => SurplusOrder::BySize,
"by_order" => SurplusOrder::ByOrder,
_ => panic!("Invalid --surplus-order"),
}
}
}
/// Enum of options for [STVOptions::exclusion]
#[wasm_bindgen]
#[derive(Clone, Copy)]
@ -451,6 +461,19 @@ impl ExclusionMethod {
}
}
impl<S: AsRef<str>> From<S> for ExclusionMethod {
fn from(s: S) -> Self {
match s.as_ref() {
"single_stage" => ExclusionMethod::SingleStage,
"by_value" => ExclusionMethod::ByValue,
"by_source" => ExclusionMethod::BySource,
"parcels_by_order" => ExclusionMethod::ParcelsByOrder,
"wright" => ExclusionMethod::Wright,
_ => panic!("Invalid --exclusion"),
}
}
}
/// Enum of options for [STVOptions::constraint_mode]
#[derive(Clone, Copy)]
#[derive(PartialEq)]
@ -471,6 +494,16 @@ impl ConstraintMode {
}
}
impl<S: AsRef<str>> From<S> for ConstraintMode {
fn from(s: S) -> Self {
match s.as_ref() {
"guard_doom" => ConstraintMode::GuardDoom,
"rollback" => ConstraintMode::Rollback,
_ => panic!("Invalid --constraint-mode"),
}
}
}
/// An error during the STV count
#[derive(Debug)]
pub enum STVError {

View File

@ -22,6 +22,7 @@ use crate::constraints::Constraints;
use crate::election::{CandidateState, CountState, Election};
use crate::numbers::{Fixed, GuardedFixed, NativeFloat64, Number, Rational};
use crate::stv;
use crate::ties;
extern crate console_error_panic_hook;
@ -216,7 +217,7 @@ impl STVOptions {
round_votes: Option<usize>,
round_quota: Option<usize>,
sum_surplus_transfers: &str,
meek_surplus_tolerance: &str,
meek_surplus_tolerance: String,
normalise_ballots: bool,
quota: &str,
quota_criterion: &str,
@ -243,18 +244,17 @@ impl STVOptions {
round_values,
round_votes,
round_quota,
sum_surplus_transfers,
sum_surplus_transfers.into(),
meek_surplus_tolerance,
normalise_ballots,
quota,
quota_criterion,
quota_mode,
&ties.iter().map(|v| v.as_string().unwrap()).collect(),
&Some(random_seed),
surplus,
surplus_order,
quota.into(),
quota_criterion.into(),
quota_mode.into(),
ties::from_strs(ties.iter().map(|v| v.as_string().unwrap()).collect(), Some(random_seed)),
surplus.into(),
surplus_order.into(),
transferable_only,
exclusion,
exclusion.into(),
meek_nz_exclusion,
sample_per_ballot,
early_bulk_elect,
@ -262,8 +262,8 @@ impl STVOptions {
defer_surpluses,
meek_immediate_elect,
min_threshold,
constraints_path.as_deref(),
constraint_mode,
constraints_path,
constraint_mode.into(),
false,
false,
pp_decimals,

View File

@ -27,7 +27,7 @@ use wasm_bindgen::prelude::wasm_bindgen;
use std::io::{stdin, stdout, Write};
/// Strategy for breaking ties
#[derive(PartialEq)]
#[derive(Clone, PartialEq)]
pub enum TieStrategy {
/// Break ties according to the candidate who first had more/fewer votes
Forwards,
@ -39,6 +39,17 @@ pub enum TieStrategy {
Prompt,
}
/// Get a [Vec] of [TieStrategy] based on string representations
pub fn from_strs<S: AsRef<str>>(strs: Vec<S>, mut random_seed: Option<String>) -> Vec<TieStrategy> {
strs.into_iter().map(|t| match t.as_ref() {
"forwards" => TieStrategy::Forwards,
"backwards" => TieStrategy::Backwards,
"random" => TieStrategy::Random(random_seed.take().expect("Must provide a --random-seed if using --ties random")),
"prompt" => TieStrategy::Prompt,
_ => panic!("Invalid --ties"),
}).collect()
}
impl TieStrategy {
/// Convert to CLI argument representation
pub fn describe(&self) -> String {

View File

@ -22,34 +22,17 @@ use opentally::stv;
#[test]
fn act_kurrajong20_rational() {
let stv_opts = stv::STVOptions {
round_surplus_fractions: None,
round_values: None,
round_votes: Some(6),
round_quota: Some(0),
sum_surplus_transfers: stv::SumSurplusTransfersMode::SingleStep,
meek_surplus_tolerance: String::new(),
normalise_ballots: false,
quota: stv::QuotaType::Droop,
quota_criterion: stv::QuotaCriterion::GreaterOrEqual,
quota_mode: stv::QuotaMode::Static,
ties: vec![],
surplus: stv::SurplusMethod::EG,
surplus_order: stv::SurplusOrder::ByOrder,
transferable_only: true,
exclusion: stv::ExclusionMethod::ByValue,
meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: false,
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,
sort_votes: false,
pp_decimals: 2,
};
let stv_opts = stv::STVOptionsBuilder::default()
.round_votes(Some(6))
.round_quota(Some(0))
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.surplus(stv::SurplusMethod::EG)
.surplus_order(stv::SurplusOrder::ByOrder)
.transferable_only(true)
.exclusion(stv::ExclusionMethod::ByValue)
.early_bulk_elect(false)
.build().unwrap();
utils::read_validate_election::<Rational>("tests/data/ACT2020Kurrajong.csv", "tests/data/ACT2020Kurrajong.blt", stv_opts, Some(6), &["exhausted", "lbf"]);
}

View File

@ -59,34 +59,16 @@ fn aec_tas19_rational() {
assert_eq!(election.candidates[i].name, *candidate);
}
let stv_opts = stv::STVOptions {
round_surplus_fractions: None,
round_values: None,
round_votes: Some(0),
round_quota: Some(0),
sum_surplus_transfers: stv::SumSurplusTransfersMode::SingleStep,
meek_surplus_tolerance: String::new(),
normalise_ballots: false,
quota: stv::QuotaType::Droop,
quota_criterion: stv::QuotaCriterion::GreaterOrEqual,
quota_mode: stv::QuotaMode::Static,
ties: vec![],
surplus: stv::SurplusMethod::UIG,
surplus_order: stv::SurplusOrder::ByOrder,
transferable_only: false,
exclusion: stv::ExclusionMethod::ByValue,
meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: true,
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,
sort_votes: false,
pp_decimals: 2,
};
let stv_opts = stv::STVOptionsBuilder::default()
.round_votes(Some(0))
.round_quota(Some(0))
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.surplus(stv::SurplusMethod::UIG)
.surplus_order(stv::SurplusOrder::ByOrder)
.exclusion(stv::ExclusionMethod::ByValue)
.bulk_exclude(true)
.build().unwrap();
utils::validate_election::<Rational>(stages, records, election, stv_opts, None, &["exhausted", "lbf"]);
}

View File

@ -22,34 +22,17 @@ 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,
sample_per_ballot: true,
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,
};
let stv_opts = stv::STVOptionsBuilder::default()
.round_quota(Some(0))
.normalise_ballots(true)
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.surplus(stv::SurplusMethod::Cincinnati)
.transferable_only(true)
.sample_per_ballot(true)
.early_bulk_elect(false)
.min_threshold("49".to_string())
.build().unwrap();
utils::read_validate_election::<Rational>("tests/data/CambCC2003.csv", "tests/data/CambCC2003.blt", stv_opts, None, &["exhausted"]);
}

View File

@ -38,35 +38,20 @@ fn prsa1_constr1_rational() {
let lines = file_reader.lines();
election.constraints = Some(Constraints::from_con(lines.map(|r| r.expect("IO Error").to_string()).into_iter()));
let stv_opts = stv::STVOptions {
round_surplus_fractions: Some(3),
round_values: Some(3),
round_votes: Some(3),
round_quota: Some(3),
sum_surplus_transfers: stv::SumSurplusTransfersMode::SingleStep,
meek_surplus_tolerance: String::new(),
normalise_ballots: false,
quota: stv::QuotaType::Droop,
quota_criterion: stv::QuotaCriterion::GreaterOrEqual,
quota_mode: stv::QuotaMode::Static,
ties: vec![],
surplus: stv::SurplusMethod::EG,
surplus_order: stv::SurplusOrder::ByOrder,
transferable_only: true,
exclusion: stv::ExclusionMethod::ParcelsByOrder,
meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: false,
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,
sort_votes: false,
pp_decimals: 2,
};
let stv_opts = stv::STVOptionsBuilder::default()
.round_surplus_fractions(Some(3))
.round_values(Some(3))
.round_votes(Some(3))
.round_quota(Some(3))
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.surplus(stv::SurplusMethod::EG)
.surplus_order(stv::SurplusOrder::ByOrder)
.transferable_only(true)
.exclusion(stv::ExclusionMethod::ParcelsByOrder)
.early_bulk_elect(false)
.constraints_path(Some("tests/data/prsa1_constr1.con".to_string()))
.build().unwrap();
// Initialise count state
let mut state = CountState::new(&election);
@ -103,35 +88,20 @@ fn prsa1_constr2_rational() {
let lines = file_reader.lines();
election.constraints = Some(Constraints::from_con(lines.map(|r| r.expect("IO Error").to_string()).into_iter()));
let stv_opts = stv::STVOptions {
round_surplus_fractions: Some(3),
round_values: Some(3),
round_votes: Some(3),
round_quota: Some(3),
sum_surplus_transfers: stv::SumSurplusTransfersMode::SingleStep,
meek_surplus_tolerance: String::new(),
normalise_ballots: false,
quota: stv::QuotaType::Droop,
quota_criterion: stv::QuotaCriterion::GreaterOrEqual,
quota_mode: stv::QuotaMode::Static,
ties: vec![],
surplus: stv::SurplusMethod::EG,
surplus_order: stv::SurplusOrder::ByOrder,
transferable_only: true,
exclusion: stv::ExclusionMethod::ParcelsByOrder,
meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: false,
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,
sort_votes: false,
pp_decimals: 2,
};
let stv_opts = stv::STVOptionsBuilder::default()
.round_surplus_fractions(Some(3))
.round_values(Some(3))
.round_votes(Some(3))
.round_quota(Some(3))
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.surplus(stv::SurplusMethod::EG)
.surplus_order(stv::SurplusOrder::ByOrder)
.transferable_only(true)
.exclusion(stv::ExclusionMethod::ParcelsByOrder)
.early_bulk_elect(false)
.constraints_path(Some("tests/data/prsa1_constr1.con".to_string()))
.build().unwrap();
// Initialise count state
let mut state = CountState::new(&election);
@ -168,35 +138,20 @@ fn prsa1_constr3_rational() {
let lines = file_reader.lines();
election.constraints = Some(Constraints::from_con(lines.map(|r| r.expect("IO Error").to_string()).into_iter()));
let stv_opts = stv::STVOptions {
round_surplus_fractions: Some(3),
round_values: Some(3),
round_votes: Some(3),
round_quota: Some(3),
sum_surplus_transfers: stv::SumSurplusTransfersMode::SingleStep,
meek_surplus_tolerance: String::new(),
normalise_ballots: false,
quota: stv::QuotaType::Droop,
quota_criterion: stv::QuotaCriterion::GreaterOrEqual,
quota_mode: stv::QuotaMode::Static,
ties: vec![],
surplus: stv::SurplusMethod::EG,
surplus_order: stv::SurplusOrder::ByOrder,
transferable_only: true,
exclusion: stv::ExclusionMethod::ParcelsByOrder,
meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: false,
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,
sort_votes: false,
pp_decimals: 2,
};
let stv_opts = stv::STVOptionsBuilder::default()
.round_surplus_fractions(Some(3))
.round_values(Some(3))
.round_votes(Some(3))
.round_quota(Some(3))
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.surplus(stv::SurplusMethod::EG)
.surplus_order(stv::SurplusOrder::ByOrder)
.transferable_only(true)
.exclusion(stv::ExclusionMethod::ParcelsByOrder)
.early_bulk_elect(false)
.constraints_path(Some("tests/data/prsa1_constr1.con".to_string()))
.build().unwrap();
// Initialise count state
let mut state = CountState::new(&election);
@ -245,34 +200,21 @@ fn ers97_cantbulkexclude_rational() {
let lines = file_reader.lines();
election.constraints = Some(Constraints::from_con(lines.map(|r| r.expect("IO Error").to_string()).into_iter()));
let stv_opts = stv::STVOptions {
round_surplus_fractions: Some(2),
round_values: Some(2),
round_votes: Some(2),
round_quota: Some(2),
sum_surplus_transfers: stv::SumSurplusTransfersMode::SingleStep,
meek_surplus_tolerance: String::new(),
normalise_ballots: false,
quota: stv::QuotaType::DroopExact,
quota_criterion: stv::QuotaCriterion::GreaterOrEqual,
quota_mode: stv::QuotaMode::ERS97,
ties: vec![],
surplus: stv::SurplusMethod::EG,
surplus_order: stv::SurplusOrder::BySize,
transferable_only: true,
exclusion: stv::ExclusionMethod::ByValue,
meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: false,
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,
sort_votes: false,
pp_decimals: 2,
};
let stv_opts = stv::STVOptionsBuilder::default()
.round_surplus_fractions(Some(2))
.round_values(Some(2))
.round_votes(Some(2))
.round_quota(Some(2))
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.quota_mode(stv::QuotaMode::ERS97)
.surplus(stv::SurplusMethod::EG)
.transferable_only(true)
.exclusion(stv::ExclusionMethod::ByValue)
.early_bulk_elect(false)
.bulk_exclude(true)
.defer_surpluses(true)
.constraints_path(Some("tests/data/ers97_cantbulkexclude".to_string()))
.build().unwrap();
utils::validate_election::<Rational>(stages, records, election, stv_opts, None, &["nt", "vre"]);
}

View File

@ -22,34 +22,14 @@ use opentally::stv;
#[test]
fn csm15_float64() {
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: false,
quota: stv::QuotaType::Droop,
quota_criterion: stv::QuotaCriterion::GreaterOrEqual,
quota_mode: stv::QuotaMode::Static,
ties: vec![],
surplus: stv::SurplusMethod::WIG,
surplus_order: stv::SurplusOrder::BySize,
transferable_only: false,
exclusion: stv::ExclusionMethod::Wright,
meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: false, // Required for validation
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,
sort_votes: false,
pp_decimals: 2,
};
let stv_opts = stv::STVOptionsBuilder::default()
.round_quota(Some(0))
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.exclusion(stv::ExclusionMethod::Wright)
.early_bulk_elect(false)
.bulk_exclude(true)
.build().unwrap();
utils::read_validate_election::<NativeFloat64>("tests/data/CSM15.csv", "tests/data/CSM15.blt", stv_opts, Some(6), &["quota"]);
}

View File

@ -22,34 +22,20 @@ use opentally::stv;
#[test]
fn ers97_rational() {
let stv_opts = stv::STVOptions {
round_surplus_fractions: Some(2),
round_values: Some(2),
round_votes: Some(2),
round_quota: Some(2),
sum_surplus_transfers: stv::SumSurplusTransfersMode::SingleStep,
meek_surplus_tolerance: String::new(),
normalise_ballots: false,
quota: stv::QuotaType::DroopExact,
quota_criterion: stv::QuotaCriterion::GreaterOrEqual,
quota_mode: stv::QuotaMode::ERS97,
ties: vec![],
surplus: stv::SurplusMethod::EG,
surplus_order: stv::SurplusOrder::BySize,
transferable_only: true,
exclusion: stv::ExclusionMethod::ByValue,
meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: false,
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,
sort_votes: false,
pp_decimals: 2,
};
let stv_opts = stv::STVOptionsBuilder::default()
.round_surplus_fractions(Some(2))
.round_values(Some(2))
.round_votes(Some(2))
.round_quota(Some(2))
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.quota_mode(stv::QuotaMode::ERS97)
.surplus(stv::SurplusMethod::EG)
.transferable_only(true)
.exclusion(stv::ExclusionMethod::ByValue)
.early_bulk_elect(false)
.bulk_exclude(true)
.defer_surpluses(true)
.build().unwrap();
utils::read_validate_election::<Rational>("tests/data/ers97.csv", "tests/data/ers97.blt", stv_opts, None, &["nt", "vre"]);
}

View File

@ -24,70 +24,31 @@ use opentally::stv;
// Compare ers97.blt count with result produced by 1987 HillWichmannWoodall reference implementation
#[test]
fn meek87_ers97_float64() {
let stv_opts = stv::STVOptions {
round_surplus_fractions: None,
round_values: None,
round_votes: None,
round_quota: None,
sum_surplus_transfers: stv::SumSurplusTransfersMode::SingleStep,
meek_surplus_tolerance: String::from("0.001%"),
normalise_ballots: false,
quota: stv::QuotaType::DroopExact,
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: false,
sample_per_ballot: false,
early_bulk_elect: true,
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,
sort_votes: false,
pp_decimals: 2,
};
let stv_opts = stv::STVOptionsBuilder::default()
.meek_surplus_tolerance("0.001%".to_string())
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.surplus(stv::SurplusMethod::Meek)
.build().unwrap();
utils::read_validate_election::<NativeFloat64>("tests/data/ers97_meek.csv", "tests/data/ers97.blt", stv_opts, Some(2), &["exhausted", "quota"]);
}
// Compare ers97.blt count with result produced by OpenSTV 1.7 "Meek STV"
#[test]
fn meek06_ers97_fixed12() {
let stv_opts = stv::STVOptions {
round_surplus_fractions: Some(9),
round_values: 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: false,
sample_per_ballot: false,
early_bulk_elect: true,
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,
sort_votes: false,
pp_decimals: 2,
};
let stv_opts = stv::STVOptionsBuilder::default()
.round_surplus_fractions(Some(9))
.round_values(Some(9))
.round_votes(Some(9))
.round_quota(Some(9))
.meek_surplus_tolerance("0.0001".to_string())
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.surplus(stv::SurplusMethod::Meek)
.defer_surpluses(true)
.meek_immediate_elect(true)
.build().unwrap();
Fixed::set_dps(12);
// Read BLT
@ -135,35 +96,20 @@ fn meek06_ers97_fixed12() {
// Compare ers97.blt count with result produced by OpenSTV 1.7 "New Zealand Meek STV" (same result as Hill 2006 implementation)
#[test]
fn meeknz_ers97_fixed12() {
let stv_opts = stv::STVOptions {
round_surplus_fractions: Some(9),
round_values: 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,
sample_per_ballot: false,
early_bulk_elect: true,
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,
sort_votes: false,
pp_decimals: 2,
};
let stv_opts = stv::STVOptionsBuilder::default()
.round_surplus_fractions(Some(9))
.round_values(Some(9))
.round_votes(Some(9))
.round_quota(Some(9))
.meek_surplus_tolerance("0.0001".to_string())
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.surplus(stv::SurplusMethod::Meek)
.meek_nz_exclusion(true)
.defer_surpluses(true)
.meek_immediate_elect(true)
.build().unwrap();
Fixed::set_dps(12);
// Read BLT

View File

@ -22,34 +22,19 @@ use opentally::stv;
#[test]
fn prsa1_rational() {
let stv_opts = stv::STVOptions {
round_surplus_fractions: Some(3),
round_values: Some(3),
round_votes: Some(3),
round_quota: Some(3),
sum_surplus_transfers: stv::SumSurplusTransfersMode::SingleStep,
meek_surplus_tolerance: String::new(),
normalise_ballots: false,
quota: stv::QuotaType::Droop,
quota_criterion: stv::QuotaCriterion::GreaterOrEqual,
quota_mode: stv::QuotaMode::Static,
ties: vec![],
surplus: stv::SurplusMethod::EG,
surplus_order: stv::SurplusOrder::ByOrder,
transferable_only: true,
exclusion: stv::ExclusionMethod::ParcelsByOrder,
meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: false, // Required for validation
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,
sort_votes: false,
pp_decimals: 2,
};
let stv_opts = stv::STVOptionsBuilder::default()
.round_surplus_fractions(Some(3))
.round_values(Some(3))
.round_votes(Some(3))
.round_quota(Some(3))
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.surplus(stv::SurplusMethod::EG)
.surplus_order(stv::SurplusOrder::ByOrder)
.transferable_only(true)
.exclusion(stv::ExclusionMethod::ParcelsByOrder)
.early_bulk_elect(false)
.build().unwrap();
utils::read_validate_election::<Rational>("tests/data/prsa1.csv", "tests/data/prsa1.blt", stv_opts, None, &["exhausted", "lbf"]);
}

View File

@ -26,70 +26,38 @@ use std::ops;
#[test]
fn scotland_linn07_fixed5() {
let stv_opts = stv::STVOptions {
round_surplus_fractions: Some(5),
round_values: Some(5),
round_votes: Some(5),
round_quota: Some(0),
sum_surplus_transfers: stv::SumSurplusTransfersMode::PerBallot,
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::WIG,
surplus_order: stv::SurplusOrder::BySize,
transferable_only: false,
exclusion: stv::ExclusionMethod::SingleStage,
meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: false,
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,
sort_votes: false,
pp_decimals: 5,
};
let stv_opts = stv::STVOptionsBuilder::default()
.round_surplus_fractions(Some(5))
.round_values(Some(5))
.round_votes(Some(5))
.round_quota(Some(0))
.sum_surplus_transfers(stv::SumSurplusTransfersMode::PerBallot)
.normalise_ballots(true)
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.early_bulk_elect(false)
.pp_decimals(5)
.build().unwrap();
Fixed::set_dps(5);
scotland_linn07::<Fixed>(stv_opts);
}
#[test]
fn scotland_linn07_gfixed5() {
let stv_opts = stv::STVOptions {
round_surplus_fractions: Some(5),
round_values: Some(5),
round_votes: Some(5),
round_quota: Some(0),
sum_surplus_transfers: stv::SumSurplusTransfersMode::PerBallot,
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::WIG,
surplus_order: stv::SurplusOrder::BySize,
transferable_only: false,
exclusion: stv::ExclusionMethod::SingleStage,
meek_nz_exclusion: false,
sample_per_ballot: false,
early_bulk_elect: false,
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,
sort_votes: false,
pp_decimals: 5,
};
let stv_opts = stv::STVOptionsBuilder::default()
.round_surplus_fractions(Some(5))
.round_values(Some(5))
.round_votes(Some(5))
.round_quota(Some(0))
.sum_surplus_transfers(stv::SumSurplusTransfersMode::PerBallot)
.normalise_ballots(true)
.quota(stv::QuotaType::Droop)
.quota_criterion(stv::QuotaCriterion::GreaterOrEqual)
.early_bulk_elect(false)
.pp_decimals(5)
.build().unwrap();
GuardedFixed::set_dps(5);
scotland_linn07::<GuardedFixed>(stv_opts);
}