extends MajorityJudgmentAbstractTallier class_name MajorityJudgmentEasyTallier """ Here's a pseudo algorithm to compare two candidates, assuming they received the same number of judgments: compare(candidate_a, candidate_b): 00. if no judgments -> done (equality) 01. compute median grades of a and b 02. if different -> done 03. remove one judgment of the median grade to both a and b 04. go to 00 See __sort_candidate_tallies below for an implementation """ func tally(poll) -> MajorityJudgmentPollTally: var participants_amount : int = poll.count_participants() var candidates_tallies := Array() for candidate in poll.candidates: var candidate_tally := MajorityJudgmentCandidateTally.new() candidate_tally.poll = poll candidate_tally.candidate = candidate var merit_profile := MajorityJudgmentCandidateMeritProfile.new() var amount_of_judgments := 0 var amount_of_judgments_per_grade := Array() for grade in poll.grading.grades: var amount = 0 for judgment in poll.judgments: if judgment.candidate != candidate: continue if judgment.grade != grade: continue amount += 1 amount_of_judgments += amount amount_of_judgments_per_grade.append(amount) # Add missing judgments as TO_REJECT assert(amount_of_judgments <= participants_amount) var amount_missing = participants_amount - amount_of_judgments if amount_missing: amount_of_judgments_per_grade[0] += amount_missing merit_profile.grades = amount_of_judgments_per_grade candidate_tally.merit_profile = merit_profile candidates_tallies.append(candidate_tally) candidates_tallies.sort_custom(self, "__sort_candidate_tallies") # Deduce the position var current_position := 1 for p in range(candidates_tallies.size()): if 0 == p: candidates_tallies[p].position = 1 else: if 0 != __compare_candidate_tallies(candidates_tallies[p], candidates_tallies[p-1]): current_position = p + 1 candidates_tallies[p].position = current_position var poll_tally = MajorityJudgmentPollTally.new() poll_tally.poll = self poll_tally.candidates_tallies = candidates_tallies return poll_tally # Compare function for a sort from best to worst (descending) # This function is expected to return true if a < b, when ascending # so we'll return true if a > b, since we're descending. # Wrap for the boolean API of Array.custom_sort(), basically. func __sort_candidate_tallies(a, b) -> bool: if 0 > __compare_candidate_tallies(a, b): return true return false # a < b (median_a > median_b) --> -1 # a = b (median_a = median_b) --> 0 # a > b (median_a < median_b) --> +1 func __compare_candidate_tallies(a, b) -> int: assert(a, "Candidate A's tally is undefined.") assert(b, "Candidate B's tally is undefined.") var profile_a = a.merit_profile.duplicate() var profile_b = b.merit_profile.duplicate() var amount_of_judgments_a = profile_a.count_judgments() var amount_of_judgments_b = profile_b.count_judgments() assert( amount_of_judgments_a == amount_of_judgments_b, "Judgments amounts mismatch between candidates tallies A and B." ) # 00. if no more judgments -> done (equality) while (amount_of_judgments_a > 0): # 01. compute median grades of a and b var median_a = profile_a.get_median() var median_b = profile_b.get_median() assert(0 <= median_a, "Median of A not found.") assert(0 <= median_b, "Median of B not found.") # 02. if different -> done if median_a < median_b: return 1 if median_a > median_b: return -1 assert(median_a == median_b, "Sanity") # 03. remove one judgment of the median grade to both a and b profile_a.remove_one_judgment(median_a) profile_b.remove_one_judgment(median_b) # 04. go to 00 amount_of_judgments_a = profile_a.count_judgments() amount_of_judgments_b = profile_b.count_judgments() # for good measure return 0 # a == b