diff --git a/html/index.js b/html/index.js
index 33129fd..d36c505 100644
--- a/html/index.js
+++ b/html/index.js
@@ -130,6 +130,7 @@ async function clickCount() {
document.getElementById('chkBulkExclusion').checked,
document.getElementById('chkDeferSurpluses').checked,
document.getElementById('chkMeekImmediateElect').checked,
+ conPath,
"guard_doom",
parseInt(document.getElementById('txtPPDP').value),
];
diff --git a/src/constraints.rs b/src/constraints.rs
index 93f48aa..49e7432 100644
--- a/src/constraints.rs
+++ b/src/constraints.rs
@@ -201,7 +201,7 @@ impl ConstraintMatrix {
// NB: Bounds on min, max, etc. will be further refined in initial step() calls
}
- /// Update cands/elected in innermost cells based on the provided [CountState::candidates]
+ /// Update cands/elected in innermost cells based on the provided [CountState::candidates](crate::election::CountState::candidates)
pub fn update_from_state(&mut self, election: &Election, candidates: &HashMap<&Candidate, CountCard>) {
let constraints = election.constraints.as_ref().unwrap();
@@ -239,7 +239,7 @@ impl ConstraintMatrix {
}
}
- /// Recompute [self::cands] and [self::elected] for totals cells based on the innermost cells
+ /// Recompute [cands](ConstraintMatrixCell::cands) and [elected](ConstraintMatrixCell::elected) for totals cells based on the innermost cells
pub fn recount_cands(&mut self) {
let shape = Vec::from(self.0.shape());
diff --git a/src/main.rs b/src/main.rs
index 6cabb3c..89242c3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -256,6 +256,7 @@ where
cmd_opts.bulk_exclude,
cmd_opts.defer_surpluses,
cmd_opts.meek_immediate_elect,
+ cmd_opts.constraints.as_deref(),
&cmd_opts.constraint_mode,
cmd_opts.pp_decimals,
);
diff --git a/src/stv/mod.rs b/src/stv/mod.rs
index 0a5f2fe..1971f6b 100644
--- a/src/stv/mod.rs
+++ b/src/stv/mod.rs
@@ -52,7 +52,7 @@ pub struct STVOptions {
pub sum_surplus_transfers: SumSurplusTransfersMode,
/// (Meek STV) Limit for stopping iteration of surplus distribution
pub meek_surplus_tolerance: String,
- /// Convert ballots with value >1 to multiple ballots of value 1
+ /// Convert ballots with value >1 to multiple ballots of value 1 (used only for [STVOptions::describe])
pub normalise_ballots: bool,
/// Quota type
pub quota: QuotaType,
@@ -80,6 +80,8 @@ pub struct STVOptions {
pub defer_surpluses: bool,
/// (Meek STV) Immediately elect candidates even if keep values have not converged
pub meek_immediate_elect: bool,
+ /// Path to constraints file (used only for [STVOptions::describe])
+ pub constraints_path: Option,
/// Mode of handling constraints
pub constraint_mode: ConstraintMode,
/// Print votes to specified decimal places in results report
@@ -110,6 +112,7 @@ impl STVOptions {
bulk_exclude: bool,
defer_surpluses: bool,
meek_immediate_elect: bool,
+ constraints_path: Option<&str>,
constraint_mode: &str,
pp_decimals: usize,
) -> Self {
@@ -175,6 +178,10 @@ impl STVOptions {
bulk_exclude,
defer_surpluses,
meek_immediate_elect,
+ constraints_path: match constraints_path {
+ Some(p) => Some(p.to_string()),
+ None => None,
+ },
constraint_mode: match constraint_mode {
"guard_doom" => ConstraintMode::GuardDoom,
"rollback" => ConstraintMode::Rollback,
@@ -210,6 +217,10 @@ impl STVOptions {
if self.bulk_exclude { flags.push("--bulk-exclude".to_string()); }
if self.defer_surpluses { flags.push("--defer-surpluses".to_string()); }
if self.surplus == SurplusMethod::Meek && self.meek_immediate_elect { flags.push("--meek-immediate-elect".to_string()); }
+ if let Some(path) = &self.constraints_path {
+ flags.push(format!("--constraints {}", path));
+ if self.constraint_mode != ConstraintMode::GuardDoom { flags.push(self.constraint_mode.describe()); }
+ }
if self.pp_decimals != 2 { flags.push(format!("--pp-decimals {}", self.pp_decimals)); }
return flags.join(" ");
}
@@ -392,6 +403,8 @@ impl ExclusionMethod {
}
/// Enum of options for [STVOptions::constraint_mode]
+#[derive(Clone, Copy)]
+#[derive(PartialEq)]
pub enum ConstraintMode {
/// Guard or doom candidates as soon as required to secure a conformant result
GuardDoom,
@@ -399,6 +412,16 @@ pub enum ConstraintMode {
Rollback,
}
+impl ConstraintMode {
+ /// Convert to CLI argument representation
+ fn describe(self) -> String {
+ match self {
+ ConstraintMode::GuardDoom => "--constraint-mode guard_doom",
+ ConstraintMode::Rollback => "--constraint-mode rollback",
+ }.to_string()
+ }
+}
+
/// An error during the STV count
#[wasm_bindgen]
#[derive(Debug)]
diff --git a/src/stv/wasm.rs b/src/stv/wasm.rs
index a7ef5d4..859beb8 100644
--- a/src/stv/wasm.rs
+++ b/src/stv/wasm.rs
@@ -212,6 +212,7 @@ impl STVOptions {
bulk_exclude: bool,
defer_surpluses: bool,
meek_immediate_elect: bool,
+ constraints_path: Option,
constraint_mode: &str,
pp_decimals: usize,
) -> Self {
@@ -237,6 +238,7 @@ impl STVOptions {
bulk_exclude,
defer_surpluses,
meek_immediate_elect,
+ constraints_path.as_deref(),
constraint_mode,
pp_decimals,
))
diff --git a/tests/aec.rs b/tests/aec.rs
index 12f7c96..dade895 100644
--- a/tests/aec.rs
+++ b/tests/aec.rs
@@ -75,6 +75,7 @@ fn aec_tas19_rational() {
bulk_exclude: true,
defer_surpluses: false,
meek_immediate_elect: false,
+ constraints_path: None,
constraint_mode: stv::ConstraintMode::GuardDoom,
pp_decimals: 2,
};
diff --git a/tests/csm.rs b/tests/csm.rs
index 1cafde9..cbe0143 100644
--- a/tests/csm.rs
+++ b/tests/csm.rs
@@ -43,6 +43,7 @@ fn csm15_float64() {
bulk_exclude: true,
defer_surpluses: false,
meek_immediate_elect: false,
+ constraints_path: None,
constraint_mode: stv::ConstraintMode::GuardDoom,
pp_decimals: 2,
};
diff --git a/tests/ers97.rs b/tests/ers97.rs
index 1f10cc7..e5c6913 100644
--- a/tests/ers97.rs
+++ b/tests/ers97.rs
@@ -43,6 +43,7 @@ fn ers97_rational() {
bulk_exclude: true,
defer_surpluses: true,
meek_immediate_elect: false,
+ constraints_path: None,
constraint_mode: stv::ConstraintMode::GuardDoom,
pp_decimals: 2,
};
diff --git a/tests/meek.rs b/tests/meek.rs
index aeca4be..083db3a 100644
--- a/tests/meek.rs
+++ b/tests/meek.rs
@@ -48,6 +48,7 @@ fn meek87_ers97_float64() {
bulk_exclude: false,
defer_surpluses: false,
meek_immediate_elect: false,
+ constraints_path: None,
constraint_mode: stv::ConstraintMode::GuardDoom,
pp_decimals: 2,
};
@@ -78,6 +79,7 @@ fn meek06_ers97_fixed12() {
bulk_exclude: false,
defer_surpluses: true,
meek_immediate_elect: true,
+ constraints_path: None,
constraint_mode: stv::ConstraintMode::GuardDoom,
pp_decimals: 2,
};
@@ -153,6 +155,7 @@ fn meeknz_ers97_fixed12() {
bulk_exclude: false,
defer_surpluses: true,
meek_immediate_elect: true,
+ constraints_path: None,
constraint_mode: stv::ConstraintMode::GuardDoom,
pp_decimals: 2,
};
diff --git a/tests/prsa.rs b/tests/prsa.rs
index 868a684..e9a861b 100644
--- a/tests/prsa.rs
+++ b/tests/prsa.rs
@@ -43,6 +43,7 @@ fn prsa1_rational() {
bulk_exclude: false,
defer_surpluses: false,
meek_immediate_elect: false,
+ constraints_path: None,
constraint_mode: stv::ConstraintMode::GuardDoom,
pp_decimals: 2,
};
diff --git a/tests/scotland.rs b/tests/scotland.rs
index bb10703..1542c56 100644
--- a/tests/scotland.rs
+++ b/tests/scotland.rs
@@ -50,6 +50,7 @@ fn scotland_linn07_fixed5() {
bulk_exclude: false,
defer_surpluses: false,
meek_immediate_elect: false,
+ constraints_path: None,
constraint_mode: stv::ConstraintMode::GuardDoom,
pp_decimals: 5,
};
@@ -80,6 +81,7 @@ fn scotland_linn07_gfixed5() {
bulk_exclude: false,
defer_surpluses: false,
meek_immediate_elect: false,
+ constraints_path: None,
constraint_mode: stv::ConstraintMode::GuardDoom,
pp_decimals: 5,
};