Implement integer number method
This commit is contained in:
parent
41251e11f6
commit
907c296337
@ -29,6 +29,9 @@ class STVCCounter:
|
||||
def reset(self):
|
||||
self.candidates = SafeDict([(c, CountCard()) for c in self.election.candidates])
|
||||
self.exhausted = CountCard()
|
||||
self.loss_fraction = CountCard()
|
||||
|
||||
self.total_orig = sum((b.value for b in self.election.ballots), Num('0'))
|
||||
|
||||
# Withdraw candidates
|
||||
for candidate in self.election.withdrawn:
|
||||
@ -57,7 +60,8 @@ class STVCCounter:
|
||||
'First preferences',
|
||||
self.candidates,
|
||||
self.exhausted,
|
||||
self.total + self.exhausted.votes,
|
||||
self.loss_fraction,
|
||||
self.total + self.exhausted.votes + self.loss_fraction.votes,
|
||||
self.quota
|
||||
)
|
||||
__pragma__('noopov')
|
||||
@ -67,6 +71,7 @@ class STVCCounter:
|
||||
for candidate, count_card in self.candidates.items():
|
||||
count_card.step()
|
||||
self.exhausted.step()
|
||||
self.loss_fraction.step()
|
||||
|
||||
# Have sufficient candidates been elected?
|
||||
if sum(1 for c, cc in self.candidates.items() if cc.state == CandidateState.PROVISIONALLY_ELECTED or cc.state == CandidateState.ELECTED) >= self.election.seats:
|
||||
@ -84,7 +89,8 @@ class STVCCounter:
|
||||
'Bulk election',
|
||||
self.candidates,
|
||||
self.exhausted,
|
||||
self.total + self.exhausted.votes,
|
||||
self.loss_fraction,
|
||||
self.total + self.exhausted.votes + self.loss_fraction.votes,
|
||||
self.quota
|
||||
)
|
||||
__pragma__('noopov')
|
||||
@ -132,7 +138,8 @@ class STVCCounter:
|
||||
'Surplus of ' + candidate_surplus.name,
|
||||
self.candidates,
|
||||
self.exhausted,
|
||||
self.total + self.exhausted.votes,
|
||||
self.loss_fraction,
|
||||
self.total + self.exhausted.votes + self.loss_fraction.votes,
|
||||
self.quota
|
||||
)
|
||||
__pragma__('noopov')
|
||||
@ -171,7 +178,8 @@ class STVCCounter:
|
||||
'Exclusion of ' + candidate_excluded.name,
|
||||
self.candidates,
|
||||
self.exhausted,
|
||||
self.total + self.exhausted.votes,
|
||||
self.loss_fraction,
|
||||
self.total + self.exhausted.votes + self.loss_fraction.votes,
|
||||
self.quota
|
||||
)
|
||||
__pragma__('noopov')
|
||||
@ -180,6 +188,7 @@ class STVCCounter:
|
||||
# Compute quota
|
||||
__pragma__('opov')
|
||||
self.total = sum((cc.votes for c, cc in self.candidates.items()), Num('0'))
|
||||
self.loss_fraction.transfers += (self.total_orig - self.total - self.exhausted.votes) - self.loss_fraction.votes
|
||||
self.quota = self.total / Num(self.election.seats + 1)
|
||||
__pragma__('noopov')
|
||||
|
||||
|
@ -77,11 +77,12 @@ class CountCompleted:
|
||||
pass
|
||||
|
||||
class CountStepResult:
|
||||
def __init__(self, comment, candidates, exhausted, total, quota):
|
||||
def __init__(self, comment, candidates, exhausted, loss_fraction, total, quota):
|
||||
self.comment = comment
|
||||
|
||||
self.candidates = candidates # SafeDict: Candidate -> CountCard
|
||||
self.exhausted = exhausted # CountCard
|
||||
self.loss_fraction = loss_fraction # CountCard
|
||||
|
||||
self.total = total
|
||||
self.quota = quota
|
||||
|
@ -23,11 +23,13 @@ __pragma__('noskip')
|
||||
if is_py:
|
||||
__pragma__('skip')
|
||||
from pyRCV2.numbers.fixed_py import Fixed, set_dps
|
||||
from pyRCV2.numbers.int_py import NativeInt
|
||||
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.int_js import NativeInt
|
||||
from pyRCV2.numbers.native_js import Native
|
||||
from pyRCV2.numbers.rational_js import Rational
|
||||
|
||||
|
@ -35,13 +35,13 @@ class Fixed:
|
||||
return format(self.impl, '.{}f'.format(dp))
|
||||
|
||||
def __add__(self, other):
|
||||
return Fixed((self.impl + other.impl).quantize(_quantize_exp))
|
||||
return Fixed(self.impl + other.impl)
|
||||
def __sub__(self, other):
|
||||
return Fixed((self.impl - other.impl).quantize(_quantize_exp))
|
||||
return Fixed(self.impl - other.impl)
|
||||
def __mul__(self, other):
|
||||
return Fixed((self.impl * other.impl).quantize(_quantize_exp))
|
||||
return Fixed(self.impl * other.impl)
|
||||
def __div__(self, other):
|
||||
return Fixed((self.impl / other.impl).quantize(_quantize_exp))
|
||||
return Fixed(self.impl / other.impl)
|
||||
|
||||
def __gt__(self, other):
|
||||
return self.impl > other.impl
|
||||
|
45
pyRCV2/numbers/int_js.py
Normal file
45
pyRCV2/numbers/int_js.py
Normal file
@ -0,0 +1,45 @@
|
||||
# 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
class NativeInt:
|
||||
"""
|
||||
Wrapper for JS integers
|
||||
"""
|
||||
|
||||
def __init__(self, val):
|
||||
self.impl = Math.floor(parseFloat(val))
|
||||
|
||||
def pp(self, dp):
|
||||
"""Pretty print to specified number of decimal places"""
|
||||
return self.impl.toFixed(0)
|
||||
|
||||
def __add__(self, other):
|
||||
return NativeInt(self.impl + other.impl)
|
||||
def __sub__(self, other):
|
||||
return NativeInt(self.impl - other.impl)
|
||||
def __mul__(self, other):
|
||||
return NativeInt(self.impl * other.impl)
|
||||
def __div__(self, other):
|
||||
return NativeInt(self.impl / other.impl)
|
||||
|
||||
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
|
45
pyRCV2/numbers/int_py.py
Normal file
45
pyRCV2/numbers/int_py.py
Normal file
@ -0,0 +1,45 @@
|
||||
# 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 <https://www.gnu.org/licenses/>.
|
||||
|
||||
class NativeInt:
|
||||
"""
|
||||
Wrapper for Python int
|
||||
"""
|
||||
|
||||
def __init__(self, val):
|
||||
self.impl = int(val)
|
||||
|
||||
def pp(self, dp):
|
||||
"""Pretty print to specified number of decimal places"""
|
||||
return str(self.impl)
|
||||
|
||||
def __add__(self, other):
|
||||
return NativeInt(self.impl + other.impl)
|
||||
def __sub__(self, other):
|
||||
return NativeInt(self.impl - other.impl)
|
||||
def __mul__(self, other):
|
||||
return NativeInt(self.impl * other.impl)
|
||||
def __div__(self, other):
|
||||
return NativeInt(self.impl / other.impl)
|
||||
|
||||
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
|
35
test.html
35
test.html
@ -33,7 +33,7 @@
|
||||
<body>
|
||||
<input type="file" id="bltFile">
|
||||
<button onclick="clickBtn();">OK</button>
|
||||
<select id="numbers"><option value="native" selected>Native</option><option value="rational">Rational</option><option value="fixed">Fixed (6 d.p.)</option></select>
|
||||
<select id="numbers"><option value="native" selected>Native Float</option><option value="int">Native Integer</option><option value="rational">Rational</option><option value="fixed">Fixed (6 d.p.)</option></select>
|
||||
|
||||
<table id="result"></table>
|
||||
|
||||
@ -54,7 +54,7 @@
|
||||
|
||||
// Step election
|
||||
let worker = new Worker('worker.js');
|
||||
let election, elComment, elExhausted1, elExhausted2, elQuota;
|
||||
let election, elComment, elExhausted1, elExhausted2, elLTF1, elLTF2, elTotal, elQuota;
|
||||
|
||||
worker.onmessage = function(evt) {
|
||||
if (evt.data.type == 'init') {
|
||||
@ -98,12 +98,26 @@
|
||||
tblResults.appendChild(elExhausted1);
|
||||
tblResults.appendChild(elExhausted2);
|
||||
|
||||
// Loss to fraction row
|
||||
elLTF1 = document.createElement('tr');
|
||||
elLTF1.classList.add('info');
|
||||
elLTF2 = document.createElement('tr');
|
||||
elLTF2.classList.add('info');
|
||||
|
||||
elTd = document.createElement('td');
|
||||
elTd.setAttribute('rowspan', '2');
|
||||
elTd.style.borderTop = '1px solid black';
|
||||
elTd.innerText = 'Loss to fraction';
|
||||
elLTF1.appendChild(elTd);
|
||||
|
||||
tblResults.appendChild(elLTF1);
|
||||
tblResults.appendChild(elLTF2);
|
||||
|
||||
// Total row
|
||||
elTotal = document.createElement('tr');
|
||||
elTotal.classList.add('info');
|
||||
elTd = document.createElement('td');
|
||||
elTd.style.borderTop = '1px solid black';
|
||||
elTd.style.borderBottom = '1px solid black';
|
||||
elTd.innerText = 'Total';
|
||||
elTotal.appendChild(elTd);
|
||||
tblResults.appendChild(elTotal);
|
||||
@ -177,11 +191,24 @@
|
||||
elTd.innerText = result.exhausted.votes;
|
||||
elExhausted2.appendChild(elTd);
|
||||
|
||||
// Display loss to fraction
|
||||
elTd = document.createElement('td');
|
||||
elTd.classList.add('count');
|
||||
elTd.style.borderTop = '1px solid black';
|
||||
if (result.loss_fraction.transfers != '0.00') {
|
||||
elTd.innerText = result.loss_fraction.transfers;
|
||||
}
|
||||
elLTF1.appendChild(elTd);
|
||||
|
||||
elTd = document.createElement('td');
|
||||
elTd.classList.add('count');
|
||||
elTd.innerText = result.loss_fraction.votes;
|
||||
elLTF2.appendChild(elTd);
|
||||
|
||||
// Display total
|
||||
elTd = document.createElement('td');
|
||||
elTd.classList.add('count');
|
||||
elTd.style.borderTop = '1px solid black';
|
||||
elTd.style.borderBottom = '1px solid black';
|
||||
elTd.innerText = result.total;
|
||||
elTotal.appendChild(elTd);
|
||||
|
||||
|
11
worker.js
11
worker.js
@ -8,6 +8,9 @@ onmessage = async function(evt) {
|
||||
if (evt.data.numbers == 'native') {
|
||||
py.pyRCV2.numbers.set_numclass(py.pyRCV2.numbers.Native);
|
||||
}
|
||||
if (evt.data.numbers == 'int') {
|
||||
py.pyRCV2.numbers.set_numclass(py.pyRCV2.numbers.NativeInt);
|
||||
}
|
||||
if (evt.data.numbers == 'rational') {
|
||||
py.pyRCV2.numbers.set_numclass(py.pyRCV2.numbers.Rational);
|
||||
}
|
||||
@ -37,6 +40,10 @@ onmessage = async function(evt) {
|
||||
'transfers': result.exhausted.transfers.pp(2),
|
||||
'votes': result.exhausted.votes.pp(2)
|
||||
},
|
||||
'loss_fraction': {
|
||||
'transfers': result.loss_fraction.transfers.pp(2),
|
||||
'votes': result.loss_fraction.votes.pp(2)
|
||||
},
|
||||
'total': result.total.pp(2),
|
||||
'quota': result.quota.pp(2)
|
||||
}});
|
||||
@ -60,6 +67,10 @@ onmessage = async function(evt) {
|
||||
'transfers': result.exhausted.transfers.pp(2),
|
||||
'votes': result.exhausted.votes.pp(2)
|
||||
},
|
||||
'loss_fraction': {
|
||||
'transfers': result.loss_fraction.transfers.pp(2),
|
||||
'votes': result.loss_fraction.votes.pp(2)
|
||||
},
|
||||
'total': result.total.pp(2),
|
||||
'quota': result.quota.pp(2)
|
||||
}});
|
||||
|
Reference in New Issue
Block a user