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, winner_count) = { let args: Vec = env::args().collect(); if args.len() != 3 { eprintln!("Usage: {} ", args[0]); return; } (args[1].clone(), args[2].parse::().unwrap()) }; 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 names = match ty { BallotType::Party => counter.header.parties.iter().map(|v| v.name.clone()).collect_vec(), BallotType::Candidate => counter.header.candidates.iter().map(|v| match v.party_id { Some(party_id) => format!("{}: {}", v.name, &counter.header.parties[party_id].name), None => v.name.to_owned(), }).collect_vec(), }; let mut running = vec![true; names.len()]; print!(",,,"); for name in names.iter() { print!(",{name:?},"); } println!(); eprintln!("Running election rounds:"); let to_percent = |score: f64| score / f64::from(total) * 100.0; let mut scores_last: Option> = None; let mut id_worst_last: Option = None; for index in 0..(running.len() - winner_count + 1) { let scores = counter.count_all_with_powers_of_2(ty, &running); let scores_ordered = scores.iter().copied().enumerate().filter(|&(i,_)| running[i]).sorted_by(|a, b| f64::total_cmp(&b.1, &a.1)).collect_vec(); let (id_worst, score_worst) = match scores_ordered.last() { Some(&v) => v, None => break, }; if let Some(scores_last) = scores_last { let id_worst_last = id_worst_last.unwrap(); let score_worst_last = scores_last[id_worst_last]; print!(",{:?},{score_worst_last},{:.3}%", names[id_worst_last], to_percent(score_worst_last)); for ((&score, &score_last), &running) in scores.iter().zip(scores_last.iter()).zip(running.iter()) { if running { let score_diff = score - score_last; print!(",+{score_diff},+{:.3}%", score_diff / score_worst_last * 100.0); } else { print!(",,"); } } println!(); } print!("{},,,", index + 1); for (&score, &running) in scores.iter().zip(running.iter()) { if running { print!(",{score},{:.3}%", to_percent(score)); } else { print!(",,"); } } println!(); running[id_worst] = false; scores_last = Some(scores); id_worst_last = Some(id_worst); let mut total_votes = 0.0; println!(); eprintln!("Round {}:", index+1); for (place, &(id, score)) in scores_ordered.iter().take(winner_count).enumerate() { eprintln!(" {}. {}: {score}, {:.3}%", place + 1, names[id], to_percent(score)); total_votes += score; } if scores_ordered.len() > winner_count { eprintln!(" {}. {}: {score_worst}, {:.3}%", scores_ordered.len(), names[id_worst], to_percent(score_worst)); } eprintln!(" Total: {total_votes}, {:.3}%", to_percent(total_votes.into())); } }