1 # election library -- a ruby library for elections
2 # copyright © 2005 MIT Media Lab and Benjamin Mako Hill
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 2 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 # General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 #################################################################
20 ## ==== election.rb ====
22 ## This file contains the core ElectionVote and ElectionResults
23 ## classes and the most common and simple election methods including
24 ## plurality and approval voting.
25 #################################################################
27 ##################################################################
28 ## ElectionVote Classes and SubClasses
30 ## There classes are used to store, verify, and "tally" (i.e. count
31 ## votes for the standard Election superclass and for the most common
32 ## types of elections.
36 attr_reader :candidates
38 def initialize(votes=nil)
39 @votes = Hash.new unless defined?(@votes)
40 @candidates = Array.new unless defined?(@candidates)
43 if votes.instance_of?( Array )
45 if self.verify_vote(vote)
48 raise InvalidVoteError.new("Invalid vote object", vote)
52 raise ElectionError, "Votes must be in the form of an array.", caller
58 # by default, this is set to look if the vote is defined. it should
59 # be overridden in each class
60 def verify_vote(vote=nil)
64 # by default, this does nothing. it must be redefined in any subclass
66 self.verify_vote(vote)
71 class PluralityVote < ElectionVote
73 PluralityResult.new(self)
77 def verify_vote(vote=nil)
81 def tally_vote(candidate)
82 if @votes.has_key?(candidate)
83 @votes[candidate] += 1
86 @candidates << candidate
91 class ApprovalVote < PluralityVote
93 ApprovalResult.new(self)
97 def verify_vote(vote=nil)
98 vote.instance_of?( Array ) and vote.length >= 1
101 def tally_vote(approvals)
102 approvals.each {|candidate| super(candidate)}
107 ##################################################################
108 ## Election Result Classes
111 # ElectionResult and its subclasses are used to identify and report the results
112 # of an election. In almost all cases, these will be returned by the #results
113 # method of a corresponding ElectionVote subclass.
115 # Each ElectionResult object has the following methods:
117 # * #winner? -- return Boolean as to the winner or winners of an election
118 # * #winners -- an array of winners of the election
119 # * #ranked_candidates -- (where available) a list of ranked candidates
122 attr_reader :election
124 def initialize(voteobj=nil)
125 unless voteobj and voteobj.kind_of?( ElectionVote )
126 raise ArgumentError, "You must pass a ElectionVote array.", caller
134 @winners[0] if @winners.length > 0
138 @winners.length > 0 and not @winners[0].nil?
143 class PluralityResult < ElectionResult
144 attr_reader :ranked_candidates
147 def initialize(voteobj=nil)
150 votes = @election.votes
151 candidates = @election.candidates
153 @ranked_candidates = votes.sort do |a, b|
155 end.collect {|a| a[0]}
157 @points = @election.votes
159 # winners are anyone who has the same number of votes as the
161 @winners = @ranked_candidates.find_all do |i|
162 votes[i] == votes[@ranked_candidates[0]]
167 # this class is complete because results for approval are computed
168 # identically to results from plurality
169 class ApprovalResult < PluralityResult
172 class ElectionError < ArgumentError
175 class InvalidVoteError < ElectionError
176 attr_accessor :voteobj
177 def initialize(msg=nil, voteobj=nil)