File Coverage

blib/lib/Vote/Count/Approval.pm
Criterion Covered Total %
statement 83 84 98.8
branch 11 12 91.6
condition n/a
subroutine 13 13 100.0
pod 1 3 33.3
total 108 112 96.4


line stmt bran cond sub pod time code
1 39     39   19794 use strict;
  39         98  
  39         1222  
2 39     39   212 use warnings;
  39         88  
  39         964  
3 39     39   612 use 5.024;
  39         139  
4              
5 39     39   210 use feature qw /postderef signatures/;
  39         77  
  39         3338  
6              
7             use Moose::Role;
8 39     39   273  
  39         88  
  39         303  
9             no warnings 'experimental';
10 39     39   204019 use Carp;
  39         101  
  39         1550  
11 39     39   246  
  39         84  
  39         40657  
12             our $VERSION='2.02';
13              
14             =head1 NAME
15              
16             Vote::Count::Approval
17              
18             =head1 VERSION 2.02
19              
20             =cut
21              
22             # ABSTRACT: Approval for Vote::Count. Toolkit for vote counting.
23              
24             =head1 Definition of Approval
25              
26             In Approval Voting, voters indicate which Choices they approve of indicating no preference. Approval can be infered from a Ranked Choice Ballot, by treating each ranked Choice as Approved.
27              
28             =head1 Method Approval
29              
30             Returns a RankCount object for the current Active Set taking an optional argument of an active list as a HashRef.
31              
32             my $Approval = $Election->Approval();
33             say $Approval->RankTable;
34              
35             # to specify the active set
36             my $Approval = $Election->Approval( $activeset );
37              
38             # to specify a cutoff on Range Ballots
39             my $Approval = $Election->Approval( $activeset, $cutoff );
40              
41             For RCV, Approval respects weighting, 'votevalue' is defaulted to 1 by readballots. Integers or Floating point values may be used.
42              
43             =head2 Method LastApprovalBallots
44              
45             Returns a hashref of the unweighted raw count from the last Approval operation.
46              
47             =head2 Method NonApproval
48              
49             The opposite of Approval. Returns a RankCount object for the current Active Set of the non-exhausted ballots not supporting a choice. It does not have the option to provide an Active Set. Only available for Ranked Ballots.
50              
51             =head3 Cutoff (Range Ballots Only)
52              
53             When counting Approval on Range Ballots an optional cutoff value may be provided as a second argument to the Approval Method. When doing so the Active Set argument must be provided. The Cutoff value is a score below which the ballot is considered to not approve of the choice.
54              
55             When counting Approval on Range Ballots it is appropriate to set a threshold below which a choice is not considered to be supported by the voter, but indicated to represent a preference to even lower or unranked choices.
56              
57             For Ranked Ballots the equivalent would be to provide a 'Nuetral Preference', for voters to indicate that any choices ranked lower should not be considered approved. This is not presently implemented, but may be at some point in the future.
58              
59             =cut
60              
61             has 'LastApprovalBallots' => (
62             is => 'rw',
63             isa => 'HashRef',
64             required => 0,
65             );
66              
67             my %approval = ( map { $_ => 0 } keys( $active->%* ) );
68 116     116   192 my %lastappcount = ( map { $_ => 0 } keys( $active->%* ) );
  116         400  
  116         174  
  116         172  
  116         176  
69 116         364 for my $b ( keys %{$ballots} ) {
  554         1071  
70 116         367 my @votes = $ballots->{$b}->{'votes'}->@*;
  554         928  
71 116         257 for my $v (@votes) {
  116         519  
72 2016         4001 if ( defined $approval{$v} ) {
73 2016         2767 $approval{$v} += $ballots->{$b}{'count'} * $ballots->{$b}{'votevalue'} ;
74 5988 100       10590 $lastappcount{$v} += $ballots->{$b}{'count'};
75 3918         5924 }
76 3918         6708 }
77             }
78             $I->LastApprovalBallots( \%lastappcount );
79             return Vote::Count::RankCount->Rank( \%approval );
80 116         3868 }
81 116         611  
82             my %approval = ( map { $_ => 0 } keys( $active->%* ) );
83             for my $b ( @{$ballots} ) {
84 157     157   173 APPROVALKEYSC: for my $c ( keys $b->{'votes'}->%* ) {
  157         196  
  157         266  
  157         194  
  157         164  
85 157         400 # croak "key is $c " . "value is $b->{'votes'}{$c}";
  1745         2440  
86 157         328 next APPROVALKEYSC if $b->{'votes'}{$c} < $cutoff;
  157         291  
87 916         1519 $approval{$c} += $b->{'count'} if defined $approval{$c};
88             }
89 3364 100       4792 }
90 3329 100       5442 return Vote::Count::RankCount->Rank( \%approval );
91             }
92              
93 157         456 my %BallotSet = $self->BallotSet()->%*;
94             $active = $self->Active() unless defined $active;
95             if ( $BallotSet{'options'}{'range'} ) {
96 270     270 0 17535 return _approval_range_do( $active, $BallotSet{'ballots'},
  270         353  
  270         384  
  270         388  
  270         316  
97 270         6542 $cutoff );
98 270 100       4275 }
99 270 100       655 else {
100 157         320 $self->_approval_rcv_do( $active, $BallotSet{'ballots'} );
101             }
102             }
103              
104 113         408  
105             my $active = $I->Active();
106             my %nonapproval = ( map { $_ => 0 } keys( $active->%* ) );
107             my %approval = %{$I->_approval_rcv_do ( $active, $ballots )->RawCount()};
108 3     3 0 19 my $activevotes = $I->VotesActive();
109             for my $A ( keys %nonapproval) {
110 3     3   6 $nonapproval{ $A } = $activevotes - $approval{ $A };
  3         5  
  3         4  
  3         5  
111 3         102 }
112 3         13 return Vote::Count::RankCount->Rank( \%nonapproval );
  24         46  
113 3         9 }
  3         10  
114 3         29  
115 3         15 # For each choice in the active set counts ballots
116 24         39 my %BallotSet = $I->BallotSet()->%*;
117             my $ballots = $BallotSet{'ballots'};
118 3         14 if ( $BallotSet{'options'}{'rcv'} ) {
119             return _non_approval_rcv_do( $I, $ballots );
120             }
121             else {
122 3     3 1 8 die "NonApproval currently implemented for RCV ballots."
  3         5  
  3         7  
  3         5  
123 3         111 }
124 3         10 }
125 3 50       11  
126 3         12 1;
127              
128             #FOOTER
129 0            
130             =pod
131              
132             BUG TRACKER
133              
134             L<https://github.com/brainbuz/Vote-Count/issues>
135              
136             AUTHOR
137              
138             John Karr (BRAINBUZ) brainbuz@cpan.org
139              
140             CONTRIBUTORS
141              
142             Copyright 2019-2021 by John Karr (BRAINBUZ) brainbuz@cpan.org.
143              
144             LICENSE
145              
146             This module is released under the GNU Public License Version 3. See license file for details. For more information on this license visit L<http://fsf.org>.
147              
148             SUPPORT
149              
150             This software is provided as is, per the terms of the GNU Public License. Professional support and customisation services are available from the author.
151              
152             =cut
153