|
|
|
@ -24,6 +24,7 @@ use crate::stv::gregory::TransferTable; |
|
|
|
|
use crate::stv::meek::BallotTree; |
|
|
|
|
|
|
|
|
|
use itertools::Itertools; |
|
|
|
|
use nohash_hasher::BuildNoHashHasher; |
|
|
|
|
|
|
|
|
|
#[cfg(not(target_arch = "wasm32"))] |
|
|
|
|
use rkyv::{Archive, Deserialize, Serialize}; |
|
|
|
@ -33,6 +34,7 @@ use crate::numbers::{SerializedNumber, SerializedOptionNumber}; |
|
|
|
|
use std::cmp::max; |
|
|
|
|
use std::collections::HashMap; |
|
|
|
|
use std::fmt; |
|
|
|
|
use std::hash::{Hash, Hasher}; |
|
|
|
|
|
|
|
|
|
/// An election to be counted
|
|
|
|
|
#[cfg_attr(not(target_arch = "wasm32"), derive(Archive, Deserialize, Serialize))] |
|
|
|
@ -98,15 +100,26 @@ impl<N: Number> Election<N> { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// A candidate in an [Election]
|
|
|
|
|
#[derive(Clone, Eq, Hash, PartialEq)] |
|
|
|
|
#[derive(Clone, Eq, PartialEq)] |
|
|
|
|
#[cfg_attr(not(target_arch = "wasm32"), derive(Archive, Deserialize, Serialize))] |
|
|
|
|
pub struct Candidate { |
|
|
|
|
/// Index of the candidate
|
|
|
|
|
pub index: usize, |
|
|
|
|
/// Name of the candidate
|
|
|
|
|
pub name: String, |
|
|
|
|
/// If this candidate is a dummy candidate (e.g. for --constraint-mode repeat_count)
|
|
|
|
|
pub is_dummy: bool, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Hash for Candidate { |
|
|
|
|
fn hash<H: Hasher>(&self, hasher: &mut H) { |
|
|
|
|
// Custom implementation of hash for use with NoHashHasher, to improve performance
|
|
|
|
|
hasher.write_usize(self.index); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl nohash_hasher::IsEnabled for Candidate {} |
|
|
|
|
|
|
|
|
|
/// The current state of counting an [Election]
|
|
|
|
|
#[derive(Clone)] |
|
|
|
|
pub struct CountState<'a, N: Number> { |
|
|
|
@ -114,7 +127,7 @@ pub struct CountState<'a, N: Number> { |
|
|
|
|
pub election: &'a Election<N>, |
|
|
|
|
|
|
|
|
|
/// [HashMap] of [CountCard]s for each [Candidate] in the election
|
|
|
|
|
pub candidates: HashMap<&'a Candidate, CountCard<'a, N>>, |
|
|
|
|
pub candidates: HashMap<&'a Candidate, CountCard<'a, N>, BuildNoHashHasher<Candidate>>, |
|
|
|
|
/// [CountCard] representing the exhausted pile
|
|
|
|
|
pub exhausted: CountCard<'a, N>, |
|
|
|
|
/// [CountCard] representing loss by fraction
|
|
|
|
@ -124,9 +137,9 @@ pub struct CountState<'a, N: Number> { |
|
|
|
|
pub ballot_tree: Option<BallotTree<'a, N>>, |
|
|
|
|
|
|
|
|
|
/// Values used to break ties, based on forwards tie-breaking
|
|
|
|
|
pub forwards_tiebreak: Option<HashMap<&'a Candidate, usize>>, |
|
|
|
|
pub forwards_tiebreak: Option<HashMap<&'a Candidate, usize, BuildNoHashHasher<Candidate>>>, |
|
|
|
|
/// Values used to break ties, based on backwards tie-breaking
|
|
|
|
|
pub backwards_tiebreak: Option<HashMap<&'a Candidate, usize>>, |
|
|
|
|
pub backwards_tiebreak: Option<HashMap<&'a Candidate, usize, BuildNoHashHasher<Candidate>>>, |
|
|
|
|
/// [SHARandom] for random tie-breaking
|
|
|
|
|
pub random: Option<SHARandom<'a>>, |
|
|
|
|
|
|
|
|
@ -161,7 +174,7 @@ impl<'a, N: Number> CountState<'a, N> { |
|
|
|
|
pub fn new(election: &'a Election<N>) -> Self { |
|
|
|
|
let mut state = CountState { |
|
|
|
|
election, |
|
|
|
|
candidates: HashMap::new(), |
|
|
|
|
candidates: HashMap::with_capacity_and_hasher(election.candidates.len(), BuildNoHashHasher::default()), |
|
|
|
|
exhausted: CountCard::new(), |
|
|
|
|
loss_fraction: CountCard::new(), |
|
|
|
|
ballot_tree: None, |
|
|
|
@ -590,7 +603,18 @@ pub enum RollbackState<'a, N> { |
|
|
|
|
/// Not rolling back
|
|
|
|
|
Normal, |
|
|
|
|
/// Start rolling back next stage
|
|
|
|
|
NeedsRollback { candidates: Option<HashMap<&'a Candidate, CountCard<'a, N>>>, exhausted: Option<CountCard<'a, N>>, constraint: &'a Constraint, group: &'a ConstrainedGroup }, |
|
|
|
|
NeedsRollback { |
|
|
|
|
candidates: Option<HashMap<&'a Candidate, CountCard<'a, N>, BuildNoHashHasher<Candidate>>>, |
|
|
|
|
exhausted: Option<CountCard<'a, N>>, |
|
|
|
|
constraint: &'a Constraint, |
|
|
|
|
group: &'a ConstrainedGroup |
|
|
|
|
}, |
|
|
|
|
/// Rolling back
|
|
|
|
|
RollingBack { candidates: Option<HashMap<&'a Candidate, CountCard<'a, N>>>, exhausted: Option<CountCard<'a, N>>, candidate_distributing: Option<&'a Candidate>, constraint: Option<&'a Constraint>, group: Option<&'a ConstrainedGroup> }, |
|
|
|
|
RollingBack { |
|
|
|
|
candidates: Option<HashMap<&'a Candidate, CountCard<'a, N>, BuildNoHashHasher<Candidate>>>, |
|
|
|
|
exhausted: Option<CountCard<'a, N>>, |
|
|
|
|
candidate_distributing: Option<&'a Candidate>, |
|
|
|
|
constraint: Option<&'a Constraint>, |
|
|
|
|
group: Option<&'a ConstrainedGroup> |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|