Implement integer number method
This commit is contained in:
parent
41251e11f6
commit
907c296337
@ -29,6 +29,9 @@ class STVCCounter:
|
|||||||
def reset(self):
|
def reset(self):
|
||||||
self.candidates = SafeDict([(c, CountCard()) for c in self.election.candidates])
|
self.candidates = SafeDict([(c, CountCard()) for c in self.election.candidates])
|
||||||
self.exhausted = CountCard()
|
self.exhausted = CountCard()
|
||||||
|
self.loss_fraction = CountCard()
|
||||||
|
|
||||||
|
self.total_orig = sum((b.value for b in self.election.ballots), Num('0'))
|
||||||
|
|
||||||
# Withdraw candidates
|
# Withdraw candidates
|
||||||
for candidate in self.election.withdrawn:
|
for candidate in self.election.withdrawn:
|
||||||
@ -57,7 +60,8 @@ class STVCCounter:
|
|||||||
'First preferences',
|
'First preferences',
|
||||||
self.candidates,
|
self.candidates,
|
||||||
self.exhausted,
|
self.exhausted,
|
||||||
self.total + self.exhausted.votes,
|
self.loss_fraction,
|
||||||
|
self.total + self.exhausted.votes + self.loss_fraction.votes,
|
||||||
self.quota
|
self.quota
|
||||||
)
|
)
|
||||||
__pragma__('noopov')
|
__pragma__('noopov')
|
||||||
@ -67,6 +71,7 @@ class STVCCounter:
|
|||||||
for candidate, count_card in self.candidates.items():
|
for candidate, count_card in self.candidates.items():
|
||||||
count_card.step()
|
count_card.step()
|
||||||
self.exhausted.step()
|
self.exhausted.step()
|
||||||
|
self.loss_fraction.step()
|
||||||
|
|
||||||
# Have sufficient candidates been elected?
|
# 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:
|
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',
|
'Bulk election',
|
||||||
self.candidates,
|
self.candidates,
|
||||||
self.exhausted,
|
self.exhausted,
|
||||||
self.total + self.exhausted.votes,
|
self.loss_fraction,
|
||||||
|
self.total + self.exhausted.votes + self.loss_fraction.votes,
|
||||||
self.quota
|
self.quota
|
||||||
)
|
)
|
||||||
__pragma__('noopov')
|
__pragma__('noopov')
|
||||||
@ -132,7 +138,8 @@ class STVCCounter:
|
|||||||
'Surplus of ' + candidate_surplus.name,
|
'Surplus of ' + candidate_surplus.name,
|
||||||
self.candidates,
|
self.candidates,
|
||||||
self.exhausted,
|
self.exhausted,
|
||||||
self.total + self.exhausted.votes,
|
self.loss_fraction,
|
||||||
|
self.total + self.exhausted.votes + self.loss_fraction.votes,
|
||||||
self.quota
|
self.quota
|
||||||
)
|
)
|
||||||
__pragma__('noopov')
|
__pragma__('noopov')
|
||||||
@ -171,7 +178,8 @@ class STVCCounter:
|
|||||||
'Exclusion of ' + candidate_excluded.name,
|
'Exclusion of ' + candidate_excluded.name,
|
||||||
self.candidates,
|
self.candidates,
|
||||||
self.exhausted,
|
self.exhausted,
|
||||||
self.total + self.exhausted.votes,
|
self.loss_fraction,
|
||||||
|
self.total + self.exhausted.votes + self.loss_fraction.votes,
|
||||||
self.quota
|
self.quota
|
||||||
)
|
)
|
||||||
__pragma__('noopov')
|
__pragma__('noopov')
|
||||||
@ -180,6 +188,7 @@ class STVCCounter:
|
|||||||
# Compute quota
|
# Compute quota
|
||||||
__pragma__('opov')
|
__pragma__('opov')
|
||||||
self.total = sum((cc.votes for c, cc in self.candidates.items()), Num('0'))
|
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)
|
self.quota = self.total / Num(self.election.seats + 1)
|
||||||
__pragma__('noopov')
|
__pragma__('noopov')
|
||||||
|
|
||||||
|
@ -77,11 +77,12 @@ class CountCompleted:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
class CountStepResult:
|
class CountStepResult:
|
||||||
def __init__(self, comment, candidates, exhausted, total, quota):
|
def __init__(self, comment, candidates, exhausted, loss_fraction, total, quota):
|
||||||
self.comment = comment
|
self.comment = comment
|
||||||
|
|
||||||
self.candidates = candidates # SafeDict: Candidate -> CountCard
|
self.candidates = candidates # SafeDict: Candidate -> CountCard
|
||||||
self.exhausted = exhausted # CountCard
|
self.exhausted = exhausted # CountCard
|
||||||
|
self.loss_fraction = loss_fraction # CountCard
|
||||||
|
|
||||||
self.total = total
|
self.total = total
|
||||||
self.quota = quota
|
self.quota = quota
|
||||||
|
@ -23,11 +23,13 @@ __pragma__('noskip')
|
|||||||
if is_py:
|
if is_py:
|
||||||
__pragma__('skip')
|
__pragma__('skip')
|
||||||
from pyRCV2.numbers.fixed_py import Fixed, set_dps
|
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.native_py import Native
|
||||||
from pyRCV2.numbers.rational_py import Rational
|
from pyRCV2.numbers.rational_py import Rational
|
||||||
__pragma__('noskip')
|
__pragma__('noskip')
|
||||||
else:
|
else:
|
||||||
from pyRCV2.numbers.fixed_js import Fixed, set_dps
|
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.native_js import Native
|
||||||
from pyRCV2.numbers.rational_js import Rational
|
from pyRCV2.numbers.rational_js import Rational
|
||||||
|
|
||||||
|
@ -35,13 +35,13 @@ class Fixed:
|
|||||||
return format(self.impl, '.{}f'.format(dp))
|
return format(self.impl, '.{}f'.format(dp))
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
return Fixed((self.impl + other.impl).quantize(_quantize_exp))
|
return Fixed(self.impl + other.impl)
|
||||||
def __sub__(self, other):
|
def __sub__(self, other):
|
||||||
return Fixed((self.impl - other.impl).quantize(_quantize_exp))
|
return Fixed(self.impl - other.impl)
|
||||||
def __mul__(self, other):
|
def __mul__(self, other):
|
||||||
return Fixed((self.impl * other.impl).quantize(_quantize_exp))
|
return Fixed(self.impl * other.impl)
|
||||||
def __div__(self, other):
|
def __div__(self, other):
|
||||||
return Fixed((self.impl / other.impl).quantize(_quantize_exp))
|
return Fixed(self.impl / other.impl)
|
||||||
|
|
||||||
def __gt__(self, other):
|
def __gt__(self, other):
|
||||||
return self.impl > other.impl
|
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>
|
<body>
|
||||||
<input type="file" id="bltFile">
|
<input type="file" id="bltFile">
|
||||||
<button onclick="clickBtn();">OK</button>
|
<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>
|
<table id="result"></table>
|
||||||
|
|
||||||
@ -54,7 +54,7 @@
|
|||||||
|
|
||||||
// Step election
|
// Step election
|
||||||
let worker = new Worker('worker.js');
|
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) {
|
worker.onmessage = function(evt) {
|
||||||
if (evt.data.type == 'init') {
|
if (evt.data.type == 'init') {
|
||||||
@ -98,12 +98,26 @@
|
|||||||
tblResults.appendChild(elExhausted1);
|
tblResults.appendChild(elExhausted1);
|
||||||
tblResults.appendChild(elExhausted2);
|
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
|
// Total row
|
||||||
elTotal = document.createElement('tr');
|
elTotal = document.createElement('tr');
|
||||||
elTotal.classList.add('info');
|
elTotal.classList.add('info');
|
||||||
elTd = document.createElement('td');
|
elTd = document.createElement('td');
|
||||||
elTd.style.borderTop = '1px solid black';
|
elTd.style.borderTop = '1px solid black';
|
||||||
elTd.style.borderBottom = '1px solid black';
|
|
||||||
elTd.innerText = 'Total';
|
elTd.innerText = 'Total';
|
||||||
elTotal.appendChild(elTd);
|
elTotal.appendChild(elTd);
|
||||||
tblResults.appendChild(elTotal);
|
tblResults.appendChild(elTotal);
|
||||||
@ -177,11 +191,24 @@
|
|||||||
elTd.innerText = result.exhausted.votes;
|
elTd.innerText = result.exhausted.votes;
|
||||||
elExhausted2.appendChild(elTd);
|
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
|
// Display total
|
||||||
elTd = document.createElement('td');
|
elTd = document.createElement('td');
|
||||||
elTd.classList.add('count');
|
elTd.classList.add('count');
|
||||||
elTd.style.borderTop = '1px solid black';
|
elTd.style.borderTop = '1px solid black';
|
||||||
elTd.style.borderBottom = '1px solid black';
|
|
||||||
elTd.innerText = result.total;
|
elTd.innerText = result.total;
|
||||||
elTotal.appendChild(elTd);
|
elTotal.appendChild(elTd);
|
||||||
|
|
||||||
|
11
worker.js
11
worker.js
@ -8,6 +8,9 @@ onmessage = async function(evt) {
|
|||||||
if (evt.data.numbers == 'native') {
|
if (evt.data.numbers == 'native') {
|
||||||
py.pyRCV2.numbers.set_numclass(py.pyRCV2.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') {
|
if (evt.data.numbers == 'rational') {
|
||||||
py.pyRCV2.numbers.set_numclass(py.pyRCV2.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),
|
'transfers': result.exhausted.transfers.pp(2),
|
||||||
'votes': result.exhausted.votes.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),
|
'total': result.total.pp(2),
|
||||||
'quota': result.quota.pp(2)
|
'quota': result.quota.pp(2)
|
||||||
}});
|
}});
|
||||||
@ -60,6 +67,10 @@ onmessage = async function(evt) {
|
|||||||
'transfers': result.exhausted.transfers.pp(2),
|
'transfers': result.exhausted.transfers.pp(2),
|
||||||
'votes': result.exhausted.votes.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),
|
'total': result.total.pp(2),
|
||||||
'quota': result.quota.pp(2)
|
'quota': result.quota.pp(2)
|
||||||
}});
|
}});
|
||||||
|
Reference in New Issue
Block a user