File Coverage

blib/lib/Vote/Count/Common.pm
Criterion Covered Total %
statement 106 107 99.0
branch 15 16 93.7
condition n/a
subroutine 22 22 100.0
pod 9 12 75.0
total 152 157 96.8


line stmt bran cond sub pod time code
1 39     39   28991 use strict;
  39         93  
  39         1264  
2 39     39   206 use warnings;
  39         177  
  39         1040  
3 39     39   717 use 5.024;
  39         132  
4              
5             package Vote::Count::Common;
6 39     39   204 use Moose::Role;
  39         72  
  39         362  
7              
8 39     39   209283 use feature qw /postderef signatures/;
  39         101  
  39         3949  
9 39     39   256 no warnings 'experimental';
  39         96  
  39         2172  
10              
11 39     39   240 use Storable 3.15 'dclone';
  39         751  
  39         2157  
12 39     39   245 use Path::Tiny;
  39         90  
  39         46678  
13              
14             # ABSTRACT: Role shared by Count and Matrix for common functionality. See Vote::Count Documentation.
15              
16             our $VERSION='2.01';
17              
18             =head1 NAME
19              
20             Vote::Count::Common
21              
22             =head1 VERSION 2.01
23              
24             =head1 Synopsis
25              
26             This Role is consumed by Vote::Count and Vote::Count::Matrix. It provides common methods for the Active Set.
27              
28             =cut
29              
30             has 'BallotSet' => ( is => 'ro', isa => 'HashRef', required => 1 );
31              
32             has 'Active' => (
33             is => 'ro',
34             isa => 'HashRef',
35             lazy => 1,
36             builder => '_defaultactive',
37             );
38              
39             has 'VoteValue' => (
40             is => 'ro',
41             isa => 'Int',
42             default => 1,
43             );
44              
45             has 'WithdrawalList' => (
46             is => 'rw',
47             isa => 'Str',
48             required => 0,
49             );
50              
51             has 'PairMatrix' => (
52             is => 'ro',
53             isa => 'Object',
54             lazy => 1,
55             builder => '_buildmatrix',
56             );
57              
58 56     56   122 sub _buildmatrix ($self) {
  56         123  
  56         103  
59 56 100       1592 my $tiebreak =
60             defined( $self->TieBreakMethod() )
61             ? $self->TieBreakMethod()
62             : 'none';
63 56         1360 my %args = (
64             BallotSet => $self->BallotSet(),
65             Active => $self->Active(),
66             TieBreakMethod => $tiebreak,
67             LogTo => $self->LogTo() . '_matrix',
68             );
69 56 100       1534 $args{'PrecedenceFile'} = $self->PrecedenceFile() if $self->PrecedenceFile();
70 56         1513 $args{'TieBreakerFallBackPrecedence'} = $self->TieBreakerFallBackPrecedence();
71 56         1799 return Vote::Count::Matrix->new( %args );
72             }
73              
74 2     2 1 3 sub UpdatePairMatrix ($self) {
  2         5  
  2         2  
75 2         8 $self->{'PairMatrix'} = $self->_buildmatrix();
76             }
77              
78 56     56 0 6190 sub GetChoices ( $self ) {
  56         98  
  56         93  
79 56         1676 return sort keys( $self->BallotSet()->{'choices'}->%* );
80             }
81              
82 153     153   256 sub _defaultactive ( $self ) {
  153         252  
  153         219  
83 153         3682 my $active = dclone $self->BallotSet()->{'choices'} ;
84 153 100       4904 if ( $self->WithdrawalList ) {
85 2         46 for my $w (path( $self->WithdrawalList )->lines({ chomp => 1})) {
86 8         472 delete $active->{$w};
87             }
88             }
89 153         3545 return $active;
90             }
91              
92 35     35 1 3294 sub SetActive ( $self, $active ) {
  35         59  
  35         55  
  35         54  
93             # Force deref
94 35         1422 $self->{'Active'} = dclone $active;
95             # if there is a child PairMatrix, update it too.
96 35 100       203 if ( defined $self->{'PairMatrix'} ) {
97 8         34 $self->{'PairMatrix'}{'Active'} = $self->{'Active'};
98             }
99             }
100              
101 1     1 1 2 sub ResetActive ( $self ) {
  1         2  
  1         1  
102 1         3 $self->{'Active'} = $self->_defaultactive();
103             }
104              
105             # I was typing the equivalent too often. made a method.
106 7     7 1 401 sub SetActiveFromArrayRef ( $self, $active ) {
  7         12  
  7         12  
  7         16  
107 7         21 $self->SetActive( { map { $_ => 1 } $active->@* } );
  29         76  
108             }
109              
110 230     230 1 431 sub GetActive ( $self ) {
  230         423  
  230         358  
111             # Force deref
112 230         8393 my $active = $self->Active();
113 230         8681 return dclone $active;
114             }
115              
116             # this deref also happens a lot
117 205     205 1 696 sub GetActiveList( $self ) {
  205         334  
  205         295  
118 205         5341 return ( sort( keys( $self->Active->%* ) ) );
119             }
120              
121 13     13 1 4898 sub Defeat ( $self, $choice ) {
  13         25  
  13         27  
  13         24  
122 13         48 delete $self->{'Active'}{$choice};
123             }
124              
125 44     44 1 96 sub VotesCast ( $self ) {
  44         111  
  44         66  
126 44         1062 return $self->BallotSet()->{'votescast'};
127             }
128              
129 6     6 1 13 sub VotesActive ( $self ) {
  6         10  
  6         8  
130 6 50       170 unless ( $self->BallotSet()->{'options'}{'rcv'} ) {
131 0         0 die "VotesActive Method only supports rcv";
132             }
133 6         128 my $set = $self->BallotSet();
134 6         141 my $active = $self->Active();
135 6         11 my $activeCount = 0;
136             LOOPVOTESACTIVE:
137 6         17 for my $B ( values $set->{ballots}->%* ) {
138 36         59 for my $V ( $B->{'votes'}->@* ) {
139 39 100       68 if ( defined $active->{$V} ) {
140 33         41 $activeCount += $B->{'count'};
141 33         48 next LOOPVOTESACTIVE;
142             }
143             }
144             }
145 6         17 return $activeCount;
146             }
147              
148 48     48 0 1422 sub BallotSetType ( $self ) {
  48         80  
  48         76  
149 48 100       1122 if ( $self->BallotSet()->{'options'}{'rcv'} ) {
    100          
150 42         200 return 'rcv';
151             }
152             elsif ( $self->BallotSet()->{'options'}{'range'} ) {
153 5         31 return 'range';
154             }
155             else {
156 1         13 die "BallotSetType is undefined or unknown type.";
157             }
158             }
159              
160 96     96 0 3745 sub GetBallots ( $self ) {
  96         155  
  96         138  
161 96         2496 return $self->BallotSet()->{'ballots'};
162             }
163              
164             1;
165              
166             =head1 Usage
167              
168             This role is consumed by Vote::Count and Vote::Count::Matrix, providing a common set of functions to all Vote::Count objects.
169              
170             =head1 new
171              
172             The only required parameter is BallotSet. The BallotSet is provided by L<Vote::Count::ReadBallots>, you may place the BallotSet in a variable or more typically read it from within the new method.
173              
174             use Vote::Count;
175             use Vote::Count::ReadBallots;
176             my $Election = Vote::Count->new( BallotSet => read_ballots( $ballotfile ) );
177              
178             =head1 Optional Paramters to Vote::Count
179              
180             =head2 Active
181              
182             Sets the Active Set at creation. See L<Active Sets>.
183              
184             =head2 VoteValue
185              
186             Sets a VoteValue for use in weighted systems like STV. The default value is 1. Approval and TopCount are aware of VoteValue for RCV ballots.
187              
188             =head2 LogTo
189              
190             Sets a path and Naming pattern for writing logs with the WriteLogs method. If LogTo is not provided the default location is I</tmp/votecount>.
191              
192             'LogTo' => '/logging_path/election_name'
193              
194             See L<Vote::Count::Log> for more informationand and additional log path options.
195              
196             =head1 Logging Methods
197              
198             Vote::Count writes 3 logs: B<t>erse/brief, B<v>erbose, and B<d>etail. The three logging methods I<logt>, I<logv>, and I<logd> are used to record messages to those respective logs. The WriteLogs method is used to write the logs to disk.
199              
200             For more detail, see L<Vote::Count::Log>.
201              
202             =head1 Active Sets
203              
204             Active sets are a Hash Reference where the keys represent the active choices and the value is true. The VoteCount Object contains an Active Set which can be Accessed via the Active() method which will return a reference to the Active Set (changing the reference will change the active set). The GetActive and SetActive methods break the reference links. GetActiveList returns the Active Set as a sorted list.
205              
206             Many Components will take an argument for $activeset or default to the current Active set of the Vote::Count object, which is defaulted to the Choices defined in the BallotSet.
207              
208             =head2 Active
209              
210             Get Active Set as HashRef to the active set. Changing the new HashRef will change the internal Active Set, GetActive is often preferable as it will return a HashRef that is a copy instead. Active may be used as an optional parameter to L<new|"Optional Paramters to Vote::Count">.
211              
212             =head2 GetActive
213              
214             Returns a hashref containing a copy of the Active Set.
215              
216             =head2 GetActiveList
217              
218             Returns a simple array of the members of the Active Set.
219              
220             =head2 ResetActive
221              
222             Sets the Active Set to the full choices list of the BallotSet.
223              
224             =head2 SetActive
225              
226             Sets the Active Set to provided HashRef. The values to the hashref should evaluate as True.
227              
228             =head2 SetActiveFromArrayRef
229              
230             Same as SetActive except it takes an ArrayRef of the choices to be set as Active.
231              
232             =head1 Vote::Count Methods
233              
234             Most of these are provided by the Role Common and available directly in both Matrix objects and Vote::Count Objects. Vote::Count objects create a child Matrix object: PairMatrix.
235              
236             =head2 BallotSet
237              
238             Get BallotSet
239              
240             =head2 PairMatrix
241              
242             Get a Matrix Object for the Active Set. Generated and cached on the first request.
243              
244             =head2 UpdatePairMatrix
245              
246             Regenerate and cache Matrix with current Active Set.
247              
248             =head2 VotesCast
249              
250             Returns the number of votes cast.
251              
252             =head2 VotesActive
253              
254             Returns the number of non-exhausted ballots based on the current Active Set.
255              
256             =head2 new
257              
258             Has the following Attributes:
259              
260             =head2 WithdrawalList
261              
262             A text file containing choices 1 per line that are withdrawn. Use when a choice may be included in the ballots but should be treated as not-present. Removing a choice from the choices list in a Ballot File will generate an exception from ReadBallots if it appears on any Ballots. Withdrawing a choice will exclude it from the Active Set if it is present in the Ballots.
263              
264             =head2 Choices
265              
266             Returns an array of all of the Choices in the Ballot Set.
267              
268             =head2 GetActiveList
269              
270             Returns a simple array of the members of the Active Set.
271              
272             =head2 ResetActive
273              
274             Sets the Active Set to the full choices list of the BallotSet.
275              
276             =head2 SetActive
277              
278             Sets the Active Set to provided HashRef. The values to the hashref should evaluate as True.
279              
280             =head2 SetActiveFromArrayRef
281              
282             Same as SetActive except it takes an ArrayRef of the choices to be set as Active.
283              
284             =head2 Defeat
285              
286             Remove $choice from current Active List.
287              
288             $Election->Defeat( $choice );
289             $Election->logv( "Defeated $choice");
290              
291             =cut
292              
293             #FOOTER
294              
295             =pod
296              
297             BUG TRACKER
298              
299             L<https://github.com/brainbuz/Vote-Count/issues>
300              
301             AUTHOR
302              
303             John Karr (BRAINBUZ) brainbuz@cpan.org
304              
305             CONTRIBUTORS
306              
307             Copyright 2019-2021 by John Karr (BRAINBUZ) brainbuz@cpan.org.
308              
309             LICENSE
310              
311             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>.
312              
313             SUPPORT
314              
315             This software is provided as is, per the terms of the GNU Public License. Professional support and customisation services are available from the author.
316              
317             =cut
318