diff --git a/pyRCV2/method/STVCCounter.py b/pyRCV2/method/STVCCounter.py
index 7c141f3..6ebf713 100644
--- a/pyRCV2/method/STVCCounter.py
+++ b/pyRCV2/method/STVCCounter.py
@@ -102,14 +102,13 @@ class STVCCounter:
__pragma__('opov')
surplus = count_card.votes - self.quota
- transfer_value = surplus / count_card.votes
- count_card.transfers -= surplus
+ #transfer_value = surplus / count_card.votes # Do not do this yet to avoid rounding errors
__pragma__('noopov')
# Transfer surplus
for ballot, ballot_value in count_card.ballots:
__pragma__('opov')
- new_value = ballot_value * transfer_value
+ new_value = (ballot_value * surplus) / count_card.votes
candidate = next((c for c in ballot.preferences if self.candidates[c].state == CandidateState.HOPEFUL), None)
if candidate is not None:
@@ -120,6 +119,10 @@ class STVCCounter:
self.exhausted.ballots.append((ballot, new_value))
__pragma__('noopov')
+ __pragma__('opov')
+ count_card.transfers -= surplus
+ __pragma__('noopov')
+
# Declare any candidates meeting the quota as a result of surpluses
self.compute_quota()
self.elect_meeting_quota()
diff --git a/pyRCV2/numbers/__init__.py b/pyRCV2/numbers/__init__.py
index 05ad195..e9a8daf 100644
--- a/pyRCV2/numbers/__init__.py
+++ b/pyRCV2/numbers/__init__.py
@@ -22,10 +22,12 @@ __pragma__('noskip')
if is_py:
__pragma__('skip')
+ from pyRCV2.numbers.fixed_py import Fixed, set_dps
from pyRCV2.numbers.native_py import Native
from pyRCV2.numbers.rational_py import Rational
__pragma__('noskip')
else:
+ from pyRCV2.numbers.fixed_js import Fixed, set_dps
from pyRCV2.numbers.native_js import Native
from pyRCV2.numbers.rational_js import Rational
diff --git a/pyRCV2/numbers/fixed_js.py b/pyRCV2/numbers/fixed_js.py
new file mode 100644
index 0000000..77e11ca
--- /dev/null
+++ b/pyRCV2/numbers/fixed_js.py
@@ -0,0 +1,50 @@
+# 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 .
+
+Big.DP = 6
+
+def set_dps(dps):
+ Big.DP = dps
+
+class Fixed:
+ """
+ Wrapper for big.js (fixed-point arithmetic)
+ """
+
+ def __init__(self, val):
+ self.impl = Big(val)
+
+ def pp(self, dp):
+ """Pretty print to specified number of decimal places"""
+ return self.impl.toFixed(dp)
+
+ def __add__(self, other):
+ return Fixed(self.impl.plus(other.impl))
+ def __sub__(self, other):
+ return Fixed(self.impl.minus(other.impl))
+ def __mul__(self, other):
+ return Fixed(self.impl.times(other.impl))
+ def __div__(self, other):
+ return Fixed(self.impl.div(other.impl))
+
+ def __gt__(self, other):
+ return self.impl.gt(other.impl)
+ def __ge__(self, other):
+ return self.impl.gte(other.impl)
+ def __lt__(self, other):
+ return self.impl.lt(other.impl)
+ def __le__(self, other):
+ return self.impl.lte(other.impl)
diff --git a/pyRCV2/numbers/fixed_py.py b/pyRCV2/numbers/fixed_py.py
new file mode 100644
index 0000000..52530de
--- /dev/null
+++ b/pyRCV2/numbers/fixed_py.py
@@ -0,0 +1,53 @@
+# 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 decimal import Decimal
+
+_quantize_exp = 6
+
+def set_dps(dps):
+ global _quantize_exp
+ _quantize_exp = Decimal('10') ** -dps
+
+class Fixed:
+ """
+ Wrapper for Python Decimal (for fixed-point arithmetic)
+ """
+
+ def __init__(self, val):
+ self.impl = Decimal(val).quantize(_quantize_exp)
+
+ def pp(self, dp):
+ """Pretty print to specified number of decimal places"""
+ return format(self.impl, '.{}f'.format(dp))
+
+ def __add__(self, other):
+ return Fixed((self.impl + other.impl).quantize(_quantize_exp))
+ def __sub__(self, other):
+ return Fixed((self.impl - other.impl).quantize(_quantize_exp))
+ def __mul__(self, other):
+ return Fixed((self.impl * other.impl).quantize(_quantize_exp))
+ def __div__(self, other):
+ return Fixed((self.impl / other.impl).quantize(_quantize_exp))
+
+ def __gt__(self, other):
+ return self.impl > other.impl
+ def __ge__(self, other):
+ return self.impl >= other.impl
+ def __lt__(self, other):
+ return self.impl < other.impl
+ def __le__(self, other):
+ return self.impl <= other.impl
diff --git a/pyRCV2/numbers/native_js.py b/pyRCV2/numbers/native_js.py
index a18f066..4ebd0d3 100644
--- a/pyRCV2/numbers/native_js.py
+++ b/pyRCV2/numbers/native_js.py
@@ -15,10 +15,15 @@
# along with this program. If not, see .
class Native:
+ """
+ Wrapper for JS numbers (naive floating-point arithmetic)
+ """
+
def __init__(self, val):
self.impl = parseFloat(val)
def pp(self, dp):
+ """Pretty print to specified number of decimal places"""
return self.impl.toFixed(dp)
def __add__(self, other):
diff --git a/pyRCV2/numbers/native_py.py b/pyRCV2/numbers/native_py.py
index 9624176..8af6f4f 100644
--- a/pyRCV2/numbers/native_py.py
+++ b/pyRCV2/numbers/native_py.py
@@ -15,6 +15,10 @@
# along with this program. If not, see .
class Native:
+ """
+ Wrapper for Python float (naive floating-point arithmetic)
+ """
+
def __init__(self, val):
self.impl = float(val)
diff --git a/pyRCV2/numbers/rational_js.py b/pyRCV2/numbers/rational_js.py
index bfb942c..9109cf2 100644
--- a/pyRCV2/numbers/rational_js.py
+++ b/pyRCV2/numbers/rational_js.py
@@ -15,10 +15,15 @@
# along with this program. If not, see .
class Rational:
+ """
+ Wrapper for BigRational.js (rational arithmetic)
+ """
+
def __init__(self, val):
self.impl = bigRat(val)
def pp(self, dp):
+ """Pretty print to specified number of decimal places"""
return self.impl.valueOf().toFixed(dp)
def __add__(self, other):
diff --git a/pyRCV2/numbers/rational_py.py b/pyRCV2/numbers/rational_py.py
index ee6072b..c161e85 100644
--- a/pyRCV2/numbers/rational_py.py
+++ b/pyRCV2/numbers/rational_py.py
@@ -17,6 +17,10 @@
from fractions import Fraction
class Rational:
+ """
+ Wrapper for Python Fraction (rational arithmetic)
+ """
+
def __init__(self, val):
self.impl = Fraction(val)
diff --git a/test.html b/test.html
index 9411d9a..66f77c0 100644
--- a/test.html
+++ b/test.html
@@ -33,11 +33,12 @@
-
+
+