Allow configuring --round-votes
Fix bug with display of negative Rational's
This commit is contained in:
parent
32234ad13b
commit
77cf60c21f
74
src/main.rs
74
src/main.rs
|
@ -21,9 +21,9 @@ mod numbers;
|
|||
mod stv;
|
||||
|
||||
use crate::election::{Candidate, CandidateState, CountCard, CountState, CountStateOrRef, Election, StageResult};
|
||||
use crate::numbers::{NativeFloat, Number, Rational};
|
||||
use crate::numbers::{NativeFloat64, Number, Rational};
|
||||
|
||||
use clap::Clap;
|
||||
use clap::{AppSettings, Clap};
|
||||
use git_version::git_version;
|
||||
|
||||
use std::fs::File;
|
||||
|
@ -47,12 +47,27 @@ enum Command {
|
|||
|
||||
/// Count a single transferable vote (STV) election
|
||||
#[derive(Clap)]
|
||||
#[clap(setting=AppSettings::DeriveDisplayOrder, setting=AppSettings::UnifiedHelpMessage)]
|
||||
struct STV {
|
||||
// -- File input --
|
||||
|
||||
/// Path to the BLT file to be counted
|
||||
filename: String,
|
||||
|
||||
// -- Numbers settings --
|
||||
|
||||
/// Numbers mode
|
||||
#[clap(short, long, possible_values(&["rational", "native"]), default_value="rational")]
|
||||
#[clap(short, long, possible_values(&["rational", "float64"]), default_value="rational")]
|
||||
numbers: String,
|
||||
|
||||
// -- Rounding settings --
|
||||
|
||||
/// Round votes to specified decimal places
|
||||
#[clap(long)]
|
||||
round_votes: Option<usize>,
|
||||
|
||||
// -- Display settings --
|
||||
|
||||
/// Hide excluded candidates from results report
|
||||
#[clap(long)]
|
||||
hide_excluded: bool,
|
||||
|
@ -77,8 +92,8 @@ fn main() {
|
|||
if cmd_opts.numbers == "rational" {
|
||||
let election: Election<Rational> = Election::from_blt(lines);
|
||||
count_election(election, cmd_opts);
|
||||
} else if cmd_opts.numbers == "native" {
|
||||
let election: Election<NativeFloat> = Election::from_blt(lines);
|
||||
} else if cmd_opts.numbers == "float64" {
|
||||
let election: Election<NativeFloat64> = Election::from_blt(lines);
|
||||
count_election(election, cmd_opts);
|
||||
}
|
||||
}
|
||||
|
@ -88,57 +103,28 @@ where
|
|||
for<'r> &'r N: ops::Sub<&'r N, Output=N>,
|
||||
for<'r> &'r N: ops::Neg<Output=N>
|
||||
{
|
||||
// Copy applicable options
|
||||
let stv_opts = stv::STVOptions {
|
||||
round_votes: cmd_opts.round_votes,
|
||||
};
|
||||
|
||||
// Initialise count state
|
||||
let mut state = CountState::new(&election);
|
||||
|
||||
// Distribute first preferences
|
||||
stv::distribute_first_preferences(&mut state);
|
||||
stv::calculate_quota(&mut state);
|
||||
stv::elect_meeting_quota(&mut state);
|
||||
stv::count_init(&mut state, &stv_opts);
|
||||
|
||||
// Display
|
||||
let mut stage_num = 1;
|
||||
make_and_print_result(stage_num, &state, &cmd_opts);
|
||||
|
||||
loop {
|
||||
state.logger.entries.clear();
|
||||
state.step_all();
|
||||
stage_num += 1;
|
||||
|
||||
// Finish count
|
||||
if stv::finished_before_stage(&state) {
|
||||
let is_done = stv::count_one_stage(&mut state, &stv_opts);
|
||||
if is_done {
|
||||
break;
|
||||
}
|
||||
|
||||
// Continue exclusions
|
||||
if stv::continue_exclusion(&mut state) {
|
||||
stv::elect_meeting_quota(&mut state);
|
||||
make_and_print_result(stage_num, &state, &cmd_opts);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Distribute surpluses
|
||||
if stv::distribute_surpluses(&mut state) {
|
||||
stv::elect_meeting_quota(&mut state);
|
||||
make_and_print_result(stage_num, &state, &cmd_opts);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Attempt bulk election
|
||||
if stv::bulk_elect(&mut state) {
|
||||
stv::elect_meeting_quota(&mut state);
|
||||
make_and_print_result(stage_num, &state, &cmd_opts);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Exclude lowest hopeful
|
||||
if stv::exclude_hopefuls(&mut state) {
|
||||
stv::elect_meeting_quota(&mut state);
|
||||
make_and_print_result(stage_num, &state, &cmd_opts);
|
||||
continue;
|
||||
}
|
||||
|
||||
todo!();
|
||||
stage_num += 1;
|
||||
make_and_print_result(stage_num, &state, &cmd_opts);
|
||||
}
|
||||
|
||||
println!("Count complete. The winning candidates are, in order of election:");
|
||||
|
|
|
@ -30,7 +30,7 @@ pub trait Number: NumRef + NumAssignRef + ops::Neg<Output=Self> + PartialOrd + A
|
|||
fn new() -> Self;
|
||||
fn from(n: usize) -> Self;
|
||||
|
||||
fn floor_mut(&mut self);
|
||||
fn floor_mut(&mut self, dps: usize);
|
||||
|
||||
fn parse(s: &str) -> Self {
|
||||
if let Ok(value) = Self::from_str_radix(s, 10) {
|
||||
|
@ -41,5 +41,5 @@ pub trait Number: NumRef + NumAssignRef + ops::Neg<Output=Self> + PartialOrd + A
|
|||
}
|
||||
}
|
||||
|
||||
pub use self::native::NativeFloat;
|
||||
pub use self::native::NativeFloat64;
|
||||
pub use self::rational::Rational;
|
||||
|
|
|
@ -25,218 +25,223 @@ use std::num::ParseIntError;
|
|||
use std::fmt;
|
||||
use std::ops;
|
||||
|
||||
pub struct NativeFloat(f32);
|
||||
type ImplType = f64;
|
||||
|
||||
impl Number for NativeFloat {
|
||||
pub struct NativeFloat64(ImplType);
|
||||
|
||||
impl Number for NativeFloat64 {
|
||||
fn new() -> Self { Self(0.0) }
|
||||
|
||||
fn from(n: usize) -> Self { Self(n as f32) }
|
||||
fn from(n: usize) -> Self { Self(n as ImplType) }
|
||||
|
||||
fn floor_mut(&mut self) { self.0 = self.0.floor() }
|
||||
fn floor_mut(&mut self, dps: usize) {
|
||||
let factor = 10.0_f64.powi(dps as i32);
|
||||
self.0 = (self.0 * factor).floor() / factor;
|
||||
}
|
||||
}
|
||||
|
||||
impl Num for NativeFloat {
|
||||
impl Num for NativeFloat64 {
|
||||
type FromStrRadixErr = ParseIntError;
|
||||
|
||||
fn from_str_radix(str: &str, radix: u32) -> Result<Self, Self::FromStrRadixErr> {
|
||||
match i32::from_str_radix(str, radix) {
|
||||
Ok(value) => Ok(Self(value as f32)),
|
||||
match i64::from_str_radix(str, radix) {
|
||||
Ok(value) => Ok(Self(value as ImplType)),
|
||||
Err(err) => Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Assign for NativeFloat {
|
||||
impl Assign for NativeFloat64 {
|
||||
fn assign(&mut self, src: Self) { self.0 = src.0 }
|
||||
}
|
||||
|
||||
impl Assign<&NativeFloat> for NativeFloat {
|
||||
fn assign(&mut self, src: &NativeFloat) { self.0 = src.0 }
|
||||
impl Assign<&NativeFloat64> for NativeFloat64 {
|
||||
fn assign(&mut self, src: &NativeFloat64) { self.0 = src.0 }
|
||||
}
|
||||
|
||||
impl Clone for NativeFloat {
|
||||
impl Clone for NativeFloat64 {
|
||||
fn clone(&self) -> Self { Self(self.0) }
|
||||
}
|
||||
|
||||
impl fmt::Display for NativeFloat {
|
||||
impl fmt::Display for NativeFloat64 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) }
|
||||
}
|
||||
|
||||
impl One for NativeFloat {
|
||||
impl One for NativeFloat64 {
|
||||
fn one() -> Self { Self(1.0) }
|
||||
}
|
||||
|
||||
impl Zero for NativeFloat {
|
||||
impl Zero for NativeFloat64 {
|
||||
fn zero() -> Self { Self::new() }
|
||||
fn is_zero(&self) -> bool { self.0.is_zero() }
|
||||
}
|
||||
|
||||
impl PartialEq for NativeFloat {
|
||||
impl PartialEq for NativeFloat64 {
|
||||
fn eq(&self, _other: &Self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for NativeFloat {
|
||||
impl PartialOrd for NativeFloat64 {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { self.0.partial_cmp(&other.0) }
|
||||
}
|
||||
|
||||
impl ops::Neg for NativeFloat {
|
||||
type Output = NativeFloat;
|
||||
impl ops::Neg for NativeFloat64 {
|
||||
type Output = NativeFloat64;
|
||||
fn neg(self) -> Self::Output { Self(-self.0) }
|
||||
}
|
||||
|
||||
impl ops::Add for NativeFloat {
|
||||
type Output = NativeFloat;
|
||||
impl ops::Add for NativeFloat64 {
|
||||
type Output = NativeFloat64;
|
||||
fn add(self, _rhs: Self) -> Self::Output {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Sub for NativeFloat {
|
||||
type Output = NativeFloat;
|
||||
impl ops::Sub for NativeFloat64 {
|
||||
type Output = NativeFloat64;
|
||||
fn sub(self, _rhs: Self) -> Self::Output {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Mul for NativeFloat {
|
||||
type Output = NativeFloat;
|
||||
impl ops::Mul for NativeFloat64 {
|
||||
type Output = NativeFloat64;
|
||||
fn mul(self, _rhs: Self) -> Self::Output {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Div for NativeFloat {
|
||||
type Output = NativeFloat;
|
||||
impl ops::Div for NativeFloat64 {
|
||||
type Output = NativeFloat64;
|
||||
fn div(self, _rhs: Self) -> Self::Output {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Rem for NativeFloat {
|
||||
type Output = NativeFloat;
|
||||
impl ops::Rem for NativeFloat64 {
|
||||
type Output = NativeFloat64;
|
||||
|
||||
fn rem(self, _rhs: Self) -> Self::Output {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Add<&NativeFloat> for NativeFloat {
|
||||
type Output = NativeFloat;
|
||||
fn add(self, rhs: &NativeFloat) -> Self::Output { Self(self.0 + &rhs.0) }
|
||||
impl ops::Add<&NativeFloat64> for NativeFloat64 {
|
||||
type Output = NativeFloat64;
|
||||
fn add(self, rhs: &NativeFloat64) -> Self::Output { Self(self.0 + &rhs.0) }
|
||||
}
|
||||
|
||||
impl ops::Sub<&NativeFloat> for NativeFloat {
|
||||
type Output = NativeFloat;
|
||||
fn sub(self, _rhs: &NativeFloat) -> Self::Output {
|
||||
impl ops::Sub<&NativeFloat64> for NativeFloat64 {
|
||||
type Output = NativeFloat64;
|
||||
fn sub(self, _rhs: &NativeFloat64) -> Self::Output {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Mul<&NativeFloat> for NativeFloat {
|
||||
type Output = NativeFloat;
|
||||
fn mul(self, rhs: &NativeFloat) -> Self::Output { NativeFloat(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) }
|
||||
}
|
||||
|
||||
impl ops::Div<&NativeFloat> for NativeFloat {
|
||||
type Output = NativeFloat;
|
||||
fn div(self, rhs: &NativeFloat) -> Self::Output { NativeFloat(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) }
|
||||
}
|
||||
|
||||
impl ops::Rem<&NativeFloat> for NativeFloat {
|
||||
type Output = NativeFloat;
|
||||
fn rem(self, _rhs: &NativeFloat) -> Self::Output {
|
||||
impl ops::Rem<&NativeFloat64> for NativeFloat64 {
|
||||
type Output = NativeFloat64;
|
||||
fn rem(self, _rhs: &NativeFloat64) -> Self::Output {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::AddAssign for NativeFloat {
|
||||
impl ops::AddAssign for NativeFloat64 {
|
||||
fn add_assign(&mut self, rhs: Self) { self.0 += rhs.0 }
|
||||
}
|
||||
|
||||
impl ops::SubAssign for NativeFloat {
|
||||
impl ops::SubAssign for NativeFloat64 {
|
||||
fn sub_assign(&mut self, rhs: Self) { self.0 -= rhs.0 }
|
||||
}
|
||||
|
||||
impl ops::MulAssign for NativeFloat {
|
||||
impl ops::MulAssign for NativeFloat64 {
|
||||
fn mul_assign(&mut self, _rhs: Self) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::DivAssign for NativeFloat {
|
||||
impl ops::DivAssign for NativeFloat64 {
|
||||
fn div_assign(&mut self, rhs: Self) {
|
||||
self.0 /= &rhs.0;
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::RemAssign for NativeFloat {
|
||||
impl ops::RemAssign for NativeFloat64 {
|
||||
fn rem_assign(&mut self, _rhs: Self) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::AddAssign<&NativeFloat> for NativeFloat {
|
||||
fn add_assign(&mut self, rhs: &NativeFloat) { self.0 += &rhs.0 }
|
||||
impl ops::AddAssign<&NativeFloat64> for NativeFloat64 {
|
||||
fn add_assign(&mut self, rhs: &NativeFloat64) { self.0 += &rhs.0 }
|
||||
}
|
||||
|
||||
impl ops::SubAssign<&NativeFloat> for NativeFloat {
|
||||
fn sub_assign(&mut self, rhs: &NativeFloat) { self.0 -= &rhs.0 }
|
||||
impl ops::SubAssign<&NativeFloat64> for NativeFloat64 {
|
||||
fn sub_assign(&mut self, rhs: &NativeFloat64) { self.0 -= &rhs.0 }
|
||||
}
|
||||
|
||||
impl ops::MulAssign<&NativeFloat> for NativeFloat {
|
||||
fn mul_assign(&mut self, _rhs: &NativeFloat) {
|
||||
impl ops::MulAssign<&NativeFloat64> for NativeFloat64 {
|
||||
fn mul_assign(&mut self, _rhs: &NativeFloat64) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::DivAssign<&NativeFloat> for NativeFloat {
|
||||
fn div_assign(&mut self, _rhs: &NativeFloat) {
|
||||
impl ops::DivAssign<&NativeFloat64> for NativeFloat64 {
|
||||
fn div_assign(&mut self, _rhs: &NativeFloat64) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::RemAssign<&NativeFloat> for NativeFloat {
|
||||
fn rem_assign(&mut self, _rhs: &NativeFloat) {
|
||||
impl ops::RemAssign<&NativeFloat64> for NativeFloat64 {
|
||||
fn rem_assign(&mut self, _rhs: &NativeFloat64) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Neg for &NativeFloat {
|
||||
type Output = NativeFloat;
|
||||
fn neg(self) -> Self::Output { NativeFloat(-&self.0) }
|
||||
impl ops::Neg for &NativeFloat64 {
|
||||
type Output = NativeFloat64;
|
||||
fn neg(self) -> Self::Output { NativeFloat64(-&self.0) }
|
||||
}
|
||||
|
||||
impl ops::Add<&NativeFloat> for &NativeFloat {
|
||||
type Output = NativeFloat;
|
||||
fn add(self, _rhs: &NativeFloat) -> Self::Output {
|
||||
impl ops::Add<&NativeFloat64> for &NativeFloat64 {
|
||||
type Output = NativeFloat64;
|
||||
fn add(self, _rhs: &NativeFloat64) -> Self::Output {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Sub<&NativeFloat> for &NativeFloat {
|
||||
type Output = NativeFloat;
|
||||
fn sub(self, rhs: &NativeFloat) -> Self::Output { NativeFloat(&self.0 - &rhs.0) }
|
||||
impl ops::Sub<&NativeFloat64> for &NativeFloat64 {
|
||||
type Output = NativeFloat64;
|
||||
fn sub(self, rhs: &NativeFloat64) -> Self::Output { NativeFloat64(&self.0 - &rhs.0) }
|
||||
}
|
||||
|
||||
impl ops::Mul<&NativeFloat> for &NativeFloat {
|
||||
type Output = NativeFloat;
|
||||
fn mul(self, _rhs: &NativeFloat) -> Self::Output {
|
||||
impl ops::Mul<&NativeFloat64> for &NativeFloat64 {
|
||||
type Output = NativeFloat64;
|
||||
fn mul(self, _rhs: &NativeFloat64) -> Self::Output {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Div<&NativeFloat> for &NativeFloat {
|
||||
type Output = NativeFloat;
|
||||
fn div(self, _rhs: &NativeFloat) -> Self::Output {
|
||||
impl ops::Div<&NativeFloat64> for &NativeFloat64 {
|
||||
type Output = NativeFloat64;
|
||||
fn div(self, _rhs: &NativeFloat64) -> Self::Output {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Rem<&NativeFloat> for &NativeFloat {
|
||||
type Output = NativeFloat;
|
||||
fn rem(self, _rhs: &NativeFloat) -> Self::Output {
|
||||
impl ops::Rem<&NativeFloat64> for &NativeFloat64 {
|
||||
type Output = NativeFloat64;
|
||||
fn rem(self, _rhs: &NativeFloat64) -> Self::Output {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,16 @@ impl Number for Rational {
|
|||
|
||||
fn from(n: usize) -> Self { Self(rug::Rational::from(n)) }
|
||||
|
||||
fn floor_mut(&mut self) { self.0.floor_mut() }
|
||||
fn floor_mut(&mut self, dps: usize) {
|
||||
if dps == 0 {
|
||||
self.0.floor_mut();
|
||||
} else {
|
||||
let factor = rug::Rational::from(10).pow(dps as u32);
|
||||
self.0 *= &factor;
|
||||
self.0.floor_mut();
|
||||
self.0 /= factor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Num for Rational {
|
||||
|
@ -65,7 +74,9 @@ impl fmt::Display for Rational {
|
|||
return f.write_str(&result);
|
||||
} else {
|
||||
let base = rug::Rational::from(10).pow(precision as u32);
|
||||
let mut result = rug::Integer::from((&self.0 * base).round_ref()).to_string();
|
||||
let mut result = rug::Integer::from((&self.0 * base).abs().round_ref()).to_string();
|
||||
|
||||
let should_add_minus = (self.0 < 0) && result != "0";
|
||||
|
||||
// Add leading 0s
|
||||
result = format!("{0:0>1$}", result, precision + 1);
|
||||
|
@ -73,6 +84,11 @@ impl fmt::Display for Rational {
|
|||
// Add the decimal point
|
||||
result.insert(result.len() - precision, '.');
|
||||
|
||||
// Add the sign
|
||||
if should_add_minus {
|
||||
result.insert(0, '-');
|
||||
}
|
||||
|
||||
return f.write_str(&result);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -23,6 +23,56 @@ use crate::election::{Candidate, CandidateState, CountCard, CountState, Parcel,
|
|||
use std::collections::HashMap;
|
||||
use std::ops::{Neg, Sub};
|
||||
|
||||
pub struct STVOptions {
|
||||
pub round_votes: Option<usize>,
|
||||
}
|
||||
|
||||
pub fn count_init<N: Number>(mut state: &mut CountState<'_, N>, _opts: &STVOptions) {
|
||||
distribute_first_preferences(&mut state);
|
||||
calculate_quota(&mut state);
|
||||
elect_meeting_quota(&mut state);
|
||||
}
|
||||
|
||||
pub fn count_one_stage<N: Number>(mut state: &mut CountState<'_, N>, opts: &STVOptions) -> bool
|
||||
where
|
||||
for<'r> &'r N: Sub<&'r N, Output=N>,
|
||||
for<'r> &'r N: Neg<Output=N>
|
||||
{
|
||||
state.logger.entries.clear();
|
||||
state.step_all();
|
||||
|
||||
// Finish count
|
||||
if finished_before_stage(&state) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Continue exclusions
|
||||
if continue_exclusion(&mut state, &opts) {
|
||||
elect_meeting_quota(&mut state);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Distribute surpluses
|
||||
if distribute_surpluses(&mut state, &opts) {
|
||||
elect_meeting_quota(&mut state);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attempt bulk election
|
||||
if bulk_elect(&mut state) {
|
||||
elect_meeting_quota(&mut state);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Exclude lowest hopeful
|
||||
if exclude_hopefuls(&mut state, &opts) {
|
||||
elect_meeting_quota(&mut state);
|
||||
return false;
|
||||
}
|
||||
|
||||
todo!();
|
||||
}
|
||||
|
||||
struct NextPreferencesResult<'a, N> {
|
||||
candidates: HashMap<&'a Candidate, NextPreferencesEntry<'a, N>>,
|
||||
exhausted: NextPreferencesEntry<'a, N>,
|
||||
|
@ -88,7 +138,7 @@ fn next_preferences<'a, N: Number>(state: &CountState<'a, N>, votes: Vec<Vote<'a
|
|||
return result;
|
||||
}
|
||||
|
||||
pub fn distribute_first_preferences<N: Number>(state: &mut CountState<N>) {
|
||||
fn distribute_first_preferences<N: Number>(state: &mut CountState<N>) {
|
||||
let votes = state.election.ballots.iter().map(|b| Vote {
|
||||
ballot: b,
|
||||
value: b.orig_value.clone(),
|
||||
|
@ -115,7 +165,7 @@ pub fn distribute_first_preferences<N: Number>(state: &mut CountState<N>) {
|
|||
state.logger.log_literal("First preferences distributed.".to_string());
|
||||
}
|
||||
|
||||
pub fn calculate_quota<N: Number>(state: &mut CountState<N>) {
|
||||
fn calculate_quota<N: Number>(state: &mut CountState<N>) {
|
||||
let mut log = String::new();
|
||||
|
||||
// Calculate the total vote
|
||||
|
@ -127,7 +177,7 @@ pub fn calculate_quota<N: Number>(state: &mut CountState<N>) {
|
|||
|
||||
// TODO: Different rounding rules
|
||||
state.quota += N::one();
|
||||
state.quota.floor_mut();
|
||||
state.quota.floor_mut(0);
|
||||
log.push_str(format!("{:.2}.", state.quota).as_str());
|
||||
|
||||
state.logger.log_literal(log);
|
||||
|
@ -138,7 +188,7 @@ fn meets_quota<N: Number>(quota: &N, count_card: &CountCard<N>) -> bool {
|
|||
return count_card.votes >= *quota;
|
||||
}
|
||||
|
||||
pub fn elect_meeting_quota<N: Number>(state: &mut CountState<N>) {
|
||||
fn elect_meeting_quota<N: Number>(state: &mut CountState<N>) {
|
||||
let quota = &state.quota; // Have to do this or else the borrow checker gets confused
|
||||
let mut cands_meeting_quota: Vec<(&&Candidate, &mut CountCard<N>)> = state.candidates.iter_mut()
|
||||
.filter(|(_, cc)| cc.state == CandidateState::HOPEFUL && meets_quota(quota, cc))
|
||||
|
@ -162,7 +212,7 @@ pub fn elect_meeting_quota<N: Number>(state: &mut CountState<N>) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn distribute_surpluses<N: Number>(state: &mut CountState<N>) -> bool
|
||||
fn distribute_surpluses<N: Number>(state: &mut CountState<N>, opts: &STVOptions) -> bool
|
||||
where
|
||||
for<'r> &'r N: Sub<&'r N, Output=N>,
|
||||
for<'r> &'r N: Neg<Output=N>
|
||||
|
@ -178,14 +228,14 @@ where
|
|||
// Distribute top candidate's surplus
|
||||
// TODO: Handle ties
|
||||
let elected_candidate = has_surplus.first_mut().unwrap().0;
|
||||
distribute_surplus(state, elected_candidate);
|
||||
distribute_surplus(state, &opts, elected_candidate);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn distribute_surplus<N: Number>(state: &mut CountState<N>, elected_candidate: &Candidate)
|
||||
fn distribute_surplus<N: Number>(state: &mut CountState<N>, opts: &STVOptions, elected_candidate: &Candidate)
|
||||
where
|
||||
for<'r> &'r N: Sub<&'r N, Output=N>,
|
||||
for<'r> &'r N: Neg<Output=N>
|
||||
|
@ -225,8 +275,9 @@ where
|
|||
|
||||
let mut candidate_transfers = entry.num_ballots * &surplus / &result.total_ballots;
|
||||
// Round transfers
|
||||
// TODO: Make configurable
|
||||
candidate_transfers.floor_mut();
|
||||
if let Some(dps) = opts.round_votes {
|
||||
candidate_transfers.floor_mut(dps);
|
||||
}
|
||||
count_card.transfer(&candidate_transfers);
|
||||
checksum += candidate_transfers;
|
||||
}
|
||||
|
@ -236,8 +287,9 @@ where
|
|||
state.exhausted.parcels.push(parcel);
|
||||
|
||||
let mut exhausted_transfers = result.exhausted.num_ballots * &surplus / &result.total_ballots;
|
||||
// TODO: Make configurable
|
||||
exhausted_transfers.floor_mut();
|
||||
if let Some(dps) = opts.round_votes {
|
||||
exhausted_transfers.floor_mut(dps);
|
||||
}
|
||||
state.exhausted.transfer(&exhausted_transfers);
|
||||
checksum += exhausted_transfers;
|
||||
|
||||
|
@ -251,7 +303,7 @@ where
|
|||
state.loss_fraction.transfer(&-checksum);
|
||||
}
|
||||
|
||||
pub fn bulk_elect<N: Number>(state: &mut CountState<N>) -> bool {
|
||||
fn bulk_elect<N: Number>(state: &mut CountState<N>) -> bool {
|
||||
if state.election.candidates.len() - state.num_excluded <= state.election.seats {
|
||||
state.kind = None;
|
||||
state.title = "Bulk election".to_string();
|
||||
|
@ -281,7 +333,7 @@ pub fn bulk_elect<N: Number>(state: &mut CountState<N>) -> bool {
|
|||
return false;
|
||||
}
|
||||
|
||||
pub fn exclude_hopefuls<N: Number>(state: &mut CountState<N>) -> bool {
|
||||
fn exclude_hopefuls<N: Number>(state: &mut CountState<N>, opts: &STVOptions) -> bool {
|
||||
let mut hopefuls: Vec<(&&Candidate, &CountCard<N>)> = state.candidates.iter()
|
||||
.filter(|(_, cc)| cc.state == CandidateState::HOPEFUL)
|
||||
.collect();
|
||||
|
@ -301,12 +353,12 @@ pub fn exclude_hopefuls<N: Number>(state: &mut CountState<N>) -> bool {
|
|||
vec![&excluded_candidate.name]
|
||||
);
|
||||
|
||||
exclude_candidate(state, excluded_candidate);
|
||||
exclude_candidate(state, opts, excluded_candidate);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn continue_exclusion<N: Number>(state: &mut CountState<N>) -> bool {
|
||||
fn continue_exclusion<N: Number>(state: &mut CountState<N>, opts: &STVOptions) -> bool {
|
||||
let mut excluded_with_votes: Vec<(&&Candidate, &CountCard<N>)> = state.candidates.iter()
|
||||
.filter(|(_, cc)| cc.state == CandidateState::EXCLUDED && !cc.votes.is_zero())
|
||||
.collect();
|
||||
|
@ -323,14 +375,14 @@ pub fn continue_exclusion<N: Number>(state: &mut CountState<N>) -> bool {
|
|||
vec![&excluded_candidate.name]
|
||||
);
|
||||
|
||||
exclude_candidate(state, excluded_candidate);
|
||||
exclude_candidate(state, opts, excluded_candidate);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
fn exclude_candidate<N: Number>(state: &mut CountState<N>, excluded_candidate: &Candidate) {
|
||||
fn exclude_candidate<N: Number>(state: &mut CountState<N>, opts: &STVOptions, excluded_candidate: &Candidate) {
|
||||
let count_card = state.candidates.get_mut(excluded_candidate).unwrap();
|
||||
count_card.state = CandidateState::EXCLUDED;
|
||||
state.num_excluded += 1;
|
||||
|
@ -352,9 +404,10 @@ fn exclude_candidate<N: Number>(state: &mut CountState<N>, excluded_candidate: &
|
|||
count_card.parcels.push(parcel);
|
||||
|
||||
// Round transfers
|
||||
// TODO: Make configurable
|
||||
let mut candidate_transfers = entry.num_votes;
|
||||
candidate_transfers.floor_mut();
|
||||
if let Some(dps) = opts.round_votes {
|
||||
candidate_transfers.floor_mut(dps);
|
||||
}
|
||||
count_card.transfer(&candidate_transfers);
|
||||
checksum += candidate_transfers;
|
||||
}
|
||||
|
@ -364,8 +417,9 @@ fn exclude_candidate<N: Number>(state: &mut CountState<N>, excluded_candidate: &
|
|||
state.exhausted.parcels.push(parcel);
|
||||
|
||||
let mut exhausted_transfers = result.exhausted.num_votes;
|
||||
// TODO: Make configurable
|
||||
exhausted_transfers.floor_mut();
|
||||
if let Some(dps) = opts.round_votes {
|
||||
exhausted_transfers.floor_mut(dps);
|
||||
}
|
||||
state.exhausted.transfer(&exhausted_transfers);
|
||||
checksum += exhausted_transfers;
|
||||
|
||||
|
@ -379,7 +433,7 @@ fn exclude_candidate<N: Number>(state: &mut CountState<N>, excluded_candidate: &
|
|||
state.loss_fraction.transfer(&-checksum);
|
||||
}
|
||||
|
||||
pub fn finished_before_stage<N: Number>(state: &CountState<N>) -> bool {
|
||||
fn finished_before_stage<N: Number>(state: &CountState<N>) -> bool {
|
||||
if state.num_elected >= state.election.seats {
|
||||
return true;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue