|
|
|
@ -18,6 +18,7 @@ |
|
|
|
|
use crate::election::{Ballot, Candidate, Election}; |
|
|
|
|
use crate::numbers::Number; |
|
|
|
|
|
|
|
|
|
use anyhow::{Context, Result}; |
|
|
|
|
use csv::ReaderBuilder; |
|
|
|
|
use itertools::Itertools; |
|
|
|
|
|
|
|
|
@ -25,7 +26,7 @@ use std::collections::HashMap; |
|
|
|
|
use std::io::Read; |
|
|
|
|
|
|
|
|
|
/// Parse the given CSP file
|
|
|
|
|
pub fn parse_reader<R: Read, N: Number>(reader: R, require_1: bool, require_sequential: bool, require_strict_order: bool) -> Election<N> { |
|
|
|
|
pub fn parse_reader<R: Read, N: Number>(reader: R, require_1: bool, require_sequential: bool, require_strict_order: bool) -> Result<Election<N>> { |
|
|
|
|
// Read CSV file
|
|
|
|
|
let mut reader = ReaderBuilder::new() |
|
|
|
|
.has_headers(true) |
|
|
|
@ -33,14 +34,14 @@ pub fn parse_reader<R: Read, N: Number>(reader: R, require_1: bool, require_sequ |
|
|
|
|
|
|
|
|
|
// Read candidates
|
|
|
|
|
let mut candidates = Vec::new(); |
|
|
|
|
let mut idx_map = HashMap::new(); // Map csp index -> candidates index
|
|
|
|
|
let mut col_map = HashMap::new(); // Map csp column -> candidates index
|
|
|
|
|
|
|
|
|
|
for (i, cand_name) in reader.headers().expect("Syntax Error").into_iter().enumerate() { |
|
|
|
|
for (i, cand_name) in reader.headers()?.into_iter().enumerate() { |
|
|
|
|
if cand_name == "$mult" { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
idx_map.insert(i, candidates.len()); |
|
|
|
|
col_map.insert(i, candidates.len()); |
|
|
|
|
candidates.push(Candidate { |
|
|
|
|
name: cand_name.to_string(), |
|
|
|
|
}); |
|
|
|
@ -49,22 +50,22 @@ pub fn parse_reader<R: Read, N: Number>(reader: R, require_1: bool, require_sequ |
|
|
|
|
// Read ballots
|
|
|
|
|
let mut ballots = Vec::new(); |
|
|
|
|
|
|
|
|
|
for record in reader.into_records() { |
|
|
|
|
let record = record.expect("Syntax Error"); |
|
|
|
|
for (csv_row, record) in reader.into_records().enumerate() { |
|
|
|
|
let record = record?; |
|
|
|
|
|
|
|
|
|
let mut value = N::one(); |
|
|
|
|
|
|
|
|
|
// Record preferences
|
|
|
|
|
let mut preferences = Vec::new(); // Vec of (ranking, candidate index)
|
|
|
|
|
for (csv_index, preference) in record.into_iter().enumerate() { |
|
|
|
|
match idx_map.get(&csv_index) { |
|
|
|
|
for (csv_col, preference) in record.into_iter().enumerate() { |
|
|
|
|
match col_map.get(&csv_col) { |
|
|
|
|
Some(cand_index) => { |
|
|
|
|
// Preference
|
|
|
|
|
if preference.len() == 0 || preference == "-" { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let preference: usize = preference.parse().expect("Syntax Error"); |
|
|
|
|
let preference: usize = preference.parse().context(format!("Invalid number \"{}\" at row {}, column {}", preference, csv_row + 2, csv_col + 1))?; |
|
|
|
|
if preference == 0 { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
@ -73,7 +74,7 @@ pub fn parse_reader<R: Read, N: Number>(reader: R, require_1: bool, require_sequ |
|
|
|
|
} |
|
|
|
|
None => { |
|
|
|
|
// $mult column
|
|
|
|
|
let mult: usize = preference.parse().expect("Syntax Error"); |
|
|
|
|
let mult: usize = preference.parse().context(format!("Invalid number \"{}\" at row {}, column {}", preference, csv_row + 2, csv_col + 1))?; |
|
|
|
|
if mult == 1 { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
@ -131,7 +132,7 @@ pub fn parse_reader<R: Read, N: Number>(reader: R, require_1: bool, require_sequ |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return Election { |
|
|
|
|
return Ok(Election { |
|
|
|
|
name: String::new(), |
|
|
|
|
seats: 0, |
|
|
|
|
candidates: candidates, |
|
|
|
@ -139,5 +140,5 @@ pub fn parse_reader<R: Read, N: Number>(reader: R, require_1: bool, require_sequ |
|
|
|
|
ballots: ballots, |
|
|
|
|
total_votes: None, |
|
|
|
|
constraints: None, |
|
|
|
|
}; |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|