diff --git a/pyRCV2/method/base_stv.py b/pyRCV2/method/base_stv.py index 6b9fcc7..009176e 100644 --- a/pyRCV2/method/base_stv.py +++ b/pyRCV2/method/base_stv.py @@ -31,6 +31,8 @@ class BaseSTVCounter: def __init__(self, election, options=None): self.election = election + self.cls_ballot_value = Num # Need to use Rational in unweighted inclusive Gregory + # Default options self.options = { 'prog_quota': False, # Progressively reducing quota? @@ -60,9 +62,35 @@ class BaseSTVCounter: """ Public function: Reset the count and perform the first step - Subclasses must override this function """ - raise NotImplementedError('Method not implemented') + + # Distribute first preferences + for ballot in self.election.ballots: + __pragma__('opov') + candidate = next((c for c in ballot.preferences if self.candidates[c].state == CandidateState.HOPEFUL), None) + + if candidate is not None: + self.candidates[candidate].transfers += ballot.value + self.candidates[candidate].ballots.append((ballot, self.cls_ballot_value(ballot.value))) + else: + self.exhausted.transfers += ballot.value + self.exhausted.ballots.append((ballot, self.cls_ballot_value(ballot.value))) + __pragma__('noopov') + + self.quota = None + self.compute_quota() + self.elect_meeting_quota() + + __pragma__('opov') + return CountStepResult( + 'First preferences', + self.candidates, + self.exhausted, + self.loss_fraction, + self.total + self.exhausted.votes + self.loss_fraction.votes, + self.quota + ) + __pragma__('noopov') def step(self): """ @@ -295,35 +323,6 @@ class BaseWIGSTVCounter(BaseSTVCounter): Basic weighted inclusive Gregory STV counter """ - def reset(self): - # Distribute first preferences - for ballot in self.election.ballots: - __pragma__('opov') - candidate = next((c for c in ballot.preferences if self.candidates[c].state == CandidateState.HOPEFUL), None) - - if candidate is not None: - self.candidates[candidate].transfers += ballot.value - self.candidates[candidate].ballots.append((ballot, ballot.value)) - else: - self.exhausted.transfers += ballot.value - self.exhausted.ballots.append((ballot, ballot.value)) - __pragma__('noopov') - - self.quota = None - self.compute_quota() - self.elect_meeting_quota() - - __pragma__('opov') - return CountStepResult( - 'First preferences', - self.candidates, - self.exhausted, - self.loss_fraction, - self.total + self.exhausted.votes + self.loss_fraction.votes, - self.quota - ) - __pragma__('noopov') - def do_surplus(self, candidate_surplus, count_card, surplus): for ballot, ballot_value in count_card.ballots: __pragma__('opov') @@ -357,34 +356,10 @@ class BaseUIGSTVCounter(BaseSTVCounter): Basic unweighted inclusive Gregory STV counter """ - def reset(self): - # Distribute first preferences - for ballot in self.election.ballots: - __pragma__('opov') - candidate = next((c for c in ballot.preferences if self.candidates[c].state == CandidateState.HOPEFUL), None) - - if candidate is not None: - self.candidates[candidate].transfers += ballot.value - self.candidates[candidate].ballots.append((ballot, Rational(ballot.value.pp(0)))) - else: - self.exhausted.transfers += ballot.value - self.exhausted.ballots.append((ballot, Rational(ballot.value.pp(0)))) - __pragma__('noopov') - - self.quota = None - self.compute_quota() - self.elect_meeting_quota() - - __pragma__('opov') - return CountStepResult( - 'First preferences', - self.candidates, - self.exhausted, - self.loss_fraction, - self.total + self.exhausted.votes + self.loss_fraction.votes, - self.quota - ) - __pragma__('noopov') + def __init__(self, *args, **kwargs): + BaseSTVCounter.__init__(self, *args, **kwargs) + # Need to use Rational for ballot value internally, as Num may be set to integers only + self.cls_ballot_value = Rational def do_surplus(self, candidate_surplus, count_card, surplus): # FIXME: Is it okay to use native int's here? diff --git a/pyRCV2/numbers/fixed_js.py b/pyRCV2/numbers/fixed_js.py index deec595..0ce24a6 100644 --- a/pyRCV2/numbers/fixed_js.py +++ b/pyRCV2/numbers/fixed_js.py @@ -25,7 +25,10 @@ class Fixed: """ def __init__(self, val): - self.impl = Big(val) + if isinstance(val, Fixed): + self.impl = val.impl + else: + self.impl = Big(val) def pp(self, dp): """Pretty print to specified number of decimal places""" diff --git a/pyRCV2/numbers/int_js.py b/pyRCV2/numbers/int_js.py index 2571ae2..3815c0f 100644 --- a/pyRCV2/numbers/int_js.py +++ b/pyRCV2/numbers/int_js.py @@ -20,7 +20,10 @@ class NativeInt: """ def __init__(self, val): - self.impl = Math.floor(parseFloat(val)) + if isinstance(val, NativeInt): + self.impl = val.impl + else: + self.impl = Math.floor(parseFloat(val)) def pp(self, dp): """Pretty print to specified number of decimal places""" diff --git a/pyRCV2/numbers/native_js.py b/pyRCV2/numbers/native_js.py index 48489b5..80f2d26 100644 --- a/pyRCV2/numbers/native_js.py +++ b/pyRCV2/numbers/native_js.py @@ -20,7 +20,10 @@ class Native: """ def __init__(self, val): - self.impl = parseFloat(val) + if isinstance(val, Native): + self.impl = val.impl + else: + self.impl = parseFloat(val) def pp(self, dp): """Pretty print to specified number of decimal places""" diff --git a/pyRCV2/numbers/rational_js.py b/pyRCV2/numbers/rational_js.py index 10cc9b2..7883072 100644 --- a/pyRCV2/numbers/rational_js.py +++ b/pyRCV2/numbers/rational_js.py @@ -20,7 +20,10 @@ class Rational: """ def __init__(self, val): - self.impl = bigRat(val) + if isinstance(val, Rational): + self.impl = val.impl + else: + self.impl = bigRat(val) def pp(self, dp): """