diff --git a/coverage.sh b/coverage.sh index 157a42f..ba1b362 100755 --- a/coverage.sh +++ b/coverage.sh @@ -9,10 +9,11 @@ export CARGO_TARGET_DIR=target/coverage cargo test llvm-profdata merge -sparse target/coverage/prof/*.profraw -o target/coverage/opentally.profdata +for file in $(cargo test --no-run --message-format=json 2>/dev/null | jq -r "select(.profile.test == true) | .filenames[]"); do echo -n --object '"'$file'" '; done > target/coverage/objects # Need "eval" to correctly parse arguments eval llvm-cov show target/coverage/debug/opentally -instr-profile=target/coverage/opentally.profdata -Xdemangler="$HOME/.cargo/bin/rustfilt" \ - $(for file in $(cargo test --no-run --message-format=json 2>/dev/null | jq -r "select(.profile.test == true) | .filenames[]"); do echo -n --object '"'$file'" '; done) \ + $(cat target/coverage/objects) \ -ignore-filename-regex="$HOME/." \ -ignore-filename-regex=rustc \ -ignore-filename-regex=numbers/rational_num.rs \ diff --git a/src/election.rs b/src/election.rs index ad2327a..53badf0 100644 --- a/src/election.rs +++ b/src/election.rs @@ -35,7 +35,6 @@ use std::collections::HashMap; use std::fmt; /// An election to be counted -#[derive(Clone)] #[cfg_attr(not(target_arch = "wasm32"), derive(Archive, Deserialize, Serialize))] pub struct Election { /// Name of the election @@ -93,7 +92,7 @@ impl Election { } /// A candidate in an [Election] -#[derive(Clone, Eq, Hash, PartialEq)] +#[derive(Eq, Hash, PartialEq)] #[cfg_attr(not(target_arch = "wasm32"), derive(Archive, Deserialize, Serialize))] pub struct Candidate { /// Name of the candidate @@ -465,7 +464,6 @@ impl<'a, N> Vote<'a, N> { } /// A record of a voter's preferences -#[derive(Clone)] #[cfg_attr(not(target_arch = "wasm32"), derive(Archive, Deserialize, Serialize))] pub struct Ballot { /// Original value/weight of the ballot diff --git a/src/logger.rs b/src/logger.rs index 33b5426..4af14e4 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -16,7 +16,6 @@ */ /// Smart logger used in election counts -#[derive(Clone)] pub struct Logger<'a> { /// [Vec] of log entries for the current stage pub entries: Vec>, @@ -72,7 +71,6 @@ impl<'a> Logger<'a> { } /// Represents either a literal or smart log entry -#[derive(Clone)] pub enum LogEntry<'a> { /// Smart log entry - see [SmartLogEntry] Smart(SmartLogEntry<'a>), @@ -81,7 +79,6 @@ pub enum LogEntry<'a> { } /// Smart log entry -#[derive(Clone)] pub struct SmartLogEntry<'a> { template1: &'a str, template2: &'a str, diff --git a/src/numbers/fixed.rs b/src/numbers/fixed.rs index 7f2fa84..594de22 100644 --- a/src/numbers/fixed.rs +++ b/src/numbers/fixed.rs @@ -176,6 +176,16 @@ impl Assign<&Self> for Fixed { fn assign(&mut self, src: &Self) { self.0 = src.0.clone() } } +#[test] +fn assign() { + Fixed::set_dps(2); + let a = Fixed::parse("123.45"); + let b = Fixed::parse("678.90"); + + let mut x = a.clone(); x.assign(b.clone()); assert_eq!(x, b); + let mut x = a.clone(); x.assign(&b); assert_eq!(x, b); +} + impl From for Fixed { fn from(n: usize) -> Self { Self(IBig::from(n) * get_factor()) } } @@ -219,6 +229,13 @@ impl fmt::Display for Fixed { } } +#[test] +fn display_debug() { + Fixed::set_dps(2); + let x = Fixed::parse("123.4"); assert_eq!(format!("{}", x), "123.40"); + let x = Fixed::parse("123.4"); assert_eq!(format!("{:?}", x), "Fixed(12340)"); +} + impl One for Fixed { fn one() -> Self { Self(get_factor().clone()) } } @@ -255,9 +272,7 @@ impl ops::Div for Fixed { impl ops::Rem for Fixed { type Output = Self; - fn rem(self, _rhs: Self) -> Self::Output { - todo!() - } + fn rem(self, rhs: Self) -> Self::Output { Self(self.0 % rhs.0) } } #[test] @@ -270,6 +285,7 @@ fn arith_owned_owned() { assert_eq!(a.clone() - b.clone(), Fixed::parse("-555.45")); assert_eq!(a.clone() * b.clone(), Fixed::parse("83810.20")); // = 83810.205 rounds to 83810.20 assert_eq!(a.clone() / b.clone(), Fixed::parse("0.18")); + assert_eq!(b.clone() % a.clone(), Fixed::parse("61.65")); } impl ops::Add<&Self> for Fixed { @@ -294,9 +310,7 @@ impl ops::Div<&Self> for Fixed { impl ops::Rem<&Self> for Fixed { type Output = Self; - fn rem(self, _rhs: &Self) -> Self::Output { - todo!() - } + fn rem(self, rhs: &Self) -> Self::Output { Self(self.0 % &rhs.0) } } #[test] @@ -309,6 +323,7 @@ fn arith_owned_ref() { assert_eq!(a.clone() - &b, Fixed::parse("-555.45")); assert_eq!(a.clone() * &b, Fixed::parse("83810.20")); assert_eq!(a.clone() / &b, Fixed::parse("0.18")); + assert_eq!(b.clone() % &a, Fixed::parse("61.65")); } impl ops::AddAssign for Fixed { @@ -334,9 +349,7 @@ impl ops::DivAssign for Fixed { } impl ops::RemAssign for Fixed { - fn rem_assign(&mut self, _rhs: Self) { - todo!() - } + fn rem_assign(&mut self, rhs: Self) { self.0 %= rhs.0; } } #[test] @@ -349,6 +362,7 @@ fn arithassign_owned() { let mut x = a.clone(); x -= b.clone(); assert_eq!(x, Fixed::parse("-555.45")); let mut x = a.clone(); x *= b.clone(); assert_eq!(x, Fixed::parse("83810.20")); let mut x = a.clone(); x /= b.clone(); assert_eq!(x, Fixed::parse("0.18")); + let mut x = b.clone(); x %= a.clone(); assert_eq!(x, Fixed::parse("61.65")); } impl ops::AddAssign<&Self> for Fixed { @@ -374,9 +388,7 @@ impl ops::DivAssign<&Self> for Fixed { } impl ops::RemAssign<&Self> for Fixed { - fn rem_assign(&mut self, _rhs: &Self) { - todo!() - } + fn rem_assign(&mut self, rhs: &Self) { self.0 %= &rhs.0; } } #[test] @@ -389,6 +401,7 @@ fn arithassign_ref() { let mut x = a.clone(); x -= &b; assert_eq!(x, Fixed::parse("-555.45")); let mut x = a.clone(); x *= &b; assert_eq!(x, Fixed::parse("83810.20")); let mut x = a.clone(); x /= &b; assert_eq!(x, Fixed::parse("0.18")); + let mut x = b.clone(); x %= &a; assert_eq!(x, Fixed::parse("61.65")); } impl ops::Neg for &Fixed { @@ -418,9 +431,7 @@ impl ops::Div for &Fixed { impl ops::Rem for &Fixed { type Output = Fixed; - fn rem(self, _rhs: Self) -> Self::Output { - todo!() - } + fn rem(self, rhs: Self) -> Self::Output { Fixed(&self.0 % &rhs.0) } } #[test] @@ -433,6 +444,7 @@ fn arith_ref_ref() { assert_eq!(&a - &b, Fixed::parse("-555.45")); assert_eq!(&a * &b, Fixed::parse("83810.20")); assert_eq!(&a / &b, Fixed::parse("0.18")); + assert_eq!(&b % &a, Fixed::parse("61.65")); } /* diff --git a/src/numbers/gfixed.rs b/src/numbers/gfixed.rs index 44862d1..e37b928 100644 --- a/src/numbers/gfixed.rs +++ b/src/numbers/gfixed.rs @@ -220,6 +220,16 @@ impl Assign<&Self> for GuardedFixed { fn assign(&mut self, src: &Self) { self.0 = src.0.clone() } } +#[test] +fn assign() { + GuardedFixed::set_dps(2); + let a = GuardedFixed::parse("123.45"); + let b = GuardedFixed::parse("678.90"); + + let mut x = a.clone(); x.assign(b.clone()); assert_eq!(x, b); + let mut x = a.clone(); x.assign(&b); assert_eq!(x, b); +} + impl From for GuardedFixed { fn from(n: usize) -> Self { Self(IBig::from(n) * get_factor()) } } @@ -262,6 +272,13 @@ impl fmt::Display for GuardedFixed { } } +#[test] +fn display_debug() { + GuardedFixed::set_dps(2); + let x = GuardedFixed::parse("123.4"); assert_eq!(format!("{}", x), "123.40"); + let x = GuardedFixed::parse("123.4"); assert_eq!(format!("{:?}", x), "GuardedFixed(1234000)"); +} + impl One for GuardedFixed { fn one() -> Self { Self(get_factor().clone()) } } @@ -298,9 +315,7 @@ impl ops::Div for GuardedFixed { impl ops::Rem for GuardedFixed { type Output = Self; - fn rem(self, _rhs: Self) -> Self::Output { - todo!() - } + fn rem(self, rhs: Self) -> Self::Output { Self(self.0 % rhs.0) } } #[test] @@ -313,6 +328,7 @@ fn arith_owned_owned() { assert_eq!(a.clone() - b.clone(), GuardedFixed::parse("-555.45")); assert_eq!(a.clone() * b.clone(), GuardedFixed::parse("83810.205")); // Must compare to 3 d.p.s as doesn't meet FACTOR_CMP assert_eq!(a.clone() / b.clone(), GuardedFixed::parse("0.18")); // Meets FACTOR_CMP so compare only 2 d.p.s + assert_eq!(b.clone() % a.clone(), GuardedFixed::parse("61.65")); } impl ops::Add<&Self> for GuardedFixed { @@ -337,9 +353,7 @@ impl ops::Div<&Self> for GuardedFixed { impl ops::Rem<&Self> for GuardedFixed { type Output = Self; - fn rem(self, _rhs: &Self) -> Self::Output { - todo!() - } + fn rem(self, rhs: &Self) -> Self::Output { Self(self.0 % &rhs.0) } } #[test] @@ -352,6 +366,7 @@ fn arith_owned_ref() { assert_eq!(a.clone() - &b, GuardedFixed::parse("-555.45")); assert_eq!(a.clone() * &b, GuardedFixed::parse("83810.205")); assert_eq!(a.clone() / &b, GuardedFixed::parse("0.18")); + assert_eq!(b.clone() % &a, GuardedFixed::parse("61.65")); } impl ops::AddAssign for GuardedFixed { @@ -377,9 +392,7 @@ impl ops::DivAssign for GuardedFixed { } impl ops::RemAssign for GuardedFixed { - fn rem_assign(&mut self, _rhs: Self) { - todo!() - } + fn rem_assign(&mut self, rhs: Self) { self.0 %= rhs.0; } } #[test] @@ -392,6 +405,7 @@ fn arithassign_owned() { let mut x = a.clone(); x -= b.clone(); assert_eq!(x, GuardedFixed::parse("-555.45")); let mut x = a.clone(); x *= b.clone(); assert_eq!(x, GuardedFixed::parse("83810.205")); let mut x = a.clone(); x /= b.clone(); assert_eq!(x, GuardedFixed::parse("0.18")); + let mut x = b.clone(); x %= a.clone(); assert_eq!(x, GuardedFixed::parse("61.65")); } impl ops::AddAssign<&Self> for GuardedFixed { @@ -417,9 +431,7 @@ impl ops::DivAssign<&Self> for GuardedFixed { } impl ops::RemAssign<&Self> for GuardedFixed { - fn rem_assign(&mut self, _rhs: &Self) { - todo!() - } + fn rem_assign(&mut self, rhs: &Self) { self.0 %= &rhs.0; } } #[test] @@ -432,6 +444,7 @@ fn arithassign_ref() { let mut x = a.clone(); x -= &b; assert_eq!(x, GuardedFixed::parse("-555.45")); let mut x = a.clone(); x *= &b; assert_eq!(x, GuardedFixed::parse("83810.205")); let mut x = a.clone(); x /= &b; assert_eq!(x, GuardedFixed::parse("0.18")); + let mut x = b.clone(); x %= &a; assert_eq!(x, GuardedFixed::parse("61.65")); } impl ops::Neg for &GuardedFixed { @@ -461,9 +474,7 @@ impl ops::Div for &GuardedFixed { impl ops::Rem for &GuardedFixed { type Output = GuardedFixed; - fn rem(self, _rhs: Self) -> Self::Output { - todo!() - } + fn rem(self, rhs: Self) -> Self::Output { GuardedFixed(&self.0 % &rhs.0) } } #[test] @@ -476,6 +487,7 @@ fn arith_ref_ref() { assert_eq!(&a - &b, GuardedFixed::parse("-555.45")); assert_eq!(&a * &b, GuardedFixed::parse("83810.205")); assert_eq!(&a / &b, GuardedFixed::parse("0.18")); + assert_eq!(&b % &a, GuardedFixed::parse("61.65")); } /* diff --git a/src/numbers/native.rs b/src/numbers/native.rs index dee6fa4..1efee47 100644 --- a/src/numbers/native.rs +++ b/src/numbers/native.rs @@ -54,6 +54,12 @@ impl Number for NativeFloat64 { } } +#[test] +fn display_debug() { + let x = NativeFloat64::parse("123.4"); assert_eq!(format!("{}", x), format!("{}", 123.40_f64)); + let x = NativeFloat64::parse("123.4"); assert_eq!(format!("{:?}", x), format!("NativeFloat64({})", 123.40_f64)); +} + #[test] fn rounding() { let mut x = NativeFloat64::parse("55.550"); x.floor_mut(2); assert_eq!(x, NativeFloat64::parse("55.55")); @@ -90,6 +96,15 @@ impl Assign<&NativeFloat64> for NativeFloat64 { fn assign(&mut self, src: &NativeFloat64) { self.0 = src.0; } } +#[test] +fn assign() { + let a = NativeFloat64::parse("123.45"); + let b = NativeFloat64::parse("678.90"); + + let mut x = a.clone(); x.assign(b.clone()); assert_eq!(x, b); + let mut x = a.clone(); x.assign(&b); assert_eq!(x, b); +} + impl From for NativeFloat64 { fn from(n: usize) -> Self { Self(n as ImplType) } } @@ -139,9 +154,7 @@ impl ops::Div for NativeFloat64 { impl ops::Rem for NativeFloat64 { type Output = NativeFloat64; - fn rem(self, _rhs: Self) -> Self::Output { - todo!() - } + fn rem(self, rhs: Self) -> Self::Output { Self(self.0 % rhs.0) } } #[test] @@ -153,33 +166,32 @@ fn arith_owned_owned() { assert_eq!(a.clone() - b.clone(), NativeFloat64::from(123.45_f64 - 678.90_f64)); assert_eq!(a.clone() * b.clone(), NativeFloat64::from(123.45_f64 * 678.90_f64)); assert_eq!(a.clone() / b.clone(), NativeFloat64::from(123.45_f64 / 678.90_f64)); + assert_eq!(b.clone() % a.clone(), NativeFloat64::from(678.90_f64 % 123.45_f64)); } impl ops::Add<&NativeFloat64> for NativeFloat64 { type Output = NativeFloat64; - fn add(self, rhs: &NativeFloat64) -> Self::Output { Self(self.0 + &rhs.0) } + fn add(self, rhs: &NativeFloat64) -> Self::Output { Self(self.0 + rhs.0) } } impl ops::Sub<&NativeFloat64> for NativeFloat64 { type Output = NativeFloat64; - fn sub(self, rhs: &NativeFloat64) -> Self::Output { Self(self.0 - &rhs.0) } + fn sub(self, rhs: &NativeFloat64) -> Self::Output { Self(self.0 - rhs.0) } } impl ops::Mul<&NativeFloat64> for NativeFloat64 { type Output = NativeFloat64; - fn mul(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(self.0 * &rhs.0) } + fn mul(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(self.0 * rhs.0) } } impl ops::Div<&NativeFloat64> for NativeFloat64 { type Output = NativeFloat64; - fn div(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(self.0 / &rhs.0) } + fn div(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(self.0 / rhs.0) } } impl ops::Rem<&NativeFloat64> for NativeFloat64 { type Output = NativeFloat64; - fn rem(self, _rhs: &NativeFloat64) -> Self::Output { - todo!() - } + fn rem(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(self.0 % rhs.0) } } #[test] @@ -191,6 +203,7 @@ fn arith_owned_ref() { assert_eq!(a.clone() - &b, NativeFloat64::from(123.45_f64 - 678.90_f64)); assert_eq!(a.clone() * &b, NativeFloat64::from(123.45_f64 * 678.90_f64)); assert_eq!(a.clone() / &b, NativeFloat64::from(123.45_f64 / 678.90_f64)); + assert_eq!(b.clone() % &a, NativeFloat64::from(678.90_f64 % 123.45_f64)); } impl ops::AddAssign for NativeFloat64 { @@ -206,13 +219,11 @@ impl ops::MulAssign for NativeFloat64 { } impl ops::DivAssign for NativeFloat64 { - fn div_assign(&mut self, rhs: Self) { self.0 /= &rhs.0; } + fn div_assign(&mut self, rhs: Self) { self.0 /= rhs.0; } } impl ops::RemAssign for NativeFloat64 { - fn rem_assign(&mut self, _rhs: Self) { - todo!() - } + fn rem_assign(&mut self, rhs: Self) { self.0 %= rhs.0; } } #[test] @@ -224,28 +235,27 @@ fn arithassign_owned() { let mut x = a.clone(); x -= b.clone(); assert_eq!(x, NativeFloat64::from(123.45_f64 - 678.90_f64)); let mut x = a.clone(); x *= b.clone(); assert_eq!(x, NativeFloat64::from(123.45_f64 * 678.90_f64)); let mut x = a.clone(); x /= b.clone(); assert_eq!(x, NativeFloat64::from(123.45_f64 / 678.90_f64)); + let mut x = b.clone(); x %= a.clone(); assert_eq!(x, NativeFloat64::from(678.90_f64 % 123.45_f64)); } impl ops::AddAssign<&NativeFloat64> for NativeFloat64 { - fn add_assign(&mut self, rhs: &NativeFloat64) { self.0 += &rhs.0; } + fn add_assign(&mut self, rhs: &NativeFloat64) { self.0 += rhs.0; } } impl ops::SubAssign<&NativeFloat64> for NativeFloat64 { - fn sub_assign(&mut self, rhs: &NativeFloat64) { self.0 -= &rhs.0; } + fn sub_assign(&mut self, rhs: &NativeFloat64) { self.0 -= rhs.0; } } impl ops::MulAssign<&NativeFloat64> for NativeFloat64 { - fn mul_assign(&mut self, rhs: &NativeFloat64) { self.0 *= &rhs.0; } + fn mul_assign(&mut self, rhs: &NativeFloat64) { self.0 *= rhs.0; } } impl ops::DivAssign<&NativeFloat64> for NativeFloat64 { - fn div_assign(&mut self, rhs: &NativeFloat64) { self.0 /= &rhs.0; } + fn div_assign(&mut self, rhs: &NativeFloat64) { self.0 /= rhs.0; } } impl ops::RemAssign<&NativeFloat64> for NativeFloat64 { - fn rem_assign(&mut self, _rhs: &NativeFloat64) { - todo!() - } + fn rem_assign(&mut self, rhs: &NativeFloat64) { self.0 %= rhs.0; } } #[test] @@ -257,38 +267,37 @@ fn arithassign_ref() { let mut x = a.clone(); x -= &b; assert_eq!(x, NativeFloat64::from(123.45_f64 - 678.90_f64)); let mut x = a.clone(); x *= &b; assert_eq!(x, NativeFloat64::from(123.45_f64 * 678.90_f64)); let mut x = a.clone(); x /= &b; assert_eq!(x, NativeFloat64::from(123.45_f64 / 678.90_f64)); + let mut x = b.clone(); x %= &a; assert_eq!(x, NativeFloat64::from(678.90_f64 % 123.45_f64)); } impl ops::Neg for &NativeFloat64 { type Output = NativeFloat64; - fn neg(self) -> Self::Output { NativeFloat64(-&self.0) } + fn neg(self) -> Self::Output { NativeFloat64(-self.0) } } impl ops::Add for &NativeFloat64 { type Output = NativeFloat64; - fn add(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(&self.0 + &rhs.0) } + fn add(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(self.0 + rhs.0) } } impl ops::Sub for &NativeFloat64 { type Output = NativeFloat64; - fn sub(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(&self.0 - &rhs.0) } + fn sub(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(self.0 - rhs.0) } } impl ops::Mul for &NativeFloat64 { type Output = NativeFloat64; - fn mul(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(&self.0 * &rhs.0) } + fn mul(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(self.0 * rhs.0) } } impl ops::Div for &NativeFloat64 { type Output = NativeFloat64; - fn div(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(&self.0 / &rhs.0) } + fn div(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(self.0 / rhs.0) } } impl ops::Rem for &NativeFloat64 { type Output = NativeFloat64; - fn rem(self, _rhs: &NativeFloat64) -> Self::Output { - todo!() - } + fn rem(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(self.0 % rhs.0) } } #[test] @@ -300,6 +309,7 @@ fn arith_ref_ref() { assert_eq!(&a - &b, NativeFloat64::from(123.45_f64 - 678.90_f64)); assert_eq!(&a * &b, NativeFloat64::from(123.45_f64 * 678.90_f64)); assert_eq!(&a / &b, NativeFloat64::from(123.45_f64 / 678.90_f64)); + assert_eq!(&b % &a, NativeFloat64::from(678.90_f64 % 123.45_f64)); } /* diff --git a/src/numbers/rational_rug.rs b/src/numbers/rational_rug.rs index f1cfc14..ce3dd28 100644 --- a/src/numbers/rational_rug.rs +++ b/src/numbers/rational_rug.rs @@ -104,6 +104,12 @@ impl Number for Rational { } } +#[test] +fn describe() { + // Never executed - just for the sake of code coverage + assert_eq!(Rational::describe(), "--numbers rational"); +} + #[test] fn rounding() { let mut x = Rational::parse("55.550"); x.floor_mut(2); assert_eq!(x, Rational::parse("55.55")); @@ -140,6 +146,15 @@ impl Assign<&Self> for Rational { fn assign(&mut self, src: &Self) { self.0.assign(&src.0) } } +#[test] +fn assign() { + let a = Rational::parse("123.45"); + let b = Rational::parse("678.90"); + + let mut x = a.clone(); x.assign(b.clone()); assert_eq!(x, b); + let mut x = a.clone(); x.assign(&b); assert_eq!(x, b); +} + impl From for Rational { fn from(n: usize) -> Self { Self(rug::Rational::from(n)) } } @@ -175,6 +190,13 @@ impl fmt::Display for Rational { } } +#[test] +fn display_debug() { + let x = Rational::parse("123.4"); assert_eq!(format!("{}", x), "617/5"); + let x = Rational::parse("123.4"); assert_eq!(format!("{:.2}", x), "123.40"); + let x = Rational::parse("123.4"); assert_eq!(format!("{:?}", x), "Rational(617/5)"); +} + impl One for Rational { fn one() -> Self { Self(rug::Rational::from(1)) } } @@ -216,8 +238,12 @@ impl ops::Div for Rational { impl ops::Rem for Rational { type Output = Self; - fn rem(self, _rhs: Self) -> Self::Output { - todo!() + fn rem(self, rhs: Self) -> Self::Output { + // TODO: Is there a cleaner way of implementing this? + let mut quotient = self.0 / &rhs.0; + quotient.rem_trunc_mut(); + quotient *= rhs.0; + return Self(quotient); } } @@ -229,7 +255,8 @@ fn arith_owned_owned() { assert_eq!(a.clone() + b.clone(), Rational::parse("802.35")); assert_eq!(a.clone() - b.clone(), Rational::parse("-555.45")); assert_eq!(a.clone() * b.clone(), Rational::parse("83810.205")); - assert_eq!((a.clone() / b.clone()) * b, a); + assert_eq!((a.clone() / b.clone()) * b.clone(), a); + assert_eq!(b.clone() % a.clone(), Rational::parse("61.65")); } impl ops::Add<&Self> for Rational { @@ -254,8 +281,11 @@ impl ops::Div<&Self> for Rational { impl ops::Rem<&Self> for Rational { type Output = Self; - fn rem(self, _rhs: &Self) -> Self::Output { - todo!() + fn rem(self, rhs: &Self) -> Self::Output { + let mut quotient = self.0 / &rhs.0; + quotient.rem_trunc_mut(); + quotient *= &rhs.0; + return Self(quotient); } } @@ -267,7 +297,8 @@ fn arith_owned_ref() { assert_eq!(a.clone() + &b, Rational::parse("802.35")); assert_eq!(a.clone() - &b, Rational::parse("-555.45")); assert_eq!(a.clone() * &b, Rational::parse("83810.205")); - assert_eq!((a.clone() / &b) * b, a); + assert_eq!((a.clone() / &b) * &b, a); + assert_eq!(b.clone() % &a, Rational::parse("61.65")); } impl ops::AddAssign for Rational { @@ -287,8 +318,10 @@ impl ops::DivAssign for Rational { } impl ops::RemAssign for Rational { - fn rem_assign(&mut self, _rhs: Self) { - todo!() + fn rem_assign(&mut self, rhs: Self) { + self.0 /= &rhs.0; + self.0.rem_trunc_mut(); + self.0 *= rhs.0; } } @@ -300,7 +333,8 @@ fn arithassign_owned() { let mut x = a.clone(); x += b.clone(); assert_eq!(x, Rational::parse("802.35")); let mut x = a.clone(); x -= b.clone(); assert_eq!(x, Rational::parse("-555.45")); let mut x = a.clone(); x *= b.clone(); assert_eq!(x, Rational::parse("83810.205")); - let mut x = a.clone(); x /= b.clone(); x *= b; assert_eq!(x, a); + let mut x = a.clone(); x /= b.clone(); x *= &b; assert_eq!(x, a); + let mut x = b.clone(); x %= a.clone(); assert_eq!(x, Rational::parse("61.65")); } impl ops::AddAssign<&Self> for Rational { @@ -320,8 +354,10 @@ impl ops::DivAssign<&Self> for Rational { } impl ops::RemAssign<&Self> for Rational { - fn rem_assign(&mut self, _rhs: &Self) { - todo!() + fn rem_assign(&mut self, rhs: &Self) { + self.0 /= &rhs.0; + self.0.rem_trunc_mut(); + self.0 *= &rhs.0; } } @@ -333,7 +369,8 @@ fn arithassign_ref() { let mut x = a.clone(); x += &b; assert_eq!(x, Rational::parse("802.35")); let mut x = a.clone(); x -= &b; assert_eq!(x, Rational::parse("-555.45")); let mut x = a.clone(); x *= &b; assert_eq!(x, Rational::parse("83810.205")); - let mut x = a.clone(); x /= &b; x *= b; assert_eq!(x, a); + let mut x = a.clone(); x /= &b; x *= &b; assert_eq!(x, a); + let mut x = b.clone(); x %= &a; assert_eq!(x, Rational::parse("61.65")); } impl ops::Neg for &Rational { @@ -363,8 +400,11 @@ impl ops::Div for &Rational { impl ops::Rem for &Rational { type Output = Rational; - fn rem(self, _rhs: Self) -> Self::Output { - todo!() + fn rem(self, rhs: Self) -> Self::Output { + let mut quotient = rug::Rational::from(&self.0 / &rhs.0); + quotient.rem_trunc_mut(); + quotient *= &rhs.0; + return Rational(quotient); } } @@ -376,7 +416,8 @@ fn arith_ref_ref() { assert_eq!(&a + &b, Rational::parse("802.35")); assert_eq!(&a - &b, Rational::parse("-555.45")); assert_eq!(&a * &b, Rational::parse("83810.205")); - assert_eq!((&a / &b) * b, a); + assert_eq!((&a / &b) * &b, a); + assert_eq!(&b % &a, Rational::parse("61.65")); } /* diff --git a/src/sharandom.rs b/src/sharandom.rs index 940cc8e..9af16e6 100644 --- a/src/sharandom.rs +++ b/src/sharandom.rs @@ -19,7 +19,6 @@ use ibig::UBig; use sha2::{Digest, Sha256}; /// Deterministic random number generator using SHA256 -#[derive(Clone)] pub struct SHARandom<'r> { seed: &'r str, counter: usize, diff --git a/src/stv/gregory/transfers.rs b/src/stv/gregory/transfers.rs index 038b876..1da8653 100644 --- a/src/stv/gregory/transfers.rs +++ b/src/stv/gregory/transfers.rs @@ -460,15 +460,15 @@ impl<'e, N: Number> TransferTable<'e, N> { } /// Render table as plain text - #[cfg(not(target_arch = "wasm32"))] + //#[cfg(not(target_arch = "wasm32"))] pub fn render_text(&self, opts: &STVOptions) -> String { return self.render(opts).to_string(); } - /// Render table as HTML - pub fn render_html(&self, opts: &STVOptions) -> String { - return self.render(opts).to_string(); - } + // Render table as HTML + //pub fn render_html(&self, opts: &STVOptions) -> String { + // return self.render(opts).to_string(); + //} } /// Column in a [TransferTable] diff --git a/src/stv/mod.rs b/src/stv/mod.rs index 38e4908..bf4c7ac 100644 --- a/src/stv/mod.rs +++ b/src/stv/mod.rs @@ -68,7 +68,7 @@ pub struct STVOptions { pub sum_surplus_transfers: SumSurplusTransfersMode, /// (Meek STV) Limit for stopping iteration of surplus distribution - #[builder(default)] + #[builder(default=r#"String::from("0.001%")"#)] pub meek_surplus_tolerance: String, /// Convert ballots with value >1 to multiple ballots of value 1 (used only for [STVOptions::describe]) @@ -88,7 +88,7 @@ pub struct STVOptions { pub quota_mode: QuotaMode, /// Tie-breaking method - #[builder(default)] + #[builder(default="vec![TieStrategy::Prompt]")] pub ties: Vec, /// Method of surplus distributions @@ -180,12 +180,12 @@ impl STVOptions { if self.normalise_ballots { flags.push("--normalise-ballots".to_string()); } if self.quota != QuotaType::Droop { flags.push(self.quota.describe()); } if self.quota_criterion != QuotaCriterion::Greater { flags.push(self.quota_criterion.describe()); } - if self.surplus != SurplusMethod::Meek && self.quota_mode != QuotaMode::Static { flags.push(self.quota_mode.describe()); } + if self.quota_mode != QuotaMode::Static { flags.push(self.quota_mode.describe()); } let ties_str = self.ties.iter().map(|t| t.describe()).join(" "); if ties_str != "prompt" { flags.push(format!("--ties {}", ties_str)); } for t in self.ties.iter() { if let TieStrategy::Random(seed) = t { flags.push(format!("--random-seed {}", seed)); } } if self.surplus != SurplusMethod::WIG { flags.push(self.surplus.describe()); } - if self.surplus == SurplusMethod::WIG || self.surplus == SurplusMethod::UIG || self.surplus == SurplusMethod::EG { + if self.surplus != SurplusMethod::Meek { if self.surplus_order != SurplusOrder::BySize { flags.push(self.surplus_order.describe()); } if self.transferable_only { flags.push("--transferable-only".to_string()); } if self.exclusion != ExclusionMethod::SingleStage { flags.push(self.exclusion.describe()); } diff --git a/src/stv/wasm.rs b/src/stv/wasm.rs index 02ca290..655aecd 100644 --- a/src/stv/wasm.rs +++ b/src/stv/wasm.rs @@ -177,7 +177,7 @@ macro_rules! impl_type { /// Call [render_html](crate::stv::transfers::TransferTable::render_html) on [CountState::transfer_table] pub fn transfer_table_render_html(&self, opts: &STVOptions) -> Option { return match &self.0.transfer_table { - Some(tt) => Some(tt.render_html(&opts.0)), // TODO + Some(tt) => Some(tt.render_text(&opts.0)), // TODO None => None, }; } diff --git a/tests/tests_impl/act.rs b/tests/tests_impl/act.rs index 0b891f5..83ceeca 100644 --- a/tests/tests_impl/act.rs +++ b/tests/tests_impl/act.rs @@ -33,5 +33,7 @@ fn act_kurrajong20_rational() { .early_bulk_elect(false) .build().unwrap(); + assert_eq!(stv_opts.describe::(), "--round-votes 6 --round-quota 0 --quota-criterion geq --surplus eg --surplus-order by_order --transferable-only --exclusion by_value --no-early-bulk-elect"); + utils::read_validate_election::("tests/data/ACT2020Kurrajong.csv", "tests/data/ACT2020Kurrajong.blt", stv_opts, Some(6), &["exhausted", "lbf"]); } diff --git a/tests/tests_impl/aec.rs b/tests/tests_impl/aec.rs index 83bfffd..3c669a6 100644 --- a/tests/tests_impl/aec.rs +++ b/tests/tests_impl/aec.rs @@ -70,5 +70,7 @@ fn aec_tas19_rational() { .bulk_exclude(true) .build().unwrap(); + assert_eq!(stv_opts.describe::(), "--round-votes 0 --round-quota 0 --quota-criterion geq --surplus uig --surplus-order by_order --exclusion by_value --bulk-exclude"); + utils::validate_election::(stages, records, election, stv_opts, None, &["exhausted", "lbf"]); } diff --git a/tests/tests_impl/cambridge.rs b/tests/tests_impl/cambridge.rs index 0b321f9..eb30b37 100644 --- a/tests/tests_impl/cambridge.rs +++ b/tests/tests_impl/cambridge.rs @@ -34,5 +34,7 @@ fn cambridge_cc03_rational() { .min_threshold("49".to_string()) .build().unwrap(); + assert_eq!(stv_opts.describe::(), "--round-quota 0 --normalise-ballots --quota-criterion geq --surplus cincinnati --transferable-only --sample nth_ballot --sample-per-ballot --no-early-bulk-elect --min-threshold 49"); + utils::read_validate_election::("tests/data/CambCC2003.csv", "tests/data/CambCC2003.blt", stv_opts, None, &["exhausted"]); } diff --git a/tests/tests_impl/cli.rs b/tests/tests_impl/cli.rs index 2c15ee6..0db868c 100644 --- a/tests/tests_impl/cli.rs +++ b/tests/tests_impl/cli.rs @@ -19,14 +19,14 @@ use assert_cmd::Command; use predicates::prelude::*; #[test] -fn cli_ers97old() { +fn cli_ers97old_fixed5() { Command::cargo_bin("opentally").expect("Cargo Error") .args(&["stv", "tests/data/ers97old.blt", "--numbers", "fixed", "--decimals", "5", "--round-tvs", "2", "--round-weights", "2", "--round-votes", "2", "--round-quota", "2", "--quota", "droop_exact", "--quota-mode", "ers97", "--surplus", "eg", "--transferable-only", "--exclusion", "by_value"]) .assert().success(); } #[test] -fn cli_ers97_transfers_detail() { +fn cli_ers97_fixed5_transfers_detail() { Command::cargo_bin("opentally").expect("Cargo Error") .args(&["stv", "tests/data/ers97.blt", "--numbers", "fixed", "--decimals", "5", "--round-surplus-fractions", "2", "--round-values", "2", "--round-votes", "2", "--round-quota", "2", "--quota", "droop_exact", "--quota-criterion", "geq", "--quota-mode", "ers97", "--surplus", "eg", "--transferable-only", "--exclusion", "by_value", "--bulk-exclude", "--defer-surpluses", "--transfers-detail"]) .assert().success(); diff --git a/tests/tests_impl/coe.rs b/tests/tests_impl/coe.rs index 60c6542..6ff0e52 100644 --- a/tests/tests_impl/coe.rs +++ b/tests/tests_impl/coe.rs @@ -35,5 +35,7 @@ fn ers97_coe_rational() { .defer_surpluses(true) .build().unwrap(); + assert_eq!(stv_opts.describe::(), "--round-surplus-fractions 2 --round-values 2 --round-votes 2 --round-quota 2 --sum-surplus-transfers per_ballot --quota-criterion geq --surplus eg --transferable-only --exclusion by_value --defer-surpluses"); + utils::read_validate_election::("tests/data/ers97_coe.csv", "tests/data/ers97.blt", stv_opts, None, &["nt"]); } diff --git a/tests/tests_impl/csm.rs b/tests/tests_impl/csm.rs index 76a2c56..c490c79 100644 --- a/tests/tests_impl/csm.rs +++ b/tests/tests_impl/csm.rs @@ -30,5 +30,7 @@ fn csm15_float64() { .bulk_exclude(true) .build().unwrap(); + assert_eq!(stv_opts.describe::(), "--numbers float64 --round-quota 0 --quota-criterion geq --exclusion wright --no-early-bulk-elect --bulk-exclude"); + utils::read_validate_election::("tests/data/CSM15.csv", "tests/data/CSM15.blt", stv_opts, Some(6), &["quota"]); } diff --git a/tests/tests_impl/ers.rs b/tests/tests_impl/ers.rs index c52bed2..ef10128 100644 --- a/tests/tests_impl/ers.rs +++ b/tests/tests_impl/ers.rs @@ -17,7 +17,7 @@ use crate::utils; -use opentally::numbers::Rational; +use opentally::numbers::{Fixed, GuardedFixed, NativeFloat64, Rational}; use opentally::stv; use assert_cmd::Command; @@ -42,6 +42,8 @@ fn ers97old_rational() { .defer_surpluses(true) .build().unwrap(); + assert_eq!(stv_opts.describe::(), "--round-surplus-fractions 2 --round-values 2 --round-votes 2 --round-quota 2 --quota droop_exact --quota-criterion geq --quota-mode ers97 --surplus eg --transferable-only --exclusion by_value --no-early-bulk-elect --bulk-exclude --defer-surpluses"); + utils::read_validate_election::("tests/data/ers97old.csv", "tests/data/ers97old.blt", stv_opts, None, &["nt", "vre"]); } @@ -63,9 +65,84 @@ fn ers97_rational() { .defer_surpluses(true) .build().unwrap(); + assert_eq!(stv_opts.describe::(), "--round-surplus-fractions 2 --round-values 2 --round-votes 2 --round-quota 2 --quota droop_exact --quota-criterion geq --quota-mode ers97 --surplus eg --transferable-only --exclusion by_value --no-early-bulk-elect --bulk-exclude --defer-surpluses"); + utils::read_validate_election::("tests/data/ers97.csv", "tests/data/ers97.blt", stv_opts, None, &["nt", "vre"]); } +#[test] +fn ers97_fixed9() { + let stv_opts = stv::STVOptionsBuilder::default() + .round_surplus_fractions(Some(2)) + .round_values(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) + .transferable_only(true) + .exclusion(stv::ExclusionMethod::ByValue) + .early_bulk_elect(false) + .bulk_exclude(true) + .defer_surpluses(true) + .build().unwrap(); + + Fixed::set_dps(9); + + assert_eq!(stv_opts.describe::(), "--numbers fixed --decimals 9 --round-surplus-fractions 2 --round-values 2 --round-votes 2 --round-quota 2 --quota droop_exact --quota-criterion geq --quota-mode ers97 --surplus eg --transferable-only --exclusion by_value --no-early-bulk-elect --bulk-exclude --defer-surpluses"); + + utils::read_validate_election::("tests/data/ers97.csv", "tests/data/ers97.blt", stv_opts, None, &["nt", "vre"]); +} + +#[test] +fn ers97_gfixed5() { + let stv_opts = stv::STVOptionsBuilder::default() + .round_surplus_fractions(Some(2)) + .round_values(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) + .transferable_only(true) + .exclusion(stv::ExclusionMethod::ByValue) + .early_bulk_elect(false) + .bulk_exclude(true) + .defer_surpluses(true) + .build().unwrap(); + + GuardedFixed::set_dps(5); + + assert_eq!(stv_opts.describe::(), "--numbers gfixed --decimals 5 --round-surplus-fractions 2 --round-values 2 --round-votes 2 --round-quota 2 --quota droop_exact --quota-criterion geq --quota-mode ers97 --surplus eg --transferable-only --exclusion by_value --no-early-bulk-elect --bulk-exclude --defer-surpluses"); + + utils::read_validate_election::("tests/data/ers97.csv", "tests/data/ers97.blt", stv_opts, None, &["nt", "vre"]); +} + +#[test] +fn ers97_float64() { + let stv_opts = stv::STVOptionsBuilder::default() + .round_surplus_fractions(Some(2)) + .round_values(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) + .transferable_only(true) + .exclusion(stv::ExclusionMethod::ByValue) + .early_bulk_elect(false) + .bulk_exclude(true) + .defer_surpluses(true) + .build().unwrap(); + + assert_eq!(stv_opts.describe::(), "--numbers float64 --round-surplus-fractions 2 --round-values 2 --round-votes 2 --round-quota 2 --quota droop_exact --quota-criterion geq --quota-mode ers97 --surplus eg --transferable-only --exclusion by_value --no-early-bulk-elect --bulk-exclude --defer-surpluses"); + + utils::read_validate_election::("tests/data/ers97.csv", "tests/data/ers97.blt", stv_opts, Some(2), &["nt", "vre"]); +} + #[test] fn ers97_rational_csv() { let cmd = Command::cargo_bin("opentally").expect("Cargo Error") @@ -98,5 +175,7 @@ fn ers76_rational() { .defer_surpluses(true) .build().unwrap(); + assert_eq!(stv_opts.describe::(), "--round-surplus-fractions 2 --round-values 2 --round-votes 2 --round-quota 0 --quota droop_exact --quota-criterion geq --quota-mode ers76 --surplus eg --transferable-only --exclusion by_value --no-early-bulk-elect --bulk-exclude --defer-surpluses"); + utils::read_validate_election::("tests/data/ers76.csv", "tests/data/ers76.blt", stv_opts, None, &["nt", "vre"]); } diff --git a/tests/tests_impl/meek.rs b/tests/tests_impl/meek.rs index 11df147..5705b6e 100644 --- a/tests/tests_impl/meek.rs +++ b/tests/tests_impl/meek.rs @@ -26,7 +26,7 @@ use opentally::stv; #[test] fn meek87_ers97old_float64() { let stv_opts = stv::STVOptionsBuilder::default() - .meek_surplus_tolerance("0.001%".to_string()) + //.meek_surplus_tolerance("0.001%".to_string()) .quota(stv::QuotaType::DroopExact) .quota_criterion(stv::QuotaCriterion::GreaterOrEqual) .quota_mode(stv::QuotaMode::DynamicByTotal) @@ -34,6 +34,8 @@ fn meek87_ers97old_float64() { .immediate_elect(false) .build().unwrap(); + assert_eq!(stv_opts.describe::(), "--numbers float64 --quota droop_exact --quota-criterion geq --quota-mode dynamic_by_total --surplus meek --no-immediate-elect"); + utils::read_validate_election::("tests/data/ers97old_meek.csv", "tests/data/ers97old.blt", stv_opts, Some(2), &["exhausted", "quota"]); } @@ -54,6 +56,8 @@ fn meek06_ers97old_fixed12() { Fixed::set_dps(12); + assert_eq!(stv_opts.describe::(), "--numbers fixed --decimals 12 --round-surplus-fractions 9 --round-values 9 --round-votes 9 --round-quota 9 --meek-surplus-tolerance 0.0001 --quota-criterion geq --quota-mode dynamic_by_total --surplus meek --defer-surpluses"); + // Read BLT let election: Election = blt::parse_path("tests/data/ers97old.blt").expect("Syntax Error"); @@ -114,6 +118,8 @@ fn meeknz_ers97old_fixed12() { Fixed::set_dps(12); + assert_eq!(stv_opts.describe::(), "--numbers fixed --decimals 12 --round-surplus-fractions 9 --round-values 9 --round-votes 9 --round-quota 9 --meek-surplus-tolerance 0.0001 --quota-criterion geq --quota-mode dynamic_by_total --surplus meek --meek-nz-exclusion --defer-surpluses"); + // Read BLT let election: Election = blt::parse_path("tests/data/ers97old.blt").expect("Syntax Error"); diff --git a/tests/tests_impl/minneapolis.rs b/tests/tests_impl/minneapolis.rs index e5dcd40..0708bb2 100644 --- a/tests/tests_impl/minneapolis.rs +++ b/tests/tests_impl/minneapolis.rs @@ -26,12 +26,14 @@ fn minneapolis_boe09_rational() { .round_surplus_fractions(Some(4)) .round_quota(Some(0)) .quota_criterion(stv::QuotaCriterion::GreaterOrEqual) - .early_bulk_elect(true) + //.early_bulk_elect(true) .bulk_exclude(true) .defer_surpluses(true) .immediate_elect(false) .build().unwrap(); + assert_eq!(stv_opts.describe::(), "--round-surplus-fractions 4 --round-quota 0 --quota-criterion geq --bulk-exclude --defer-surpluses --no-immediate-elect"); + utils::read_validate_election::("tests/data/Mppls-2009-BOE-full.csv", "tests/data/Mppls-2009-BOE-full.blt", stv_opts, Some(4), &[]); } @@ -41,11 +43,13 @@ fn minneapolis_pal13_rational() { .round_surplus_fractions(Some(4)) .round_quota(Some(0)) .quota_criterion(stv::QuotaCriterion::GreaterOrEqual) - .early_bulk_elect(true) + //.early_bulk_elect(true) .bulk_exclude(true) .defer_surpluses(true) .immediate_elect(false) .build().unwrap(); + assert_eq!(stv_opts.describe::(), "--round-surplus-fractions 4 --round-quota 0 --quota-criterion geq --bulk-exclude --defer-surpluses --no-immediate-elect"); + utils::read_validate_election::("tests/data/2013-Park-At-Large-CVR.csv", "tests/data/2013-Park-At-Large-CVR.blt", stv_opts, Some(4), &[]); } diff --git a/tests/tests_impl/prsa.rs b/tests/tests_impl/prsa.rs index 72a2e2e..08b4d60 100644 --- a/tests/tests_impl/prsa.rs +++ b/tests/tests_impl/prsa.rs @@ -35,5 +35,7 @@ fn prsa1_rational() { .early_bulk_elect(false) .build().unwrap(); + assert_eq!(stv_opts.describe::(), "--round-surplus-fractions 3 --round-values 3 --round-votes 3 --round-quota 3 --quota-criterion geq --surplus eg --surplus-order by_order --transferable-only --exclusion parcels_by_order --no-early-bulk-elect"); + utils::read_validate_election::("tests/data/prsa1.csv", "tests/data/prsa1.blt", stv_opts, None, &["exhausted", "lbf"]); } diff --git a/tests/tests_impl/scotland.rs b/tests/tests_impl/scotland.rs index e7eec19..099e382 100644 --- a/tests/tests_impl/scotland.rs +++ b/tests/tests_impl/scotland.rs @@ -33,13 +33,15 @@ fn scotland_linn07_fixed5() { //.round_votes(Some(5)) .round_quota(Some(0)) .sum_surplus_transfers(stv::SumSurplusTransfersMode::PerBallot) - //.normalise_ballots(true) .quota_criterion(stv::QuotaCriterion::GreaterOrEqual) .early_bulk_elect(false) .pp_decimals(5) .build().unwrap(); Fixed::set_dps(5); + + assert_eq!(stv_opts.describe::(), "--numbers fixed --decimals 5 --round-surplus-fractions 5 --round-quota 0 --sum-surplus-transfers per_ballot --quota-criterion geq --no-early-bulk-elect --pp-decimals 5"); + scotland_linn07::(stv_opts); } @@ -51,13 +53,15 @@ fn scotland_linn07_gfixed5() { .round_votes(Some(5)) .round_quota(Some(0)) .sum_surplus_transfers(stv::SumSurplusTransfersMode::PerBallot) - //.normalise_ballots(true) .quota_criterion(stv::QuotaCriterion::GreaterOrEqual) .early_bulk_elect(false) .pp_decimals(5) .build().unwrap(); GuardedFixed::set_dps(5); + + assert_eq!(stv_opts.describe::(), "--numbers gfixed --decimals 5 --round-surplus-fractions 5 --round-values 5 --round-votes 5 --round-quota 0 --sum-surplus-transfers per_ballot --quota-criterion geq --no-early-bulk-elect --pp-decimals 5"); + scotland_linn07::(stv_opts); }