ballot-counter/src/main.rs

109 lines
2.8 KiB
Rust

use std::env;
use ballot::BallotType;
use counter::Counter;
use itertools::Itertools;
pub mod candidate;
pub mod ballot;
pub mod header;
pub mod party;
mod counter;
fn main() {
let csv_path = {
let args: Vec<String> = env::args().collect();
if args.len() != 2 {
eprintln!("Usage: {} <filename.csv>", args[0]);
return;
}
args[1].clone()
};
let csv = quick_csv::Csv::from_file(csv_path).unwrap().flexible(true);
let counter = Counter::new(csv).unwrap();
eprintln!("Header: {:#?}", counter.header);
eprintln!("Parties: {}", counter.header.parties.len());
eprintln!("Candidates: {}", counter.header.candidates.len());
let total = counter.ballots.len() as u32;
let mut total_atl = 0u32;
let mut total_btl = 0u32;
for ballot in counter.ballots.iter() {
*match ballot.ty {
BallotType::Party => &mut total_atl,
BallotType::Candidate => &mut total_btl,
} += 1;
}
eprintln!("Above the line: {total_atl} {}%", f64::from(total_atl) / f64::from(total) * 100.0);
eprintln!("Below the line: {total_btl} {}%", f64::from(total_btl) / f64::from(total) * 100.0);
eprintln!("Total: {total}");
let ty = BallotType::Candidate;
let winner_count = 6;
let names = match ty {
BallotType::Party => counter.header.parties.iter().map(|v| v.name.clone()).collect_vec(),
BallotType::Candidate => counter.header.candidates.iter().map(|v| format!("{}: {}",
v.name,
v.party_id.map_or("IND", |id| &counter.header.parties[id].name),
)).collect_vec(),
};
let mut running = vec![true; names.len()];
let mut first = true;
for name in names.iter() {
if first {
first = false;
}
else {
print!(",");
}
print!("{name:?}");
}
println!();
eprintln!("Running election rounds:");
let mut scores: Vec<u32> = Vec::new();
for index in 0..(running.len() - winner_count) {
scores = counter.count_primaries(ty, &running);
let (id_worst, score_worst) = match scores.iter().copied().enumerate().filter(|&(i,_)| running[i]).min_by(|a, b| u32::cmp(&a.1, &b.1)) {
Some(v) => v,
None => break
};
let (id_best, score_best) = match scores.iter().copied().enumerate().filter(|&(i,_)| running[i]).min_by(|a, b| u32::cmp(&b.1, &a.1)) {
Some(v) => v,
None => break,
};
let mut first = true;
for (&score, &running) in scores.iter().zip(running.iter()) {
if first {
first = false;
}
else {
print!(",");
}
if running {
print!("{score}");
}
}
running[id_worst] = false;
println!();
eprintln!(" - {index}:");
eprintln!(" Best: {} ({score_best})", names[id_best]);
eprintln!(" Lost: {} ({score_worst})", names[id_worst]);
}
let winners = names.iter().enumerate().filter_map(|(index, name)| match running[index] {
true => Some((name.as_str(), scores[index])),
false => None,
}).collect_vec();
eprintln!("Winners: {winners:#?}");
}