From 266d8e2495ed15ef5cf82d938888781f5e930004 Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Sun, 13 Jun 2021 00:56:18 +1000 Subject: [PATCH] Report when and how ties are broken --- src/logger.rs | 7 +++++-- src/stv/mod.rs | 4 ++-- src/ties.rs | 17 ++++++++++++++--- 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/logger.rs b/src/logger.rs index b91d368..b4dd8ed 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -81,8 +81,11 @@ impl<'a> SmartLogEntry<'a> { } else if self.data.len() == 1 { return String::from(self.template1).replace("{}", self.data.first().unwrap()); } else { - let rendered_list = format!("{} and {}", self.data[0..self.data.len()-1].join(", "), self.data.last().unwrap()); - return String::from(self.template2).replace("{}", &rendered_list); + return String::from(self.template2).replace("{}", &smart_join(&self.data)); } } } + +pub fn smart_join(data: &Vec<&str>) -> String { + return format!("{} and {}", data[0..data.len()-1].join(", "), data.last().unwrap()); +} diff --git a/src/stv/mod.rs b/src/stv/mod.rs index 3490488..d76652b 100644 --- a/src/stv/mod.rs +++ b/src/stv/mod.rs @@ -1243,7 +1243,7 @@ fn finished_before_stage(state: &CountState) -> bool { return false; } -fn choose_highest<'c, N: Number>(state: &CountState, opts: &STVOptions, candidates: Vec<&'c Candidate>) -> Result<&'c Candidate, STVError> { +fn choose_highest<'c, N: Number>(state: &mut CountState, opts: &STVOptions, candidates: Vec<&'c Candidate>) -> Result<&'c Candidate, STVError> { for strategy in opts.ties.iter() { match strategy.choose_highest(state, &candidates) { Ok(c) => { @@ -1261,7 +1261,7 @@ fn choose_highest<'c, N: Number>(state: &CountState, opts: &STVOptions, candi panic!("Unable to resolve tie"); } -fn choose_lowest<'c, N: Number>(state: &CountState, opts: &STVOptions, candidates: Vec<&'c Candidate>) -> Result<&'c Candidate, STVError> { +fn choose_lowest<'c, N: Number>(state: &mut CountState, opts: &STVOptions, candidates: Vec<&'c Candidate>) -> Result<&'c Candidate, STVError> { for strategy in opts.ties.iter() { match strategy.choose_lowest(state, &candidates) { Ok(c) => { diff --git a/src/ties.rs b/src/ties.rs index cbfe639..74ecca3 100644 --- a/src/ties.rs +++ b/src/ties.rs @@ -16,6 +16,7 @@ */ use crate::election::{Candidate, CountState}; +use crate::logger::smart_join; use crate::numbers::Number; use crate::stv::STVError; @@ -43,7 +44,7 @@ impl<'s> TieStrategy<'s> { }.to_string() } - pub fn choose_highest<'c, N: Number>(&self, state: &CountState, candidates: &Vec<&'c Candidate>) -> Result<&'c Candidate, STVError> { + pub fn choose_highest<'c, N: Number>(&self, state: &mut CountState, candidates: &Vec<&'c Candidate>) -> Result<&'c Candidate, STVError> { match self { Self::Forwards => { let mut candidates = candidates.clone(); @@ -55,6 +56,7 @@ impl<'s> TieStrategy<'s> { if state.forwards_tiebreak.as_ref().unwrap().get(candidates[0]).unwrap() == state.forwards_tiebreak.as_ref().unwrap().get(candidates[1]).unwrap() { return Err(STVError::UnresolvedTie); } else { + state.logger.log_literal(format!("Tie between {} broken forwards.", smart_join(&candidates.iter().map(|c| c.name.as_str()).collect()))); return Ok(candidates[0]); } } @@ -67,17 +69,24 @@ impl<'s> TieStrategy<'s> { if state.backwards_tiebreak.as_ref().unwrap().get(candidates[0]).unwrap() == state.backwards_tiebreak.as_ref().unwrap().get(candidates[1]).unwrap() { return Err(STVError::UnresolvedTie); } else { + state.logger.log_literal(format!("Tie between {} broken backwards.", smart_join(&candidates.iter().map(|c| c.name.as_str()).collect()))); return Ok(candidates[0]); } } Self::Random(_seed) => { todo!() } Self::Prompt => { - return prompt(candidates); + match prompt(candidates) { + Ok(c) => { + state.logger.log_literal(format!("Tie between {} broken by manual intervention.", smart_join(&candidates.iter().map(|c| c.name.as_str()).collect()))); + return Ok(c); + } + Err(e) => { return Err(e); } + } } } } - pub fn choose_lowest<'c, N: Number>(&self, state: &CountState, candidates: &Vec<&'c Candidate>) -> Result<&'c Candidate, STVError> { + pub fn choose_lowest<'c, N: Number>(&self, state: &mut CountState, candidates: &Vec<&'c Candidate>) -> Result<&'c Candidate, STVError> { match self { Self::Forwards => { let mut candidates = candidates.clone(); @@ -88,6 +97,7 @@ impl<'s> TieStrategy<'s> { if state.forwards_tiebreak.as_ref().unwrap().get(candidates[0]).unwrap() == state.forwards_tiebreak.as_ref().unwrap().get(candidates[1]).unwrap() { return Err(STVError::UnresolvedTie); } else { + state.logger.log_literal(format!("Tie between {} broken forwards.", smart_join(&candidates.iter().map(|c| c.name.as_str()).collect()))); return Ok(candidates[0]); } } @@ -100,6 +110,7 @@ impl<'s> TieStrategy<'s> { if state.backwards_tiebreak.as_ref().unwrap().get(candidates[0]).unwrap() == state.backwards_tiebreak.as_ref().unwrap().get(candidates[1]).unwrap() { return Err(STVError::UnresolvedTie); } else { + state.logger.log_literal(format!("Tie between {} broken backwards.", smart_join(&candidates.iter().map(|c| c.name.as_str()).collect()))); return Ok(candidates[0]); } }