More work on unit/integration tests

This commit is contained in:
RunasSudo 2021-09-14 02:23:51 +10:00
parent f12db205b9
commit a641b97d1f
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
22 changed files with 275 additions and 100 deletions

View File

@ -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 \

View File

@ -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<N> {
/// Name of the election
@ -93,7 +92,7 @@ impl<N: Number> Election<N> {
}
/// 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<N> {
/// Original value/weight of the ballot

View File

@ -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<LogEntry<'a>>,
@ -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,

View File

@ -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<usize> 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<Self> for &Fixed {
impl ops::Rem<Self> 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"));
}
/*

View File

@ -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<usize> 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<Self> for &GuardedFixed {
impl ops::Rem<Self> 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"));
}
/*

View File

@ -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<usize> 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<Self> 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<Self> 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<Self> 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<Self> 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<Self> 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));
}
/*

View File

@ -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<usize> 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<Self> for &Rational {
impl ops::Rem<Self> 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"));
}
/*

View File

@ -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,

View File

@ -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]

View File

@ -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<TieStrategy>,
/// 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()); }

View File

@ -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<String> {
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,
};
}

View File

@ -33,5 +33,7 @@ fn act_kurrajong20_rational() {
.early_bulk_elect(false)
.build().unwrap();
assert_eq!(stv_opts.describe::<Rational>(), "--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::<Rational>("tests/data/ACT2020Kurrajong.csv", "tests/data/ACT2020Kurrajong.blt", stv_opts, Some(6), &["exhausted", "lbf"]);
}

View File

@ -70,5 +70,7 @@ fn aec_tas19_rational() {
.bulk_exclude(true)
.build().unwrap();
assert_eq!(stv_opts.describe::<Rational>(), "--round-votes 0 --round-quota 0 --quota-criterion geq --surplus uig --surplus-order by_order --exclusion by_value --bulk-exclude");
utils::validate_election::<Rational>(stages, records, election, stv_opts, None, &["exhausted", "lbf"]);
}

View File

@ -34,5 +34,7 @@ fn cambridge_cc03_rational() {
.min_threshold("49".to_string())
.build().unwrap();
assert_eq!(stv_opts.describe::<Rational>(), "--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::<Rational>("tests/data/CambCC2003.csv", "tests/data/CambCC2003.blt", stv_opts, None, &["exhausted"]);
}

View File

@ -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();

View File

@ -35,5 +35,7 @@ fn ers97_coe_rational() {
.defer_surpluses(true)
.build().unwrap();
assert_eq!(stv_opts.describe::<Rational>(), "--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::<Rational>("tests/data/ers97_coe.csv", "tests/data/ers97.blt", stv_opts, None, &["nt"]);
}

View File

@ -30,5 +30,7 @@ fn csm15_float64() {
.bulk_exclude(true)
.build().unwrap();
assert_eq!(stv_opts.describe::<NativeFloat64>(), "--numbers float64 --round-quota 0 --quota-criterion geq --exclusion wright --no-early-bulk-elect --bulk-exclude");
utils::read_validate_election::<NativeFloat64>("tests/data/CSM15.csv", "tests/data/CSM15.blt", stv_opts, Some(6), &["quota"]);
}

View File

@ -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::<Rational>(), "--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::<Rational>("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::<Rational>(), "--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::<Rational>("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::<Fixed>(), "--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::<Fixed>("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::<GuardedFixed>(), "--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::<GuardedFixed>("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::<NativeFloat64>(), "--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::<NativeFloat64>("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::<Rational>(), "--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::<Rational>("tests/data/ers76.csv", "tests/data/ers76.blt", stv_opts, None, &["nt", "vre"]);
}

View File

@ -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::<NativeFloat64>(), "--numbers float64 --quota droop_exact --quota-criterion geq --quota-mode dynamic_by_total --surplus meek --no-immediate-elect");
utils::read_validate_election::<NativeFloat64>("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::<Fixed>(), "--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<Fixed> = 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::<Fixed>(), "--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<Fixed> = blt::parse_path("tests/data/ers97old.blt").expect("Syntax Error");

View File

@ -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::<Rational>(), "--round-surplus-fractions 4 --round-quota 0 --quota-criterion geq --bulk-exclude --defer-surpluses --no-immediate-elect");
utils::read_validate_election::<Rational>("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::<Rational>(), "--round-surplus-fractions 4 --round-quota 0 --quota-criterion geq --bulk-exclude --defer-surpluses --no-immediate-elect");
utils::read_validate_election::<Rational>("tests/data/2013-Park-At-Large-CVR.csv", "tests/data/2013-Park-At-Large-CVR.blt", stv_opts, Some(4), &[]);
}

View File

@ -35,5 +35,7 @@ fn prsa1_rational() {
.early_bulk_elect(false)
.build().unwrap();
assert_eq!(stv_opts.describe::<Rational>(), "--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::<Rational>("tests/data/prsa1.csv", "tests/data/prsa1.blt", stv_opts, None, &["exhausted", "lbf"]);
}

View File

@ -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::<Fixed>(), "--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::<Fixed>(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::<GuardedFixed>(), "--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::<GuardedFixed>(stv_opts);
}