Implement stricter validation modes for CSP input
This commit is contained in:
parent
047a53d0d9
commit
3ceaf67091
|
@ -47,6 +47,18 @@ pub struct SubcmdOptions {
|
|||
/// Number of seats
|
||||
#[clap(help_heading=Some("ELECTION SPECIFICATION"), long)]
|
||||
seats: Option<usize>,
|
||||
|
||||
/// Require 1st preference
|
||||
#[clap(help_heading=Some("PREFERENCE VALIDATION"), long)]
|
||||
require_1: bool,
|
||||
|
||||
/// Require sequential preferences
|
||||
#[clap(help_heading=Some("PREFERENCE VALIDATION"), long)]
|
||||
require_sequential: bool,
|
||||
|
||||
/// Require strict ordering of preferences
|
||||
#[clap(help_heading=Some("PREFERENCE VALIDATION"), long)]
|
||||
require_strict_order: bool,
|
||||
}
|
||||
|
||||
/// Entrypoint for subcommand
|
||||
|
@ -97,7 +109,7 @@ pub fn main(mut cmd_opts: SubcmdOptions) -> Result<(), i32> {
|
|||
}
|
||||
"csp" => {
|
||||
let file = File::open(cmd_opts.infile).expect("IO Error");
|
||||
election = parser::csp::parse_reader(file);
|
||||
election = parser::csp::parse_reader(file, cmd_opts.require_1, cmd_opts.require_sequential, cmd_opts.require_strict_order);
|
||||
}
|
||||
_ => unreachable!()
|
||||
};
|
||||
|
|
|
@ -25,7 +25,7 @@ use std::collections::HashMap;
|
|||
use std::io::Read;
|
||||
|
||||
/// Parse the given CSP file
|
||||
pub fn parse_reader<R: Read, N: Number>(reader: R) -> Election<N> {
|
||||
pub fn parse_reader<R: Read, N: Number>(reader: R, require_1: bool, require_sequential: bool, require_strict_order: bool) -> Election<N> {
|
||||
// Read CSV file
|
||||
let mut reader = ReaderBuilder::new()
|
||||
.has_headers(true)
|
||||
|
@ -86,13 +86,43 @@ pub fn parse_reader<R: Read, N: Number>(reader: R) -> Election<N> {
|
|||
let mut unique_rankings: Vec<usize> = preferences.iter().map(|(r, _)| *r).unique().collect();
|
||||
unique_rankings.sort();
|
||||
|
||||
if require_1 {
|
||||
if unique_rankings.first().map(|r| *r == 1).unwrap_or(false) == false {
|
||||
// No #1 preference
|
||||
ballots.push(Ballot {
|
||||
orig_value: value,
|
||||
preferences: vec![],
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let mut sorted_preferences = Vec::with_capacity(preferences.len());
|
||||
let mut last_ranking = None;
|
||||
for ranking in unique_rankings {
|
||||
// Filter for preferences at this ranking
|
||||
let prefs_this_ranking: Vec<usize> = preferences.iter()
|
||||
.filter_map(|(r, i)| if *r == ranking { Some(**i) } else { None })
|
||||
.collect();
|
||||
|
||||
if require_strict_order {
|
||||
if prefs_this_ranking.len() != 1 {
|
||||
// Duplicate rankings
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if require_sequential {
|
||||
if let Some(r) = last_ranking {
|
||||
if ranking != r + 1 {
|
||||
// Not sequential
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sorted_preferences.push(prefs_this_ranking);
|
||||
last_ranking = Some(ranking);
|
||||
}
|
||||
|
||||
ballots.push(Ballot {
|
||||
|
|
Loading…
Reference in New Issue