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
|
/// Number of seats
|
||||||
#[clap(help_heading=Some("ELECTION SPECIFICATION"), long)]
|
#[clap(help_heading=Some("ELECTION SPECIFICATION"), long)]
|
||||||
seats: Option<usize>,
|
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
|
/// Entrypoint for subcommand
|
||||||
@ -97,7 +109,7 @@ pub fn main(mut cmd_opts: SubcmdOptions) -> Result<(), i32> {
|
|||||||
}
|
}
|
||||||
"csp" => {
|
"csp" => {
|
||||||
let file = File::open(cmd_opts.infile).expect("IO Error");
|
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!()
|
_ => unreachable!()
|
||||||
};
|
};
|
||||||
|
@ -25,7 +25,7 @@ use std::collections::HashMap;
|
|||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
/// Parse the given CSP file
|
/// 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
|
// Read CSV file
|
||||||
let mut reader = ReaderBuilder::new()
|
let mut reader = ReaderBuilder::new()
|
||||||
.has_headers(true)
|
.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();
|
let mut unique_rankings: Vec<usize> = preferences.iter().map(|(r, _)| *r).unique().collect();
|
||||||
unique_rankings.sort();
|
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 sorted_preferences = Vec::with_capacity(preferences.len());
|
||||||
|
let mut last_ranking = None;
|
||||||
for ranking in unique_rankings {
|
for ranking in unique_rankings {
|
||||||
// Filter for preferences at this ranking
|
// Filter for preferences at this ranking
|
||||||
let prefs_this_ranking: Vec<usize> = preferences.iter()
|
let prefs_this_ranking: Vec<usize> = preferences.iter()
|
||||||
.filter_map(|(r, i)| if *r == ranking { Some(**i) } else { None })
|
.filter_map(|(r, i)| if *r == ranking { Some(**i) } else { None })
|
||||||
.collect();
|
.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);
|
sorted_preferences.push(prefs_this_ranking);
|
||||||
|
last_ranking = Some(ranking);
|
||||||
}
|
}
|
||||||
|
|
||||||
ballots.push(Ballot {
|
ballots.push(Ballot {
|
||||||
|
Loading…
Reference in New Issue
Block a user