use crate::tally::Metadata;
use std::collections::{HashMap, HashSet};
#[derive(Clone)]
pub struct VoteMatrix {
n: u32,
data: HashMap<(u32, u32), u64>,
meta: Metadata,
}
pub struct VoteMatrixPairIterator<'a> {
wrapped: std::collections::hash_map::Iter<'a, (u32, u32), u64>,
base: &'a VoteMatrix,
}
impl<'a> Iterator for VoteMatrixPairIterator<'a> {
type Item = ((u32, u32), (u64, u64));
fn next(&mut self) -> Option<Self::Item> {
if let Some((&(i, j), &v)) = self.wrapped.next() {
let alt = self.base[(j, i)];
Some(((i, j), (v, alt)))
} else {
None
}
}
}
impl VoteMatrix {
pub fn with_vector_bycol(x: &[u64]) -> VoteMatrix {
if x.is_empty() {
return VoteMatrix {
n: 0,
data: HashMap::new(),
meta: Metadata::empty(),
};
}
let s: f64 = x.len() as f64;
let ln = (1. + (1. + 4. * s).sqrt()) / 2.;
let mut data: HashMap<(u32, u32), u64> = HashMap::new();
if ln.fract() != 0. {
panic!("Cannot build VoteMatrix from a vector of such size");
}
let n: u32 = ln as u32;
let mut ii = x.iter();
for j in 0..n {
for i in 0..n {
if i != j {
let ne = *ii.next().unwrap();
if ne > 0 {
data.insert((i, j), ne);
}
}
}
}
VoteMatrix {
n,
data,
meta: Metadata::empty(),
}
}
pub fn pairs(&self) -> VoteMatrixPairIterator {
VoteMatrixPairIterator {
wrapped: self.data.iter(),
base: self,
}
}
}
use crate::tally::Tally;
impl Tally for VoteMatrix {
fn get_meta(&self) -> &'_ Metadata {
&self.meta
}
fn get_candidates(&self) -> u32 {
self.n
}
}
use std::fmt;
impl fmt::Debug for VoteMatrix {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "/ {0}x{0} VoteMatrix:\n|", self.n)?;
for i in 0..self.n {
for j in 0..self.n {
if i != j {
write!(f, "{:^5}", &self.data.get(&(i, j)).unwrap_or(&0))?;
} else {
write!(f, " - ")?;
}
}
write!(f, "\n|")?
}
writeln!(f, ".\n")
}
}
use std::ops::Index;
impl Index<(u32, u32)> for VoteMatrix {
type Output = u64;
fn index(&self, ij: (u32, u32)) -> &u64 {
self.data.get(&ij).unwrap_or(&0)
}
}
use std::iter::FromIterator;
impl FromIterator<(u32, u32, u64)> for VoteMatrix {
fn from_iter<I: IntoIterator<Item = (u32, u32, u64)>>(i: I) -> Self {
let data: HashMap<(u32, u32), u64> = i.into_iter().map(|(i, j, k)| ((i, j), k)).collect();
let n = if let Some(cmax) = data.keys().map(|&(i, j)| std::cmp::max(i, j)).max() {
cmax + 1
} else {
0
};
VoteMatrix {
n,
data,
meta: Metadata::empty(),
}
}
}
use self::VoteToken::*;
use crate::tally::{VoteReadError, VoteToken};
impl FromIterator<VoteToken> for Result<VoteMatrix, VoteReadError> {
fn from_iter<I: IntoIterator<Item = VoteToken>>(i: I) -> Self {
let mut data: HashMap<(u32, u32), u64> = HashMap::new();
let mut withdrawn: HashSet<u32> = HashSet::new();
let mut candidates: Option<u32> = None;
let mut candidate_names: HashMap<u32, String> = HashMap::new();
let mut title: Option<String> = None;
let mut seats: Option<u32> = None;
let mut ballot: HashMap<u32, i32> = HashMap::new();
let mut ballot_repeat: Option<u64> = None;
for token in i {
match token {
ReadFailure(x) => return Err(x),
DeclareCandidates(x) => candidates = candidates.or(Some(x)),
DeclareSeats(x) => seats = seats.or(Some(x)),
WithdrawCandidate(x) => {
withdrawn.insert(x);
}
BallotRepeat(x) => ballot_repeat = Some(x),
Vote(pref, cand) => {
ballot.insert(cand, pref);
}
EndBallot => {
let cn = candidates.unwrap();
for cand in 0..cn {
ballot.entry(cand).or_insert(i32::MAX);
}
for (&runner, &r_pref) in &ballot {
for (&opponent, &o_pref) in &ballot {
if r_pref < o_pref {
*data.entry((runner, opponent)).or_insert(0) +=
ballot_repeat.unwrap(); }
}
}
ballot_repeat = None;
ballot.clear();
}
ElectionTitle(x) => title = title.or(Some(x)),
CandidateName(cand, name) => {
candidate_names.insert(cand, name);
}
_ => (),
}
}
if let Some(n) = candidates {
Ok(VoteMatrix {
n,
data,
meta: Metadata::new(seats, withdrawn, candidate_names, title),
})
} else {
Err(VoteReadError::InvalidMeta)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_blt_and_idx() {
use crate::io::BltReader;
use std::collections::HashSet;
use std::io::Cursor;
const EASY_BLT: &'static [u8] =
b" 3 1\n -3 \n 7 1 =2 =3 0\n \n 17 2 3 1 0\n 0 17 8 0\n27 3 2 1 0\n 0 \n \"A\"\n \n\"B\" \n\"C\"\n \"Title\" \n";
let mut cursor = Cursor::new(EASY_BLT);
let blt_reader = BltReader::new(&mut cursor);
let vc = blt_reader
.collect::<Result<VoteMatrix, VoteReadError>>()
.unwrap();
assert_eq!(vc[(1, 0)], 44);
assert_eq!(vc[(2, 0)], 44);
assert_eq!(vc[(0, 1)], 0);
assert_eq!(vc[(2, 1)], 27);
assert_eq!(vc[(0, 2)], 0);
assert_eq!(vc[(1, 2)], 17);
let ps = vc.pairs().collect::<HashSet<_>>();
assert!(ps.contains(&((2, 0), (44, 0))));
assert!(ps.contains(&((2, 1), (27, 17))));
assert!(ps.contains(&((1, 0), (44, 0))));
assert!(ps.contains(&((1, 2), (17, 27))));
assert_eq!(ps.len(), 4);
}
}