File Coverage

blib/lib/Vote/Count/Approval.pm
Criterion Covered Total %
statement 82 83 98.8
branch 11 12 91.6
condition n/a
subroutine 12 12 100.0
pod 1 2 50.0
total 106 109 97.2


line stmt bran cond sub pod time code
1 39     39   24629 use strict;
  39         99  
  39         1412  
2 39     39   228 use warnings;
  39         112  
  39         1069  
3 39     39   727 use 5.024;
  39         144  
4              
5 39     39   217 use feature qw /postderef signatures/;
  39         83  
  39         3571  
6              
7             package Vote::Count::Approval;
8 39     39   295 use Moose::Role;
  39         78  
  39         302  
9              
10 39     39   212146 no warnings 'experimental';
  39         104  
  39         1700  
11 39     39   248 use Carp;
  39         83  
  39         42567  
12              
13             our $VERSION='2.00';
14              
15             =head1 NAME
16              
17             Vote::Count::Approval
18              
19             =head1 VERSION 2.00
20              
21             =cut
22              
23             # ABSTRACT: Approval for Vote::Count. Toolkit for vote counting.
24              
25             =head1 Definition of Approval
26              
27             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.
28              
29             =head1 Method Approval
30              
31             Returns a RankCount object for the current Active Set taking an optional argument of an active list as a HashRef.
32              
33             my $Approval = $Election->Approval();
34             say $Approval->RankTable;
35              
36             # to specify the active set
37             my $Approval = $Election->Approval( $activeset );
38              
39             # to specify a cutoff on Range Ballots
40             my $Approval = $Election->Approval( $activeset, $cutoff );
41              
42             For RCV, Approval respects weighting, 'votevalue' is defaulted to 1 by readballots. Integers or Floating point values may be used.
43              
44             =head2 Method LastApprovalBallots
45              
46             Returns a hashref of the unweighted raw count from the last Approval operation.
47              
48             =head2 Method NonApproval
49              
50             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.
51              
52             =head3 Cutoff (Range Ballots Only)
53              
54             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.
55              
56             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.
57              
58             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.
59              
60             =cut
61              
62             has 'LastApprovalBallots' => (
63             is => 'rw',
64             isa => 'HashRef',
65             required => 0,
66             );
67              
68 55     55   175 sub _approval_rcv_do ( $I, $active, $ballots ) {
  55         90  
  55         92  
  55         91  
  55         88  
69 55         198 my %approval = ( map { $_ => 0 } keys( $active->%* ) );
  352         654  
70 55         194 my %lastappcount = ( map { $_ => 0 } keys( $active->%* ) );
  352         563  
71 55         145 for my $b ( keys %{$ballots} ) {
  55         342  
72 1315         2616 my @votes = $ballots->{$b}->{'votes'}->@*;
73 1315         1769 for my $v (@votes) {
74 4592 100       7741 if ( defined $approval{$v} ) {
75 3557         5255 $approval{$v} += $ballots->{$b}{'count'} * $ballots->{$b}{'votevalue'} ;
76 3557         5706 $lastappcount{$v} += $ballots->{$b}{'count'};
77             }
78             }
79             }
80 55         1952 $I->LastApprovalBallots( \%lastappcount );
81 55         300 return Vote::Count::RankCount->Rank( \%approval );
82             }
83              
84 151     151   204 sub _approval_range_do ( $active, $ballots, $cutoff ) {
  151         213  
  151         211  
  151         193  
  151         193  
85 151         492 my %approval = ( map { $_ => 0 } keys( $active->%* ) );
  1721         2825  
86 151         373 for my $b ( @{$ballots} ) {
  151         310  
87 892         1857 APPROVALKEYSC: for my $c ( keys $b->{'votes'}->%* ) {
88             # croak "key is $c " . "value is $b->{'votes'}{$c}";
89 3268 100       5542 next APPROVALKEYSC if $b->{'votes'}{$c} < $cutoff;
90 3233 100       6150 $approval{$c} += $b->{'count'} if defined $approval{$c};
91             }
92             }
93 151         529 return Vote::Count::RankCount->Rank( \%approval );
94             }
95              
96 203     203 0 17562 sub Approval ( $self, $active = undef, $cutoff = 0 ) {
  203         286  
  203         319  
  203         312  
  203         279  
97 203         5403 my %BallotSet = $self->BallotSet()->%*;
98 203 100       4651 $active = $self->Active() unless defined $active;
99 203 100       586 if ( $BallotSet{'options'}{'range'} ) {
100 151         358 return _approval_range_do( $active, $BallotSet{'ballots'},
101             $cutoff );
102             }
103             else {
104 52         233 $self->_approval_rcv_do( $active, $BallotSet{'ballots'} );
105             }
106             }
107              
108 3     3   7 sub _non_approval_rcv_do ( $I, $ballots ) {
  3         4  
  3         6  
  3         19  
109 3         79 my $active = $I->Active();
110 3         13 my %nonapproval = ( map { $_ => 0 } keys( $active->%* ) );
  24         49  
111 3         10 my %approval = %{$I->_approval_rcv_do ( $active, $ballots )->RawCount()};
  3         10  
112 3         26 my $activevotes = $I->VotesActive();
113 3         12 for my $A ( keys %nonapproval) {
114 24         39 $nonapproval{ $A } = $activevotes - $approval{ $A };
115             }
116 3         14 return Vote::Count::RankCount->Rank( \%nonapproval );
117             }
118              
119             # For each choice in the active set counts ballots
120 3     3 1 5 sub NonApproval ( $I, $cutoff = 0 ) {
  3         8  
  3         6  
  3         5  
121 3         85 my %BallotSet = $I->BallotSet()->%*;
122 3         9 my $ballots = $BallotSet{'ballots'};
123 3 50       21 if ( $BallotSet{'options'}{'rcv'} ) {
124 3         11 return _non_approval_rcv_do( $I, $ballots );
125             }
126             else {
127 0           die "NonApproval currently implemented for RCV ballots."
128             }
129             }
130              
131             1;
132              
133             #FOOTER
134              
135             =pod
136              
137             BUG TRACKER
138              
139             L<https://github.com/brainbuz/Vote-Count/issues>
140              
141             AUTHOR
142              
143             John Karr (BRAINBUZ) brainbuz@cpan.org
144              
145             CONTRIBUTORS
146              
147             Copyright 2019-2021 by John Karr (BRAINBUZ) brainbuz@cpan.org.
148              
149             LICENSE
150              
151             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>.
152              
153             SUPPORT
154              
155             This software is provided as is, per the terms of the GNU Public License. Professional support and customisation services are available from the author.
156              
157             =cut
158