diff --git a/html/index.html b/html/index.html
index 60d7f84..ecea932 100644
--- a/html/index.html
+++ b/html/index.html
@@ -40,6 +40,7 @@
+
@@ -92,10 +93,11 @@
diff --git a/html/index.js b/html/index.js
index 8c797d6..c84a18a 100644
--- a/html/index.js
+++ b/html/index.js
@@ -50,11 +50,21 @@ function changePreset() {
document.getElementById('selQuotaCriterion').value = 'geq';
document.getElementById('selQuota').value = 'droop';
document.getElementById('chkProgQuota').checked = false;
- document.getElementById('chkBulkExclusion').checked = false;
+ document.getElementById('chkBulkExclusion').checked = true;
document.getElementById('selNumbers').value = 'int';
document.getElementById('selSurplus').value = 'order';
document.getElementById('selTransfers').value = 'uig';
document.getElementById('selTies').value = 'backwards_random';
+ } else if (document.getElementById('selPreset').value === 'wright') {
+ document.getElementById('selQuotaCriterion').value = 'geq';
+ document.getElementById('selQuota').value = 'droop';
+ document.getElementById('chkProgQuota').checked = false;
+ document.getElementById('chkBulkExclusion').checked = true;
+ document.getElementById('selNumbers').value = 'fixed';
+ document.getElementById('txtDP').value = '5';
+ document.getElementById('selSurplus').value = 'size';
+ document.getElementById('selTransfers').value = 'wright';
+ document.getElementById('selTies').value = 'backwards_random';
}
}
diff --git a/html/worker.js b/html/worker.js
index 7c688aa..ea335de 100644
--- a/html/worker.js
+++ b/html/worker.js
@@ -41,10 +41,12 @@ onmessage = function(evt) {
// Create counter
let counter;
- if (evt.data.transfers === 'wig') {
- counter = py.pyRCV2.method.base_stv.BaseWIGSTVCounter(election, evt.data.options);
- } else {
+ if (evt.data.transfers === 'uig') {
counter = py.pyRCV2.method.base_stv.BaseUIGSTVCounter(election, evt.data.options);
+ } else if (evt.data.transfers === 'wright') {
+ counter = py.pyRCV2.method.wright.WrightSTVCounter(election, evt.data.options);
+ } else {
+ counter = py.pyRCV2.method.base_stv.BaseWIGSTVCounter(election, evt.data.options);
}
// Reset
diff --git a/pyRCV2/method/base_stv.py b/pyRCV2/method/base_stv.py
index ccf5fae..1e69150 100644
--- a/pyRCV2/method/base_stv.py
+++ b/pyRCV2/method/base_stv.py
@@ -192,11 +192,7 @@ class BaseSTVCounter:
Exclude the lowest ranked hopeful
"""
- hopefuls = [(c, cc) for c, cc in self.candidates.items() if cc.state == CandidateState.HOPEFUL]
- hopefuls.sort(lambda x: x[1].votes)
-
- # TODO: Handle ties
- candidate_excluded, count_card = hopefuls[0]
+ candidate_excluded, count_card = self.candidate_to_exclude()
count_card.state = CandidateState.EXCLUDED
# Exclude this candidate
@@ -221,6 +217,19 @@ class BaseSTVCounter:
)
__pragma__('noopov')
+ def candidate_to_exclude(self):
+ """
+ Determine the candidate to exclude
+ """
+
+ hopefuls = [(c, cc) for c, cc in self.candidates.items() if cc.state == CandidateState.HOPEFUL]
+ hopefuls.sort(lambda x: x[1].votes)
+
+ # TODO: Handle ties
+ candidate_excluded, count_card = hopefuls[0]
+
+ return candidate_excluded, count_card
+
def do_exclusion(self, candidate_excluded, count_card):
"""
Exclude the given candidate and transfer the votes
diff --git a/pyRCV2/method/wright.py b/pyRCV2/method/wright.py
new file mode 100644
index 0000000..c3ef75a
--- /dev/null
+++ b/pyRCV2/method/wright.py
@@ -0,0 +1,56 @@
+# pyRCV2: Preferential vote counting
+# Copyright © 2020 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 .
+
+from pyRCV2.method.base_stv import BaseWIGSTVCounter
+from pyRCV2.model import CandidateState, CountCard
+from pyRCV2.safedict import SafeDict
+
+class WrightSTVCounter(BaseWIGSTVCounter):
+ """
+ Wright STV implementation
+ """
+
+ def exclude_candidate(self):
+ """
+ Overrides BaseSTVCounter.exclude_candidate per Wright STV
+ """
+
+ candidate_excluded, count_card = self.candidate_to_exclude()
+ count_card.state = CandidateState.EXCLUDED
+
+ # Reset the count
+ new_candidates = SafeDict()
+ for candidate, count_card in self.candidates.items():
+ new_count_card = CountCard()
+
+ if count_card.state == CandidateState.WITHDRAWN:
+ new_count_card.state = CandidateState.WITHDRAWN
+ elif count_card.state == CandidateState.EXCLUDED:
+ new_count_card.state = CandidateState.EXCLUDED
+
+ __pragma__('opov')
+ new_candidates[candidate] = new_count_card
+ __pragma__('noopov')
+
+ self.candidates = new_candidates
+ self.exhausted = CountCard()
+ self.loss_fraction = CountCard()
+ self.num_elected = 0
+
+ result = self.reset()
+ result.comment = 'Exclusion of ' + candidate_excluded.name
+
+ return result
diff --git a/pyRCV2/transcrypt.py b/pyRCV2/transcrypt.py
index 20795f0..aa09038 100644
--- a/pyRCV2/transcrypt.py
+++ b/pyRCV2/transcrypt.py
@@ -16,7 +16,7 @@
import pyRCV2.blt
import pyRCV2.model
-import pyRCV2.method, pyRCV2.method.base_stv
+import pyRCV2.method, pyRCV2.method.base_stv, pyRCV2.method.wright
import pyRCV2.numbers
__pragma__('js', '{}', 'export {pyRCV2, isinstance, repr, str};')