Complete BLT writer and implement tests for file conversions
This commit is contained in:
parent
e9e1c63c9c
commit
27ead09960
@ -29,19 +29,19 @@ use std::fs::File;
|
||||
#[clap(setting=AppSettings::DeriveDisplayOrder)]
|
||||
pub struct SubcmdOptions {
|
||||
/// Path to the input data file
|
||||
#[clap(help_heading=Some("INPUT/OUTPUT"))]
|
||||
#[clap(help_heading=Some("INPUT"))]
|
||||
infile: String,
|
||||
|
||||
/// Path to the output data file
|
||||
#[clap(help_heading=Some("INPUT/OUTPUT"))]
|
||||
outfile: String,
|
||||
|
||||
/// Format of input file
|
||||
#[clap(help_heading=Some("INPUT/OUTPUT"), short, long, possible_values=&["bin", "blt", "csp"], value_name="format")]
|
||||
#[clap(help_heading=Some("INPUT"), short, long, possible_values=&["bin", "blt", "csp"], value_name="format")]
|
||||
r#in: Option<String>,
|
||||
|
||||
/// Path to the output data file
|
||||
#[clap(help_heading=Some("OUTPUT"))]
|
||||
outfile: String,
|
||||
|
||||
/// Format of output file
|
||||
#[clap(help_heading=Some("INPUT/OUTPUT"), short, long, possible_values=&["bin", "blt", "csp"], value_name="format")]
|
||||
#[clap(help_heading=Some("OUTPUT"), short, long, possible_values=&["bin", "blt", "csp"], value_name="format")]
|
||||
out: Option<String>,
|
||||
|
||||
/// Number of seats
|
||||
|
@ -30,7 +30,7 @@ use std::fmt;
|
||||
use std::ops;
|
||||
|
||||
/// Constraints for an [crate::election::Election]
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), derive(Archive, Deserialize, Serialize))]
|
||||
pub struct Constraints(pub Vec<Constraint>);
|
||||
|
||||
@ -124,7 +124,7 @@ impl Constraints {
|
||||
}
|
||||
|
||||
/// A single dimension of constraint
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), derive(Archive, Deserialize, Serialize))]
|
||||
pub struct Constraint {
|
||||
/// Name of this constraint
|
||||
@ -134,7 +134,7 @@ pub struct Constraint {
|
||||
}
|
||||
|
||||
/// A group of candidates, of which a certain minimum and maximum must be elected
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), derive(Archive, Deserialize, Serialize))]
|
||||
pub struct ConstrainedGroup {
|
||||
/// Name of this group
|
||||
|
@ -28,6 +28,7 @@ use std::cmp::max;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// An election to be counted
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), derive(Archive, Deserialize, Serialize))]
|
||||
pub struct Election<N> {
|
||||
/// Name of the election
|
||||
@ -65,7 +66,7 @@ impl<N: Number> Election<N> {
|
||||
}
|
||||
|
||||
/// A candidate in an [Election]
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
#[derive(Clone, Eq, Hash, PartialEq)]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), derive(Archive, Deserialize, Serialize))]
|
||||
pub struct Candidate {
|
||||
/// Name of the candidate
|
||||
@ -368,6 +369,7 @@ pub struct Vote<'a, N> {
|
||||
}
|
||||
|
||||
/// A record of a voter's preferences
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(not(target_arch = "wasm32"), derive(Archive, Deserialize, Serialize))]
|
||||
pub struct Ballot<N> {
|
||||
/// Original value/weight of the ballot
|
||||
|
@ -26,6 +26,11 @@ use std::path::Path;
|
||||
/// Parse the given BIN file
|
||||
pub fn parse_path<P: AsRef<Path>, N: Number>(path: P) -> Election<N> {
|
||||
let content = fs::read(path).expect("IO Error");
|
||||
return parse_bytes(&content);
|
||||
}
|
||||
|
||||
/// Parse the given BIN file
|
||||
pub fn parse_bytes<N: Number>(content: &[u8]) -> Election<N> {
|
||||
let archived = unsafe {
|
||||
archived_root::<Election<N>>(&content)
|
||||
};
|
||||
|
@ -18,6 +18,8 @@
|
||||
use crate::election::Election;
|
||||
use crate::numbers::Number;
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use std::io::{BufWriter, Write};
|
||||
|
||||
/// Write the [Election] into BLT format
|
||||
@ -29,7 +31,8 @@ pub fn write<W: Write, N: Number>(election: Election<N>, output: W) {
|
||||
|
||||
// Write withdrawn candidates
|
||||
if !election.withdrawn_candidates.is_empty() {
|
||||
todo!();
|
||||
output.write(election.withdrawn_candidates.into_iter().map(|idx| format!("-{}", idx + 1)).join(" ").as_bytes()).expect("IO Error");
|
||||
output.write(b"\n").expect("IO Error");
|
||||
}
|
||||
|
||||
// Write ballots
|
||||
|
57
tests/convert.rs
Normal file
57
tests/convert.rs
Normal file
@ -0,0 +1,57 @@
|
||||
/* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use opentally::election::Election;
|
||||
use opentally::numbers::Rational;
|
||||
use opentally::parser;
|
||||
use opentally::writer;
|
||||
|
||||
use std::fs;
|
||||
|
||||
#[test]
|
||||
fn ers97_wd_conversions() {
|
||||
let blt_input = fs::read_to_string("tests/data/ers97_wd.blt").expect("IO Error");
|
||||
|
||||
let election: Election<Rational> = parser::blt::parse_path("tests/data/ers97_wd.blt").expect("Parse Error");
|
||||
|
||||
// Check BLT
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
writer::blt::write(election.clone(), &mut buf);
|
||||
assert_eq!(blt_input, std::str::from_utf8(&buf).expect("UTF-8 Error"));
|
||||
|
||||
// Check BIN
|
||||
buf.clear();
|
||||
writer::bin::write(election.clone(), &mut buf);
|
||||
let election2: Election<Rational> = parser::bin::parse_bytes(&buf);
|
||||
|
||||
buf.clear();
|
||||
writer::blt::write(election2, &mut buf);
|
||||
assert_eq!(blt_input, std::str::from_utf8(&buf).expect("UTF-8 Error"));
|
||||
|
||||
// Check CSP
|
||||
buf.clear();
|
||||
writer::csp::write(election.clone(), &mut buf);
|
||||
let mut election2: Election<Rational> = parser::csp::parse_reader(&buf[..]);
|
||||
|
||||
election2.seats = election.seats;
|
||||
election2.name = election.name;
|
||||
election2.withdrawn_candidates = election.withdrawn_candidates.clone();
|
||||
|
||||
buf.clear();
|
||||
writer::blt::write(election2, &mut buf);
|
||||
assert_eq!(blt_input, std::str::from_utf8(&buf).expect("UTF-8 Error"));
|
||||
}
|
69
tests/data/ers97_wd.blt
Normal file
69
tests/data/ers97_wd.blt
Normal file
@ -0,0 +1,69 @@
|
||||
11 6
|
||||
-1 -2
|
||||
8 1 2 0
|
||||
3 1 3 0
|
||||
14 1 4 0
|
||||
34 1 5 0
|
||||
1 1 6 0
|
||||
4 1 7 0
|
||||
1 1 8 0
|
||||
4 1 9 3 0
|
||||
3 1 9 4 0
|
||||
2 1 9 6 0
|
||||
3 1 9 7 0
|
||||
4 1 9 8 0
|
||||
9 1 9 0
|
||||
4 1 10 3 0
|
||||
5 1 10 4 0
|
||||
3 1 10 6 0
|
||||
3 1 10 7 0
|
||||
6 1 10 8 0
|
||||
10 1 10 0
|
||||
1 1 11 5 0
|
||||
1 1 11 0
|
||||
11 1 0
|
||||
105 2 0
|
||||
91 3 0
|
||||
90 4 0
|
||||
81 5 0
|
||||
64 6 0
|
||||
11 7 6 0
|
||||
36 7 8 0
|
||||
12 7 0
|
||||
55 8 0
|
||||
3 9 3 0
|
||||
2 9 4 0
|
||||
2 9 5 3 0
|
||||
1 9 5 4 0
|
||||
15 9 5 0
|
||||
2 9 6 0
|
||||
2 9 8 0
|
||||
2 10 3 0
|
||||
2 10 4 0
|
||||
2 10 5 6 0
|
||||
1 10 5 7 6 0
|
||||
2 10 5 8 0
|
||||
11 10 5 0
|
||||
1 10 6 0
|
||||
1 10 7 4 0
|
||||
1 10 8 0
|
||||
1 10 0
|
||||
2 11 2 0
|
||||
4 11 3 0
|
||||
1 11 4 0
|
||||
5 11 7 8 0
|
||||
10 11 8 0
|
||||
1 11 0
|
||||
0
|
||||
"Smith"
|
||||
"Duke"
|
||||
"Prince"
|
||||
"Freeman"
|
||||
"Carpenter"
|
||||
"Baron"
|
||||
"Abbot"
|
||||
"Vicar"
|
||||
"Wright"
|
||||
"Glazier"
|
||||
"Monk"
|
||||
"ERS Model - 1997 Edition"
|
Loading…
Reference in New Issue
Block a user