def exclude_candidates(self, candidates, reason):
        """
        mark one or more candidates as excluded from the count
        candidates: list of candidate_ids to exclude
        reason: the reason for the exclusion
        """

        # put some paranoia around exclusion: we want to make sure that
        # `candidates` is unique, and that none of these candidates have
        # been previously excluded
        for candidate_id in candidates:
            assert(candidate_id not in self.candidates_excluded)
        assert(len(set(candidates)) == len(candidates))

        # determine the paper transfers to be run, and the candidates
        # holding papers which are distributed in each transfer
        transfers_applicable = defaultdict(set)
        for candidate_id in candidates:
            self.candidates_excluded[candidate_id] = True
            for bundle_transaction in self.candidate_bundle_transactions.get(candidate_id):
                value = bundle_transaction.transfer_value
                transfers_applicable[value].add(candidate_id)

        transfer_values = list(reversed(sorted(transfers_applicable)))
        self.results.candidates_excluded(
            CandidatesExcluded(
                candidates=candidates,
                transfer_values=transfer_values,
                reason=reason))

        for transfer_value in transfer_values:
            self.exclusion_distributions_pending.append((list(transfers_applicable[transfer_value]), transfer_value))