/* OpenTally: Open-source election vote counting * Copyright © 2021 Lee Yingtong Li (RunasSudo) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ use crate::numbers::Number; use std::collections::HashMap; use std::io::{BufRead, Lines}; pub struct Election { pub seats: usize, pub candidates: Vec, pub ballots: Vec>, } impl Election { pub fn from_blt(mut lines: Lines) -> Self { // Read first line let line = lines.next().expect("Unexpected EOF").expect("IO Error"); let mut bits = line.split(" "); let num_candidates = bits.next().expect("Syntax Error").parse().expect("Syntax Error"); let seats: usize = bits.next().expect("Syntax Error").parse().expect("Syntax Error"); // Initialise the Election object let mut election = Election { seats: seats, candidates: Vec::with_capacity(num_candidates), ballots: Vec::new(), }; // Read ballots for line in &mut lines { let line = line.expect("IO Error"); if line == "0" { break; } let mut bits = line.split(" "); let value = N::parse(bits.next().expect("Syntax Error")); let mut ballot = Ballot { orig_value: value, preferences: Vec::new(), }; for preference in bits { if preference != "0" { let preference = preference.parse::().expect("Syntax Error"); ballot.preferences.push(preference - 1); } } election.ballots.push(ballot); } // Read candidates for line in lines.take(num_candidates) { let mut line = &line.expect("IO Error")[..]; if line.starts_with("\"") && line.ends_with("\"") { line = &line[1..line.len()-1]; } election.candidates.push(Candidate { name: line.to_string() }); } return election; } } #[derive(PartialEq, Eq, Hash)] pub struct Candidate { pub name: String, } #[derive(Clone)] pub struct CountState<'a, N> { pub election: &'a Election, pub candidates: HashMap<&'a Candidate, CountCard<'a, N>>, pub exhausted: CountCard<'a, N>, pub quota: N, pub num_elected: usize, pub num_excluded: usize, } impl<'a, N: Number> CountState<'a, N> { pub fn new(election: &'a Election) -> Self { let mut state = CountState { election: &election, candidates: HashMap::new(), exhausted: CountCard::new(), quota: N::new(), num_elected: 0, num_excluded: 0, }; for candidate in election.candidates.iter() { state.candidates.insert(candidate, CountCard::new()); } return state; } pub fn step_all(&mut self) { for (_, count_card) in self.candidates.iter_mut() { count_card.step(); } self.exhausted.step(); } } pub enum CountStateOrRef<'a, N> { State(CountState<'a, N>), Ref(&'a CountState<'a, N>), } impl<'a, N> CountStateOrRef<'a, N> { pub fn as_ref(&self) -> &CountState { match self { CountStateOrRef::State(state) => &state, CountStateOrRef::Ref(state) => state, } } } pub struct StageResult<'a, N> { pub title: &'a str, pub logs: Vec<&'a str>, pub state: CountStateOrRef<'a, N>, } #[derive(Clone)] pub struct CountCard<'a, N> { pub state: CandidateState, pub order_elected: isize, pub orig_votes: N, pub transfers: N, pub votes: N, pub parcels: Vec>, } impl<'a, N: Number> CountCard<'a, N> { pub fn new() -> Self { return CountCard { state: CandidateState::HOPEFUL, order_elected: 0, orig_votes: N::new(), transfers: N::new(), votes: N::new(), parcels: Vec::new(), }; } //pub fn votes(&'a self) -> N { // return self.orig_votes.clone() + &self.transfers; //} pub fn transfer(&mut self, transfer: &'_ N) { self.transfers += transfer; self.votes += transfer; } pub fn step(&mut self) { self.orig_votes = self.votes.clone(); self.transfers = N::new(); } } pub type Parcel<'a, N> = Vec>; #[derive(Clone)] pub struct Vote<'a, N> { pub ballot: &'a Ballot, pub value: N, pub up_to_pref: usize, } pub struct Ballot { pub orig_value: N, pub preferences: Vec, } #[allow(dead_code)] #[derive(PartialEq)] #[derive(Clone)] pub enum CandidateState { HOPEFUL, GUARDED, ELECTED, DOOMED, EXCLUDED, }