1 class InstantRunoffVote < ElectionVote
2 def initialize(votes=nil)
3 @candidates = Array.new
5 @candidates = vote.uniq if vote.uniq.length > candidates.length
8 @candidates.each do |candidate|
9 @votes[candidate] = [0, Hash.new] unless @votes.has_key?(candidate)
14 InstantRunoffResult.new(self, params)
20 candidate = votecopy.shift
21 votes[candidate] = [0, Hash.new] unless votes.has_key?(candidate)
22 votes[candidate][0] += 1
23 if votes[candidate][1].has_key?(votecopy)
24 votes[candidate][1][votecopy] += 1
26 votes[candidate][1][votecopy] = 1
30 def verify_vote(vote=nil)
31 vote.instance_of?( Array ) and
36 class InstantRunoffLogicVote < InstantRunoffVote
38 InstantRunoffLogicResult.new(self, params)
42 class InstantRunoffFirstRoundVote < InstantRunoffVote
44 InstantRunoffFirstRoundResult.new(self, params)
48 class InstantRunoffAllVote < InstantRunoffVote
50 InstantRunoffAllResult.new(self, params)
54 class InstantRunoffRandomVote < InstantRunoffVote
56 InstantRunoffRandomResult.new(self, params)
60 class InstantRunoffResult < ElectionResult
61 attr_reader :ranked_candidates
63 def initialize(voteobj=nil, params={})
64 unless voteobj and voteobj.kind_of?( InstantRunoffVote )
65 raise ArgumentError, "You must pass an InstantRunoffVote array.", caller
69 votes = @election.votes.clone
70 candidates = @election.candidates
71 votes_sum = votes.inject(0) {|n, value| n + value[1][0]}
72 @majority = votes_sum/2 + 1
73 @ranked_candidates = Array.new()
74 ranked_candidates = Array.new()
77 if params.has_key?('candidate_count')
78 apply_candidate_count(votes, params['candidate_count'])
80 if params.has_key?('vote_minimum')
81 apply_vote_minimum(votes, params['vote_minimum'])
83 if params.has_key?('percent_minimum')
84 apply_vote_minimum(votes, votes_sum * params['percent_minimum'])
86 if params.has_key?('percent_retention')
87 apply_retention(votes, votes_sum * params['percent_retention'])
90 unless votes.length > 0
96 ranked_candidates = votes.sort do |a, b|
98 end.collect {|i| i[0]}
99 @winners = ranked_candidates.find_all do |i|
100 votes[i][0] >= @majority
102 end while not self.winner? and next_round(votes, ranked_candidates)
103 @ranked_candidates.unshift(*ranked_candidates)
107 def apply_candidate_count(votes, candidate_count)
108 if votes.size > candidate_count
109 losers = votes.sort do |a, b|
111 end.collect {|i| i[0]}.last(votes.size - candidate_count)
112 @ranked_candidates.unshift(losers) unless losers.empty?
113 losers.each { |loser| remove_candidate(votes, loser) }
117 def apply_vote_minimum(votes, vote_minimum)
118 losers = votes.find_all do |i|
119 i[1][0] < vote_minimum
120 end.collect {|i| i[0]}
121 if losers.length == votes.size
124 @ranked_candidates.unshift(losers) unless losers.empty?
125 losers.each { |loser| remove_candidate(votes, loser) }
129 def apply_retention(votes, retention)
130 losers = votes.sort do |a, b|
132 end.collect {|i| i[0]}
134 while partial_sum < retention
135 partial_sum += votes[losers.shift][0]
137 @ranked_candidates.unshift(losers) unless losers.empty?
138 losers.each { |loser| remove_candidate(votes, loser) }
141 def next_round(votes, ranked_candidates)
142 loser = ranked_candidates[-1]
143 if votes.empty? or votes[loser][0] == votes[ranked_candidates[-2]][0]
146 @ranked_candidates.unshift(loser)
147 remove_candidate(votes, loser)
152 def remove_candidate(votes, loser)
153 votes.each_pair do |candidate, morevotes|
155 hash.each_pair do |vote, count|
161 votes[loser][1].each_pair do |vote, count|
162 candidate = vote.shift
163 votes[candidate][0] += count
164 if votes[candidate][1].has_key?(vote)
165 votes[candidate][1][vote] += count
167 votes[candidate][1][vote] = count
174 class InstantRunoffLogicResult < InstantRunoffResult
175 def next_round(votes, ranked_candidates)
176 losers = ranked_candidates.find_all do |i|
177 votes[i][0] == votes[ranked_candidates[-1]][0]
179 if losers.inject(0) {|n, loser| n + votes[loser][0]} >= @majority
182 @ranked_candidates.unshift(losers)
183 losers.each do |loser|
184 remove_candidate(votes, loser)
191 class InstantRunoffFirstRoundResult < InstantRunoffResult
192 def next_round(votes, ranked_candidates)
193 losers = ranked_candidates.find_all do |i|
194 votes[i][0] == votes[ranked_candidates[-1]][0]
196 loser = losers.sort do |a, b|
197 @election.votes[a][0] <=> @election.votes[b][0]
199 @ranked_candidates.unshift(loser)
200 remove_candidate(votes, loser)
204 class InstantRunoffAllResult < InstantRunoffResult
205 def next_round(votes, ranked_candidates)
206 losers = ranked_candidates.find_all do |i|
207 votes[i][0] == votes[ranked_candidates[-1]][0]
209 if losers.length == ranked_candidates.length
212 @ranked_candidates.unshift(losers)
213 losers.each do |loser|
214 remove_candidate(votes, loser)
221 class InstantRunoffRandomResult < InstantRunoffResult
222 def next_round(votes, ranked_candidates)
223 losers = ranked_candidates.find_all do |i|
224 votes[i][0] == votes[ranked_candidates[-1]][0]
226 loser = losers[rand(losers.length)]
227 @ranked_candidates.unshift(loser)
228 remove_candidate(votes, loser)