# Path: dividebatur/results.py
# class ElectionDistributionPerformed:
#     """
#     Information on any election distribution which is performed during a
#     counting round.
#     """
# 
#     def __init__(self, candidate_id, transfer_value):
#         """
#         transfer_value is a Fraction instance
#         """
#         self.candidate_id = candidate_id
#         self.transfer_value = transfer_value
# 
# class ExclusionDistributionPerformed:
#     """
#     Information on any exclusion distribution which is performed during a
#     counting round.
#     """
# 
#     def __init__(self, candidates, transfer_value):
#         """
#         candidates: a List of candidate_ids
#         transfer_value: transfer value, as a Fraction
#         """
#         self.candidates = candidates
#         self.transfer_value = transfer_value
# 
# class ActProvision:
#     """
#     Note that a provision of the Act has been used.
#     """
# 
#     def __init__(self, text):
#         """
#         text: textual description of the provision used.
#         """
# 
#         self.text = text
# 
# class CandidateElected:
#     """
#     Information on the election of a candidate.
#     """
# 
#     def __init__(self, candidate_id, order, excess_votes, paper_count, transfer_value):
#         """
#         candidate_id: the candidate elected
#         order: the number of the spot the candidate was elected to [1..N_vacancies]
#         excess_votes: the number of excess votes the candidate received
#         paper_count: the number of papers the candidate held at time of elect
#         transfer_value: the transfer value for the excess papers (0 if no excess papers)
#         """
# 
#         self.candidate_id = candidate_id
#         self.order = order
#         self.excess_votes = excess_votes
#         self.paper_count = paper_count
#         self.transfer_value = transfer_value
# 
# class CandidatesExcluded:
#     """
#     Information on the exclusion of one or more candidates.
#     """
# 
#     def __init__(self, candidates, transfer_values, reason):
#         """
#         candidates: list of candidate_ids of those candidates elected
#         transfer_values: the transfer values of papers to be distributed (a list of Fraction instances)
#         reason: an instance of ExclusionReason
#         """
#         self.candidates = candidates
#         self.transfer_values = transfer_values
#         self.reason = reason
# 
# class ExclusionReason:
#     """
#     information of interest about an exclusion of one or more
#     candidates.
# 
#     simply to make this information available to users of this
#     counter, this data is not used to make any decisions which
#     affect count results.
#     """
#     def __init__(self, reason, info):
#         """
#         reason: a string identifying the reason
#         info: additional information
#         """
#         self.reason, self.info = reason, info


from collections import defaultdict, namedtuple
from .results import ElectionDistributionPerformed, \
    ExclusionDistributionPerformed, ActProvision, \
    CandidateElected, CandidatesExcluded, \
    ExclusionReason
