diff --git a/html/index.js b/html/index.js
index bdb2549..604e944 100644
--- a/html/index.js
+++ b/html/index.js
@@ -103,6 +103,7 @@ async function clickCount() {
document.getElementById('chkRoundVotes').checked ? parseInt(document.getElementById('txtRoundVotes').value) : null,
document.getElementById('chkRoundQuota').checked ? parseInt(document.getElementById('txtRoundQuota').value) : null,
document.getElementById('selSumTransfers').value,
+ document.getElementById('chkNormaliseBallots').checked,
document.getElementById('selQuota').value,
document.getElementById('selQuotaCriterion').value,
document.getElementById('selQuotaMode').value,
diff --git a/src/main.rs b/src/main.rs
index 90a1048..c70dbf6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -181,6 +181,7 @@ where
cmd_opts.round_votes,
cmd_opts.round_quota,
&cmd_opts.sum_surplus_transfers,
+ cmd_opts.normalise_ballots,
&cmd_opts.quota,
&cmd_opts.quota_criterion,
&cmd_opts.quota_mode,
@@ -193,6 +194,17 @@ where
cmd_opts.pp_decimals,
);
+ // Describe count
+ let total_ballots = election.ballots.iter().fold(N::zero(), |acc, b| { acc + &b.orig_value });
+ print!("Count computed by OpenTally (revision {}). Read {:.0} ballots from \"{}\" for election \"{}\". There are {} candidates for {} vacancies. ", opentally::VERSION, total_ballots, cmd_opts.filename, election.name, election.candidates.len(), election.seats);
+ let opts_str = stv_opts.describe::();
+ if opts_str.len() > 0 {
+ println!("Counting using options \"{}\".", opts_str);
+ } else {
+ println!("Counting using default options.");
+ }
+ println!();
+
// Normalise ballots if requested
if cmd_opts.normalise_ballots {
election.normalise_ballots();
diff --git a/src/numbers/mod.rs b/src/numbers/mod.rs
index 2842809..ba87026 100644
--- a/src/numbers/mod.rs
+++ b/src/numbers/mod.rs
@@ -47,6 +47,7 @@ where
fn new() -> Self;
fn describe() -> String;
+ fn describe_opt() -> String { Self::describe() }
fn pow_assign(&mut self, exponent: i32);
fn floor_mut(&mut self, dps: usize);
diff --git a/src/numbers/rational_num.rs b/src/numbers/rational_num.rs
index a7b4e5d..95e4638 100644
--- a/src/numbers/rational_num.rs
+++ b/src/numbers/rational_num.rs
@@ -33,6 +33,7 @@ impl Number for Rational {
fn new() -> Self { Self(RatioType::zero()) }
fn describe() -> String { "--numbers rational".to_string() }
+ fn describe_opt() -> String { String::new() }
fn pow_assign(&mut self, exponent: i32) {
self.0 = self.0.pow(exponent);
diff --git a/src/numbers/rational_rug.rs b/src/numbers/rational_rug.rs
index 9cfa977..d59570c 100644
--- a/src/numbers/rational_rug.rs
+++ b/src/numbers/rational_rug.rs
@@ -32,6 +32,7 @@ impl Number for Rational {
fn new() -> Self { Self(rug::Rational::new()) }
fn describe() -> String { "--numbers rational".to_string() }
+ fn describe_opt() -> String { String::new() }
fn pow_assign(&mut self, exponent: i32) {
self.0.pow_assign(exponent);
diff --git a/src/stv/mod.rs b/src/stv/mod.rs
index 668669b..4a0a270 100644
--- a/src/stv/mod.rs
+++ b/src/stv/mod.rs
@@ -38,6 +38,7 @@ pub struct STVOptions {
pub round_votes: Option,
pub round_quota: Option,
pub sum_surplus_transfers: SumSurplusTransfersMode,
+ pub normalise_ballots: bool,
pub quota: QuotaType,
pub quota_criterion: QuotaCriterion,
pub quota_mode: QuotaMode,
@@ -57,7 +58,8 @@ impl STVOptions {
round_weights: Option,
round_votes: Option,
round_quota: Option,
- sum_transfers: &str,
+ sum_surplus_transfers: &str,
+ normalise_ballots: bool,
quota: &str,
quota_criterion: &str,
quota_mode: &str,
@@ -74,12 +76,13 @@ impl STVOptions {
round_weights,
round_votes,
round_quota,
- sum_surplus_transfers: match sum_transfers {
+ sum_surplus_transfers: match sum_surplus_transfers {
"single_step" => SumSurplusTransfersMode::SingleStep,
"by_value" => SumSurplusTransfersMode::ByValue,
"per_ballot" => SumSurplusTransfersMode::PerBallot,
_ => panic!("Invalid --sum-transfers"),
},
+ normalise_ballots,
quota: match quota {
"droop" => QuotaType::Droop,
"hare" => QuotaType::Hare,
@@ -127,11 +130,13 @@ impl STVOptions {
// Not exported to WebAssembly
pub fn describe(&self) -> String {
let mut flags = Vec::new();
- flags.push(N::describe());
+ let n_str = N::describe_opt(); if n_str.len() > 0 { flags.push(N::describe_opt()) };
if let Some(dps) = self.round_tvs { flags.push(format!("--round-tvs {}", dps)); }
if let Some(dps) = self.round_weights { flags.push(format!("--round-weights {}", dps)); }
if let Some(dps) = self.round_votes { flags.push(format!("--round-votes {}", dps)); }
if let Some(dps) = self.round_quota { flags.push(format!("--round-quota {}", dps)); }
+ if self.sum_surplus_transfers != SumSurplusTransfersMode::SingleStep { flags.push(self.sum_surplus_transfers.describe()); }
+ if self.normalise_ballots { flags.push("--normalise-ballots".to_string()); }
if self.quota != QuotaType::DroopExact { flags.push(self.quota.describe()); }
if self.quota_criterion != QuotaCriterion::Greater { flags.push(self.quota_criterion.describe()); }
if self.quota_mode != QuotaMode::Static { flags.push(self.quota_mode.describe()); }
@@ -139,6 +144,8 @@ impl STVOptions {
if self.surplus_order != SurplusOrder::BySize { flags.push(self.surplus_order.describe()); }
if self.transferable_only { flags.push("--transferable-only".to_string()); }
if self.exclusion != ExclusionMethod::SingleStage { flags.push(self.exclusion.describe()); }
+ if self.bulk_exclude { flags.push("--bulk-exclude".to_string()); }
+ if self.defer_surpluses { flags.push("--defer-surpluses".to_string()); }
if self.pp_decimals != 2 { flags.push(format!("--pp-decimals {}", self.pp_decimals)); }
return flags.join(" ");
}
@@ -153,6 +160,16 @@ pub enum SumSurplusTransfersMode {
PerBallot,
}
+impl SumSurplusTransfersMode {
+ fn describe(self) -> String {
+ match self {
+ SumSurplusTransfersMode::SingleStep => "--sum-surplus-transfers single_step",
+ SumSurplusTransfersMode::ByValue => "--sum-surplus-transfers by_value",
+ SumSurplusTransfersMode::PerBallot => "--sum-surplus-transfers per_ballot",
+ }.to_string()
+ }
+}
+
#[wasm_bindgen]
#[derive(Clone, Copy)]
#[derive(PartialEq)]
diff --git a/src/stv/wasm.rs b/src/stv/wasm.rs
index d9213db..3c2aa80 100644
--- a/src/stv/wasm.rs
+++ b/src/stv/wasm.rs
@@ -157,7 +157,15 @@ fn describe_count(filename: String, election: &Election, opts: &st
let mut result = String::from("Count computed by OpenTally (revision ");
result.push_str(crate::VERSION);
let total_ballots = election.ballots.iter().fold(N::zero(), |acc, b| { acc + &b.orig_value });
- result.push_str(&format!(r#"). Read {:.0} ballots from ‘{}’ for election ‘{}’. There are {} candidates for {} vacancies. Counting using options {}.
"#, total_ballots, filename, election.name, election.candidates.len(), election.seats, opts.describe::()));
+ result.push_str(&format!(r#"). Read {:.0} ballots from ‘{}’ for election ‘{}’. There are {} candidates for {} vacancies. "#, total_ballots, filename, election.name, election.candidates.len(), election.seats));
+
+ let opts_str = opts.describe::();
+ if opts_str.len() > 0 {
+ result.push_str(&format!(r#"Counting using options {}.
"#, opts_str))
+ } else {
+ result.push_str(r#"Counting using default options."#);
+ }
+
return result;
}