diff --git a/html/index.js b/html/index.js
index 87f14c6..bdb2549 100644
--- a/html/index.js
+++ b/html/index.js
@@ -70,6 +70,13 @@ worker.onmessage = function(evt) {
} else if (evt.data.type === 'finalResultSummary') {
divLogs2.insertAdjacentHTML('beforeend', evt.data.summary);
document.getElementById('printPane').style.display = 'block';
+
+ } else if (evt.data.type === 'requireInput') {
+ let response = window.prompt(evt.data.message);
+ while (response === null) {
+ response = window.prompt(evt.data.message);
+ }
+ worker.postMessage({'type': 'userInput', 'response': response});
}
}
diff --git a/html/worker.js b/html/worker.js
index f766283..67a7888 100644
--- a/html/worker.js
+++ b/html/worker.js
@@ -8,9 +8,10 @@ async function initWasm() {
}
initWasm();
+var numbers, election, opts, state, stageNum;
+
onmessage = function(evt) {
if (evt.data.type === 'countElection') {
- let numbers;
if (evt.data.numbers === 'fixed') {
numbers = 'Fixed';
wasm.fixed_set_dps(evt.data.decimals);
@@ -23,14 +24,14 @@ onmessage = function(evt) {
}
// Init election
- let election = wasm['election_from_blt_' + numbers](evt.data.electionData);
+ election = wasm['election_from_blt_' + numbers](evt.data.electionData);
if (evt.data.normaliseBallots) {
wasm['election_normalise_ballots_' + numbers](election);
}
// Init STV options
- let opts = wasm.STVOptions.new.apply(null, evt.data.optsStr);
+ opts = wasm.STVOptions.new.apply(null, evt.data.optsStr);
// Describe count
postMessage({'type': 'describeCount', 'content': wasm['describe_count_' + numbers](evt.data.filePath, election, opts)});
@@ -39,23 +40,54 @@ onmessage = function(evt) {
postMessage({'type': 'initResultsTable', 'content': wasm['init_results_table_' + numbers](election, opts)});
// Step election
- let state = wasm['CountState' + numbers].new(election);
+ state = wasm['CountState' + numbers].new(election);
wasm['count_init_' + numbers](state, opts);
postMessage({'type': 'updateResultsTable', 'result': wasm['update_results_table_' + numbers](1, state, opts)});
postMessage({'type': 'updateStageComments', 'comment': wasm['update_stage_comments_' + numbers](state)});
- for (let stageNum = 2;; stageNum++) {
+ stageNum = 2;
+
+ resume_count();
+
+ } else if (evt.data.type == 'userInput') {
+ user_input_buffer = evt.data.response;
+ resume_count();
+ }
+}
+
+function resume_count() {
+ for (;; stageNum++) {
+ try {
let isDone = wasm['count_one_stage_' + numbers](state, opts);
if (isDone) {
break;
}
-
- postMessage({'type': 'updateResultsTable', 'result': wasm['update_results_table_' + numbers](stageNum, state, opts)});
- postMessage({'type': 'updateStageComments', 'comment': wasm['update_stage_comments_' + numbers](state)});
+ } catch (ex) {
+ if (ex === "RequireInput") {
+ return;
+ } else {
+ throw ex;
+ }
}
- postMessage({'type': 'updateResultsTable', 'result': wasm['finalise_results_table_' + numbers](state)});
- postMessage({'type': 'finalResultSummary', 'summary': wasm['final_result_summary_' + numbers](state)});
+ postMessage({'type': 'updateResultsTable', 'result': wasm['update_results_table_' + numbers](stageNum, state, opts)});
+ postMessage({'type': 'updateStageComments', 'comment': wasm['update_stage_comments_' + numbers](state)});
+ }
+
+ postMessage({'type': 'updateResultsTable', 'result': wasm['finalise_results_table_' + numbers](state)});
+ postMessage({'type': 'finalResultSummary', 'summary': wasm['final_result_summary_' + numbers](state)});
+}
+
+var user_input_buffer = null;
+
+function read_user_input_buffer(message) {
+ if (user_input_buffer === null) {
+ postMessage({'type': 'requireInput', 'message': message});
+ return null;
+ } else {
+ let user_input = user_input_buffer;
+ user_input_buffer = null;
+ return user_input;
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 0d2bd97..efda35e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -19,6 +19,7 @@ pub mod election;
pub mod logger;
pub mod numbers;
pub mod stv;
+pub mod ties;
use git_version::git_version;
use wasm_bindgen::prelude::wasm_bindgen;
diff --git a/src/main.rs b/src/main.rs
index 187591e..90a1048 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -208,7 +208,7 @@ where
loop {
let is_done = stv::count_one_stage(&mut state, &stv_opts);
- if is_done {
+ if is_done.unwrap() {
break;
}
stage_num += 1;
diff --git a/src/stv/mod.rs b/src/stv/mod.rs
index cf3c42d..5335347 100644
--- a/src/stv/mod.rs
+++ b/src/stv/mod.rs
@@ -22,6 +22,7 @@ pub mod wasm;
use crate::numbers::Number;
use crate::election::{Candidate, CandidateState, CountCard, CountState, Parcel, Vote};
+use crate::ties::TieStrategy;
use itertools::Itertools;
use wasm_bindgen::prelude::wasm_bindgen;
@@ -264,13 +265,19 @@ impl ExclusionMethod {
}
}
+#[wasm_bindgen]
+#[derive(Debug)]
+pub enum STVError {
+ RequireInput,
+}
+
pub fn count_init(mut state: &mut CountState<'_, N>, opts: &STVOptions) {
distribute_first_preferences(&mut state);
calculate_quota(&mut state, opts);
elect_meeting_quota(&mut state, opts);
}
-pub fn count_one_stage<'a, N: Number>(mut state: &mut CountState<'a, N>, opts: &STVOptions) -> bool
+pub fn count_one_stage<'a, N: Number>(mut state: &mut CountState<'a, N>, opts: &STVOptions) -> Result
where
for<'r> &'r N: ops::Sub<&'r N, Output=N>,
for<'r> &'r N: ops::Div<&'r N, Output=N>,
@@ -281,36 +288,36 @@ where
// Finish count
if finished_before_stage(&state) {
- return true;
+ return Ok(true);
}
// Continue exclusions
if continue_exclusion(&mut state, &opts) {
calculate_quota(&mut state, opts);
elect_meeting_quota(&mut state, opts);
- return false;
+ return Ok(false);
}
// Distribute surpluses
- if distribute_surpluses(&mut state, &opts) {
+ if distribute_surpluses(&mut state, &opts)? {
calculate_quota(&mut state, opts);
elect_meeting_quota(&mut state, opts);
- return false;
+ return Ok(false);
}
// Attempt bulk election
if bulk_elect(&mut state) {
- return false;
+ return Ok(false);
}
// Exclude lowest hopeful
- if exclude_hopefuls(&mut state, &opts) {
+ if exclude_hopefuls(&mut state, &opts)? {
calculate_quota(&mut state, opts);
elect_meeting_quota(&mut state, opts);
- return false;
+ return Ok(false);
}
- todo!();
+ panic!("Count incomplete but unable to proceed");
}
struct NextPreferencesResult<'a, N> {
@@ -596,14 +603,15 @@ where
return true;
}
-fn distribute_surpluses(state: &mut CountState, opts: &STVOptions) -> bool
+fn distribute_surpluses(state: &mut CountState, opts: &STVOptions) -> Result
where
for<'r> &'r N: ops::Sub<&'r N, Output=N>,
for<'r> &'r N: ops::Div<&'r N, Output=N>,
for<'r> &'r N: ops::Neg