Use NoHashHasher for Candidate HashMaps to improve performance
This commit is contained in:
parent
61b22b388d
commit
422a198cf5
|
@ -651,6 +651,12 @@ dependencies = [
|
||||||
"rawpointer",
|
"rawpointer",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nohash-hasher"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "normalize-line-endings"
|
name = "normalize-line-endings"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
@ -738,6 +744,7 @@ dependencies = [
|
||||||
"itertools",
|
"itertools",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"ndarray",
|
"ndarray",
|
||||||
|
"nohash-hasher",
|
||||||
"num-bigint",
|
"num-bigint",
|
||||||
"num-rational",
|
"num-rational",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
|
|
@ -17,6 +17,7 @@ csv = "1.1.6"
|
||||||
derive_builder = "0.10.2"
|
derive_builder = "0.10.2"
|
||||||
derive_more = "0.99.14"
|
derive_more = "0.99.14"
|
||||||
git-version = "0.3.4"
|
git-version = "0.3.4"
|
||||||
|
nohash-hasher = "0.2.0"
|
||||||
ibig = "0.3.2"
|
ibig = "0.3.2"
|
||||||
itertools = "0.10.1"
|
itertools = "0.10.1"
|
||||||
ndarray = "0.15.3"
|
ndarray = "0.15.3"
|
||||||
|
|
|
@ -22,6 +22,7 @@ use crate::ties::{self, TieStrategy};
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use ndarray::{Array, Dimension, IxDyn};
|
use ndarray::{Array, Dimension, IxDyn};
|
||||||
|
use nohash_hasher::BuildNoHashHasher;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use rkyv::{Archive, Deserialize, Serialize};
|
use rkyv::{Archive, Deserialize, Serialize};
|
||||||
|
@ -130,7 +131,7 @@ impl Constraints {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if any elected candidates exceed constrained maximums
|
/// Check if any elected candidates exceed constrained maximums
|
||||||
pub fn exceeds_maximum<'a, N: Number>(&self, election: &Election<N>, candidates: HashMap<&'a Candidate, CountCard<'a, N>>) -> Option<(&Constraint, &ConstrainedGroup)> {
|
pub fn exceeds_maximum<'a, N: Number>(&self, election: &Election<N>, candidates: HashMap<&'a Candidate, CountCard<'a, N>, BuildNoHashHasher<Candidate>>) -> Option<(&Constraint, &ConstrainedGroup)> {
|
||||||
for constraint in &self.0 {
|
for constraint in &self.0 {
|
||||||
for group in &constraint.groups {
|
for group in &constraint.groups {
|
||||||
let mut num_elected = 0;
|
let mut num_elected = 0;
|
||||||
|
@ -360,7 +361,7 @@ impl ConstraintMatrix {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Update cands/elected in innermost cells based on the provided [CountState::candidates](crate::election::CountState::candidates)
|
/// Update cands/elected in innermost cells based on the provided [CountState::candidates](crate::election::CountState::candidates)
|
||||||
pub fn update_from_state<N: Number>(&mut self, election: &Election<N>, candidates: &HashMap<&Candidate, CountCard<N>>) {
|
pub fn update_from_state<N: Number>(&mut self, election: &Election<N>, candidates: &HashMap<&Candidate, CountCard<N>, BuildNoHashHasher<Candidate>>) {
|
||||||
let constraints = election.constraints.as_ref().unwrap();
|
let constraints = election.constraints.as_ref().unwrap();
|
||||||
|
|
||||||
// Reset innermost cells
|
// Reset innermost cells
|
||||||
|
@ -626,7 +627,7 @@ impl ops::IndexMut<&[usize]> for ConstraintMatrix {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the [Candidate]s referred to in the given [ConstraintMatrixCell] at location `idx`
|
/// Return the [Candidate]s referred to in the given [ConstraintMatrixCell] at location `idx`
|
||||||
fn candidates_in_constraint_cell<'a, N: Number>(election: &'a Election<N>, candidates: &HashMap<&Candidate, CountCard<N>>, idx: &[usize]) -> Vec<&'a Candidate> {
|
fn candidates_in_constraint_cell<'a, N: Number>(election: &'a Election<N>, candidates: &HashMap<&Candidate, CountCard<N>, BuildNoHashHasher<Candidate>>, idx: &[usize]) -> Vec<&'a Candidate> {
|
||||||
let mut result: Vec<&Candidate> = Vec::new();
|
let mut result: Vec<&Candidate> = Vec::new();
|
||||||
for (i, candidate) in election.candidates.iter().enumerate() {
|
for (i, candidate) in election.candidates.iter().enumerate() {
|
||||||
let cc = &candidates[candidate];
|
let cc = &candidates[candidate];
|
||||||
|
@ -782,7 +783,7 @@ pub fn init_repeat_count<N: Number>(election: &mut Election<N>) {
|
||||||
|
|
||||||
/// Initialise the rollback for [ConstraintMode::TwoStage]
|
/// Initialise the rollback for [ConstraintMode::TwoStage]
|
||||||
pub fn init_repeat_count_rollback<'a, N: Number>(state: &mut CountState<'a, N>, constraint: &'a Constraint, group: &'a ConstrainedGroup) {
|
pub fn init_repeat_count_rollback<'a, N: Number>(state: &mut CountState<'a, N>, constraint: &'a Constraint, group: &'a ConstrainedGroup) {
|
||||||
let mut rollback_candidates = HashMap::new();
|
let mut rollback_candidates = HashMap::with_capacity_and_hasher(state.candidates.len(), BuildNoHashHasher::default());
|
||||||
let rollback_exhausted = state.exhausted.clone();
|
let rollback_exhausted = state.exhausted.clone();
|
||||||
|
|
||||||
// Copy ballot papers to rollback state
|
// Copy ballot papers to rollback state
|
||||||
|
|
|
@ -24,6 +24,7 @@ use crate::stv::gregory::TransferTable;
|
||||||
use crate::stv::meek::BallotTree;
|
use crate::stv::meek::BallotTree;
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use nohash_hasher::BuildNoHashHasher;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use rkyv::{Archive, Deserialize, Serialize};
|
use rkyv::{Archive, Deserialize, Serialize};
|
||||||
|
@ -33,6 +34,7 @@ use crate::numbers::{SerializedNumber, SerializedOptionNumber};
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
/// An election to be counted
|
/// An election to be counted
|
||||||
#[cfg_attr(not(target_arch = "wasm32"), derive(Archive, Deserialize, Serialize))]
|
#[cfg_attr(not(target_arch = "wasm32"), derive(Archive, Deserialize, Serialize))]
|
||||||
|
@ -98,15 +100,26 @@ impl<N: Number> Election<N> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A candidate in an [Election]
|
/// A candidate in an [Election]
|
||||||
#[derive(Clone, Eq, Hash, PartialEq)]
|
#[derive(Clone, Eq, PartialEq)]
|
||||||
#[cfg_attr(not(target_arch = "wasm32"), derive(Archive, Deserialize, Serialize))]
|
#[cfg_attr(not(target_arch = "wasm32"), derive(Archive, Deserialize, Serialize))]
|
||||||
pub struct Candidate {
|
pub struct Candidate {
|
||||||
|
/// Index of the candidate
|
||||||
|
pub index: usize,
|
||||||
/// Name of the candidate
|
/// Name of the candidate
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// If this candidate is a dummy candidate (e.g. for --constraint-mode repeat_count)
|
/// If this candidate is a dummy candidate (e.g. for --constraint-mode repeat_count)
|
||||||
pub is_dummy: bool,
|
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]
|
/// The current state of counting an [Election]
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CountState<'a, N: Number> {
|
pub struct CountState<'a, N: Number> {
|
||||||
|
@ -114,7 +127,7 @@ pub struct CountState<'a, N: Number> {
|
||||||
pub election: &'a Election<N>,
|
pub election: &'a Election<N>,
|
||||||
|
|
||||||
/// [HashMap] of [CountCard]s for each [Candidate] in the election
|
/// [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
|
/// [CountCard] representing the exhausted pile
|
||||||
pub exhausted: CountCard<'a, N>,
|
pub exhausted: CountCard<'a, N>,
|
||||||
/// [CountCard] representing loss by fraction
|
/// [CountCard] representing loss by fraction
|
||||||
|
@ -124,9 +137,9 @@ pub struct CountState<'a, N: Number> {
|
||||||
pub ballot_tree: Option<BallotTree<'a, N>>,
|
pub ballot_tree: Option<BallotTree<'a, N>>,
|
||||||
|
|
||||||
/// Values used to break ties, based on forwards tie-breaking
|
/// 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
|
/// 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
|
/// [SHARandom] for random tie-breaking
|
||||||
pub random: Option<SHARandom<'a>>,
|
pub random: Option<SHARandom<'a>>,
|
||||||
|
|
||||||
|
@ -161,7 +174,7 @@ impl<'a, N: Number> CountState<'a, N> {
|
||||||
pub fn new(election: &'a Election<N>) -> Self {
|
pub fn new(election: &'a Election<N>) -> Self {
|
||||||
let mut state = CountState {
|
let mut state = CountState {
|
||||||
election,
|
election,
|
||||||
candidates: HashMap::new(),
|
candidates: HashMap::with_capacity_and_hasher(election.candidates.len(), BuildNoHashHasher::default()),
|
||||||
exhausted: CountCard::new(),
|
exhausted: CountCard::new(),
|
||||||
loss_fraction: CountCard::new(),
|
loss_fraction: CountCard::new(),
|
||||||
ballot_tree: None,
|
ballot_tree: None,
|
||||||
|
@ -590,7 +603,18 @@ pub enum RollbackState<'a, N> {
|
||||||
/// Not rolling back
|
/// Not rolling back
|
||||||
Normal,
|
Normal,
|
||||||
/// Start rolling back next stage
|
/// 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
|
/// 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>
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,9 +175,10 @@ impl<N: Number, I: Iterator<Item=char>> BLTParser<N, I> {
|
||||||
|
|
||||||
/// Parse the list of strings at the end of the BLT file
|
/// Parse the list of strings at the end of the BLT file
|
||||||
fn strings(&mut self) -> Result<(), ParseError> {
|
fn strings(&mut self) -> Result<(), ParseError> {
|
||||||
for _ in 0..self.num_candidates {
|
for index in 0..self.num_candidates {
|
||||||
let name = self.string()?;
|
let name = self.string()?;
|
||||||
self.election.candidates.push(Candidate {
|
self.election.candidates.push(Candidate {
|
||||||
|
index,
|
||||||
name,
|
name,
|
||||||
is_dummy: false,
|
is_dummy: false,
|
||||||
});
|
});
|
||||||
|
|
|
@ -36,13 +36,14 @@ pub fn parse_reader<R: Read, N: Number>(reader: R, require_1: bool, require_sequ
|
||||||
let mut candidates = Vec::new();
|
let mut candidates = Vec::new();
|
||||||
let mut col_map = HashMap::new(); // Map csp column -> candidates index
|
let mut col_map = HashMap::new(); // Map csp column -> candidates index
|
||||||
|
|
||||||
for (i, cand_name) in reader.headers()?.into_iter().enumerate() {
|
for (index, cand_name) in reader.headers()?.into_iter().enumerate() {
|
||||||
if cand_name == "$mult" {
|
if cand_name == "$mult" {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
col_map.insert(i, candidates.len());
|
col_map.insert(index, candidates.len());
|
||||||
candidates.push(Candidate {
|
candidates.push(Candidate {
|
||||||
|
index,
|
||||||
name: cand_name.to_string(),
|
name: cand_name.to_string(),
|
||||||
is_dummy: false,
|
is_dummy: false,
|
||||||
});
|
});
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use nohash_hasher::BuildNoHashHasher;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use prettytable::{Cell, Row, Table};
|
use prettytable::{Cell, Row, Table};
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
|
@ -52,13 +54,15 @@ pub struct TransferTable<'e, N: Number> {
|
||||||
impl<'e, N: Number> TransferTable<'e, N> {
|
impl<'e, N: Number> TransferTable<'e, N> {
|
||||||
/// Return a new [TransferTable] for an exclusion
|
/// Return a new [TransferTable] for an exclusion
|
||||||
pub fn new_exclusion(hopefuls: Vec<&'e Candidate>) -> Self {
|
pub fn new_exclusion(hopefuls: Vec<&'e Candidate>) -> Self {
|
||||||
TransferTable {
|
let num_hopefuls = hopefuls.len();
|
||||||
|
|
||||||
|
return TransferTable {
|
||||||
hopefuls,
|
hopefuls,
|
||||||
columns: Vec::new(),
|
columns: Vec::new(),
|
||||||
total: TransferTableColumn {
|
total: TransferTableColumn {
|
||||||
value_fraction: N::new(),
|
value_fraction: N::new(),
|
||||||
order: 0,
|
order: 0,
|
||||||
cells: HashMap::new(),
|
cells: HashMap::with_capacity_and_hasher(num_hopefuls, BuildNoHashHasher::default()),
|
||||||
exhausted: TransferTableCell { ballots: N::new(), votes_in: N::new(), votes_out: N::new() },
|
exhausted: TransferTableCell { ballots: N::new(), votes_in: N::new(), votes_out: N::new() },
|
||||||
total: TransferTableCell { ballots: N::new(), votes_in: N::new(), votes_out: N::new() },
|
total: TransferTableCell { ballots: N::new(), votes_in: N::new(), votes_out: N::new() },
|
||||||
},
|
},
|
||||||
|
@ -71,13 +75,15 @@ impl<'e, N: Number> TransferTable<'e, N> {
|
||||||
|
|
||||||
/// Return a new [TransferTable] for a surplus distribution
|
/// Return a new [TransferTable] for a surplus distribution
|
||||||
pub fn new_surplus(hopefuls: Vec<&'e Candidate>, surplus: N, surpfrac: Option<N>, surpfrac_numer: Option<N>, surpfrac_denom: Option<N>) -> Self {
|
pub fn new_surplus(hopefuls: Vec<&'e Candidate>, surplus: N, surpfrac: Option<N>, surpfrac_numer: Option<N>, surpfrac_denom: Option<N>) -> Self {
|
||||||
TransferTable {
|
let num_hopefuls = hopefuls.len();
|
||||||
|
|
||||||
|
return TransferTable {
|
||||||
hopefuls,
|
hopefuls,
|
||||||
columns: Vec::new(),
|
columns: Vec::new(),
|
||||||
total: TransferTableColumn {
|
total: TransferTableColumn {
|
||||||
value_fraction: N::new(),
|
value_fraction: N::new(),
|
||||||
order: 0,
|
order: 0,
|
||||||
cells: HashMap::new(),
|
cells: HashMap::with_capacity_and_hasher(num_hopefuls, BuildNoHashHasher::default()),
|
||||||
exhausted: TransferTableCell { ballots: N::new(), votes_in: N::new(), votes_out: N::new() },
|
exhausted: TransferTableCell { ballots: N::new(), votes_in: N::new(), votes_out: N::new() },
|
||||||
total: TransferTableCell { ballots: N::new(), votes_in: N::new(), votes_out: N::new() },
|
total: TransferTableCell { ballots: N::new(), votes_in: N::new(), votes_out: N::new() },
|
||||||
},
|
},
|
||||||
|
@ -102,7 +108,7 @@ impl<'e, N: Number> TransferTable<'e, N> {
|
||||||
let mut col = TransferTableColumn {
|
let mut col = TransferTableColumn {
|
||||||
value_fraction: value_fraction.clone(),
|
value_fraction: value_fraction.clone(),
|
||||||
order: order.unwrap_or(0),
|
order: order.unwrap_or(0),
|
||||||
cells: HashMap::new(),
|
cells: HashMap::with_hasher(BuildNoHashHasher::default()),
|
||||||
exhausted: TransferTableCell { ballots: N::new(), votes_in: N::new(), votes_out: N::new() },
|
exhausted: TransferTableCell { ballots: N::new(), votes_in: N::new(), votes_out: N::new() },
|
||||||
total: TransferTableCell { ballots: N::new(), votes_in: N::new(), votes_out: N::new() },
|
total: TransferTableCell { ballots: N::new(), votes_in: N::new(), votes_out: N::new() },
|
||||||
};
|
};
|
||||||
|
@ -124,7 +130,7 @@ impl<'e, N: Number> TransferTable<'e, N> {
|
||||||
let col = TransferTableColumn {
|
let col = TransferTableColumn {
|
||||||
value_fraction: value_fraction.clone(),
|
value_fraction: value_fraction.clone(),
|
||||||
order: order.unwrap_or(0),
|
order: order.unwrap_or(0),
|
||||||
cells: HashMap::new(),
|
cells: HashMap::with_hasher(BuildNoHashHasher::default()),
|
||||||
exhausted: TransferTableCell { ballots: ballots.clone(), votes_in: N::new(), votes_out: N::new() },
|
exhausted: TransferTableCell { ballots: ballots.clone(), votes_in: N::new(), votes_out: N::new() },
|
||||||
total: TransferTableCell { ballots: N::new(), votes_in: N::new(), votes_out: N::new() },
|
total: TransferTableCell { ballots: N::new(), votes_in: N::new(), votes_out: N::new() },
|
||||||
};
|
};
|
||||||
|
@ -585,7 +591,7 @@ pub struct TransferTableColumn<'e, N: Number> {
|
||||||
pub order: usize,
|
pub order: usize,
|
||||||
|
|
||||||
/// Cells in this column
|
/// Cells in this column
|
||||||
pub cells: HashMap<&'e Candidate, TransferTableCell<N>>,
|
pub cells: HashMap<&'e Candidate, TransferTableCell<N>, BuildNoHashHasher<Candidate>>,
|
||||||
|
|
||||||
/// Exhausted cell
|
/// Exhausted cell
|
||||||
pub exhausted: TransferTableCell<N>,
|
pub exhausted: TransferTableCell<N>,
|
||||||
|
|
|
@ -21,6 +21,7 @@ use crate::election::{Ballot, Candidate, CandidateState, CountCard, CountState,
|
||||||
use crate::numbers::Number;
|
use crate::numbers::Number;
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use nohash_hasher::BuildNoHashHasher;
|
||||||
|
|
||||||
use std::cmp::max;
|
use std::cmp::max;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -39,7 +40,7 @@ struct BallotInTree<'b, N: Number> {
|
||||||
pub struct BallotTree<'t, N: Number> {
|
pub struct BallotTree<'t, N: Number> {
|
||||||
num_ballots: N,
|
num_ballots: N,
|
||||||
ballots: Vec<BallotInTree<'t, N>>,
|
ballots: Vec<BallotInTree<'t, N>>,
|
||||||
next_preferences: Option<Box<HashMap<&'t Candidate, BallotTree<'t, N>>>>,
|
next_preferences: Option<Box<HashMap<&'t Candidate, BallotTree<'t, N>, BuildNoHashHasher<Candidate>>>>,
|
||||||
next_exhausted: Option<Box<BallotTree<'t, N>>>,
|
next_exhausted: Option<Box<BallotTree<'t, N>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +57,7 @@ impl<'t, N: Number> BallotTree<'t, N> {
|
||||||
|
|
||||||
/// Descend one level of the [BallotTree]
|
/// Descend one level of the [BallotTree]
|
||||||
fn descend_tree(&mut self, candidates: &'t [Candidate]) {
|
fn descend_tree(&mut self, candidates: &'t [Candidate]) {
|
||||||
let mut next_preferences: HashMap<&Candidate, BallotTree<N>> = HashMap::new();
|
let mut next_preferences: HashMap<&Candidate, BallotTree<N>, BuildNoHashHasher<Candidate>> = HashMap::with_capacity_and_hasher(candidates.len(), BuildNoHashHasher::default());
|
||||||
let mut next_exhausted = BallotTree::new();
|
let mut next_exhausted = BallotTree::new();
|
||||||
|
|
||||||
for bit in self.ballots.iter() {
|
for bit in self.ballots.iter() {
|
||||||
|
@ -164,7 +165,7 @@ where
|
||||||
/// Distribute preferences recursively
|
/// Distribute preferences recursively
|
||||||
///
|
///
|
||||||
/// Called by [distribute_preferences]
|
/// Called by [distribute_preferences]
|
||||||
fn distribute_recursively<'t, N: Number>(candidates: &mut HashMap<&'t Candidate, CountCard<N>>, exhausted: &mut CountCard<N>, tree: &mut BallotTree<'t, N>, remaining_multiplier: N, election: &'t Election<N>, opts: &STVOptions)
|
fn distribute_recursively<'t, N: Number>(candidates: &mut HashMap<&'t Candidate, CountCard<N>, BuildNoHashHasher<Candidate>>, exhausted: &mut CountCard<N>, tree: &mut BallotTree<'t, N>, remaining_multiplier: N, election: &'t Election<N>, opts: &STVOptions)
|
||||||
where
|
where
|
||||||
for<'r> &'r N: ops::Mul<&'r N, Output=N>,
|
for<'r> &'r N: ops::Mul<&'r N, Output=N>,
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,6 +35,7 @@ use crate::ties::{self, TieStrategy};
|
||||||
use derive_builder::Builder;
|
use derive_builder::Builder;
|
||||||
use derive_more::Constructor;
|
use derive_more::Constructor;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use nohash_hasher::BuildNoHashHasher;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use wasm_bindgen::prelude::wasm_bindgen;
|
use wasm_bindgen::prelude::wasm_bindgen;
|
||||||
|
|
||||||
|
@ -781,7 +782,7 @@ where
|
||||||
/// See [next_preferences]
|
/// See [next_preferences]
|
||||||
pub struct NextPreferencesResult<'a, N> {
|
pub struct NextPreferencesResult<'a, N> {
|
||||||
/// [NextPreferencesEntry] for each [Candidate]
|
/// [NextPreferencesEntry] for each [Candidate]
|
||||||
pub candidates: HashMap<&'a Candidate, NextPreferencesEntry<'a, N>>,
|
pub candidates: HashMap<&'a Candidate, NextPreferencesEntry<'a, N>, BuildNoHashHasher<Candidate>>,
|
||||||
/// [NextPreferencesEntry] for exhausted ballots
|
/// [NextPreferencesEntry] for exhausted ballots
|
||||||
pub exhausted: NextPreferencesEntry<'a, N>,
|
pub exhausted: NextPreferencesEntry<'a, N>,
|
||||||
/// Total weight of ballots examined
|
/// Total weight of ballots examined
|
||||||
|
@ -799,7 +800,7 @@ pub struct NextPreferencesEntry<'a, N> {
|
||||||
/// Count the given votes, grouping according to next available preference
|
/// Count the given votes, grouping according to next available preference
|
||||||
pub fn next_preferences<'a, N: Number>(state: &CountState<'a, N>, votes: Vec<Vote<'a, N>>) -> NextPreferencesResult<'a, N> {
|
pub fn next_preferences<'a, N: Number>(state: &CountState<'a, N>, votes: Vec<Vote<'a, N>>) -> NextPreferencesResult<'a, N> {
|
||||||
let mut result = NextPreferencesResult {
|
let mut result = NextPreferencesResult {
|
||||||
candidates: HashMap::new(),
|
candidates: HashMap::with_capacity_and_hasher(state.election.candidates.len(), BuildNoHashHasher::default()),
|
||||||
exhausted: NextPreferencesEntry {
|
exhausted: NextPreferencesEntry {
|
||||||
votes: Vec::new(),
|
votes: Vec::new(),
|
||||||
num_ballots: N::new(),
|
num_ballots: N::new(),
|
||||||
|
@ -1767,7 +1768,7 @@ fn init_tiebreaks<N: Number>(state: &mut CountState<N>, opts: &STVOptions) {
|
||||||
|
|
||||||
// Update forwards tie-breaking order
|
// Update forwards tie-breaking order
|
||||||
if opts.ties.iter().any(|t| t == &TieStrategy::Forwards) {
|
if opts.ties.iter().any(|t| t == &TieStrategy::Forwards) {
|
||||||
let mut hm: HashMap<&Candidate, usize> = HashMap::new();
|
let mut hm: HashMap<&Candidate, usize, BuildNoHashHasher<Candidate>> = HashMap::with_capacity_and_hasher(state.candidates.len(), BuildNoHashHasher::default());
|
||||||
for (i, group) in sorted_candidates.iter().enumerate() {
|
for (i, group) in sorted_candidates.iter().enumerate() {
|
||||||
for (candidate, _) in group.iter() {
|
for (candidate, _) in group.iter() {
|
||||||
hm.insert(candidate, i);
|
hm.insert(candidate, i);
|
||||||
|
@ -1778,7 +1779,7 @@ fn init_tiebreaks<N: Number>(state: &mut CountState<N>, opts: &STVOptions) {
|
||||||
|
|
||||||
// Update backwards tie-breaking order
|
// Update backwards tie-breaking order
|
||||||
if opts.ties.iter().any(|t| t == &TieStrategy::Backwards) {
|
if opts.ties.iter().any(|t| t == &TieStrategy::Backwards) {
|
||||||
let mut hm: HashMap<&Candidate, usize> = HashMap::new();
|
let mut hm: HashMap<&Candidate, usize, BuildNoHashHasher<Candidate>> = HashMap::with_capacity_and_hasher(state.candidates.len(), BuildNoHashHasher::default());
|
||||||
for (i, group) in sorted_candidates.iter().enumerate() {
|
for (i, group) in sorted_candidates.iter().enumerate() {
|
||||||
for (candidate, _) in group.iter() {
|
for (candidate, _) in group.iter() {
|
||||||
hm.insert(candidate, i);
|
hm.insert(candidate, i);
|
||||||
|
|
Loading…
Reference in New Issue