import itertools
import fractions
            for candidate_id in candidate_ids:
                votes = candidate_aggregates.get_vote_count(candidate_id)
                candidates_on_vote[votes] += 1
            if max(candidates_on_vote.values()) == 1:
                return candidate_aggregates

    def candidate_to_exclude(self, candidate_aggregates):
        def eligible(candidate_id):
            return candidate_id not in self.candidates_elected and candidate_id not in self.candidates_excluded

        candidate_ids = [t for t in self.candidate_ids if eligible(t)]
        min_votes = min(candidate_aggregates.get_vote_count(t) for t in candidate_ids)

        candidates_for_exclusion = []
        for candidate_id in candidate_ids:
            if candidate_aggregates.get_vote_count(candidate_id) == min_votes:
                candidates_for_exclusion.append(candidate_id)

        next_to_min_votes = None
        candidates_next_excluded = None
        if len(candidates_for_exclusion) > 1:
            tie_breaker_round = self.find_tie_breaker(candidates_for_exclusion)
            if tie_breaker_round is not None:
                excluded_votes = dict((tie_breaker_round.get_vote_count(candidate_id), candidate_id) for candidate_id in candidates_for_exclusion)
                self.results.provision_used(
                    ActProvision("Multiple candidates for exclusion holding %d votes. Tie broken from previous totals." % (min_votes)))
                lowest_vote = min(excluded_votes)
                excluded_candidate_id = excluded_votes[lowest_vote]
            else:
                self.results.provision_used(

# Path: dividebatur/results.py
# class ElectionDistributionPerformed:
#     """
#     Information on any election distribution which is performed during a
#     counting round.
#     """
# 
#     def __init__(self, candidate_id, transfer_value):
#         """
#         transfer_value is a Fraction instance
#         """
#         self.candidate_id = candidate_id
#         self.transfer_value = transfer_value
# 
# class ExclusionDistributionPerformed:
#     """
#     Information on any exclusion distribution which is performed during a
#     counting round.
#     """
# 
#     def __init__(self, candidates, transfer_value):
#         """
#         candidates: a List of candidate_ids
#         transfer_value: transfer value, as a Fraction
#         """
#         self.candidates = candidates
#         self.transfer_value = transfer_value
# 
# class ActProvision:
#     """
#     Note that a provision of the Act has been used.
#     """
# 
#     def __init__(self, text):
#         """
#         text: textual description of the provision used.
#         """
# 
#         self.text = text
# 
# class CandidateElected:
#     """
#     Information on the election of a candidate.
#     """
# 
#     def __init__(self, candidate_id, order, excess_votes, paper_count, transfer_value):
#         """
#         candidate_id: the candidate elected
#         order: the number of the spot the candidate was elected to [1..N_vacancies]
#         excess_votes: the number of excess votes the candidate received
#         paper_count: the number of papers the candidate held at time of elect
#         transfer_value: the transfer value for the excess papers (0 if no excess papers)
#         """
# 
#         self.candidate_id = candidate_id
#         self.order = order
#         self.excess_votes = excess_votes
#         self.paper_count = paper_count
#         self.transfer_value = transfer_value
# 
# class CandidatesExcluded:
#     """
#     Information on the exclusion of one or more candidates.
#     """
# 
#     def __init__(self, candidates, transfer_values, reason):
#         """
#         candidates: list of candidate_ids of those candidates elected
#         transfer_values: the transfer values of papers to be distributed (a list of Fraction instances)
#         reason: an instance of ExclusionReason
#         """
#         self.candidates = candidates
#         self.transfer_values = transfer_values
#         self.reason = reason
# 
# class ExclusionReason:
#     """
#     information of interest about an exclusion of one or more
#     candidates.
# 
#     simply to make this information available to users of this
#     counter, this data is not used to make any decisions which
#     affect count results.
#     """
#     def __init__(self, reason, info):
#         """
#         reason: a string identifying the reason
#         info: additional information
#         """
#         self.reason, self.info = reason, info


# Path: dividebatur/counter.py
from collections import defaultdict, namedtuple
from .results import ElectionDistributionPerformed, \
    ExclusionDistributionPerformed, ActProvision, \
    CandidateElected, CandidatesExcluded, \
    ExclusionReason
import itertools
import fractions

            for candidate_id in candidate_ids:
                votes = candidate_aggregates.get_vote_count(candidate_id)
                candidates_on_vote[votes] += 1
            if max(candidates_on_vote.values()) == 1:
                return candidate_aggregates

    def candidate_to_exclude(self, candidate_aggregates):
        def eligible(candidate_id):
            return candidate_id not in self.candidates_elected and candidate_id not in self.candidates_excluded

        candidate_ids = [t for t in self.candidate_ids if eligible(t)]
        min_votes = min(candidate_aggregates.get_vote_count(t) for t in candidate_ids)

        candidates_for_exclusion = []
        for candidate_id in candidate_ids:
            if candidate_aggregates.get_vote_count(candidate_id) == min_votes:
                candidates_for_exclusion.append(candidate_id)

        next_to_min_votes = None
        candidates_next_excluded = None
        if len(candidates_for_exclusion) > 1:
            tie_breaker_round = self.find_tie_breaker(candidates_for_exclusion)
            if tie_breaker_round is not None:
                excluded_votes = dict((tie_breaker_round.get_vote_count(candidate_id), candidate_id) for candidate_id in candidates_for_exclusion)
                self.results.provision_used(
                    ActProvision("Multiple candidates for exclusion holding %d votes. Tie broken from previous totals." % (min_votes)))
                lowest_vote = min(excluded_votes)
                excluded_candidate_id = excluded_votes[lowest_vote]
            else:
                self.results.provision_used(

                    ActProvision("Multiple candidates for exclusion holding %d votes. Input required from Australian Electoral Officer." % (min_votes)))