File Coverage

blib/lib/Vote/Count/Start.pm
Criterion Covered Total %
statement 119 132 90.1
branch 14 22 63.6
condition n/a
subroutine 21 22 95.4
pod 0 1 0.0
total 154 177 87.0


line stmt bran cond sub pod time code
1             package Vote::Count::Start;
2              
3 1     1   229061 use 5.024;
  1         8  
4 1     1   6 use strict;
  1         2  
  1         19  
5 1     1   3 use warnings;
  1         2  
  1         38  
6 1     1   5 use feature qw/postderef signatures/;
  1         1  
  1         76  
7 1     1   6 no warnings qw/experimental/;
  1         1  
  1         35  
8 1     1   740 use Path::Tiny 0.108;
  1         10267  
  1         64  
9 1     1   12 use Carp;
  1         2  
  1         46  
10 1     1   639 use Try::Tiny;
  1         1797  
  1         58  
11             # use Data::Dumper;
12             # use Vote::Count::Method::CondorcetDropping;
13 1     1   441 use Vote::Count;
  1         3  
  1         37  
14 1     1   509 use Vote::Count::ReadBallots 'read_ballots';
  1         2  
  1         80  
15              
16             our $VERSION='2.01';
17              
18             =head1 NAME
19              
20             Vote::Count::Start
21              
22             =head1 VERSION 2.01
23              
24             =cut
25              
26             # ABSTRACT: Vote::Count Common Setup
27              
28             =head1 SYNOPSIS
29              
30             use Vote::Count::Start;
31              
32             my $Election = StartElection(
33             BallotFile => $filepath,
34             FloorRule => 'TopCount',
35             FloorValue => 2,
36             LogPath -> '/some/path',
37             ...
38             );
39              
40             $Election->WriteLog();
41              
42             =head1 Description
43              
44             Does common startup steps useful accross methods. It includes a lot of the boiler plate for common usage. Use for resolving elections where the rules don't require customization, or as an example for writing more customized methods.
45              
46             =over
47              
48             * Reads Ballots from a file/path
49              
50             * Calculates and logs Top Count
51              
52             * Calculates and logs Approval
53              
54             * Applies a Floor Rule
55              
56             * Calculatures and logs a Borda Count
57              
58             * Generates a Condorcet Matrix and logs the Win/Loss Summary and the Scores
59              
60             * Conducts IRV (default options) and logs the result
61              
62             * Returns a Vote::Count Object
63              
64             =back
65              
66             =head1 Method StartElection
67              
68             Returns a Vote::Count object performing the above operations.
69              
70             =head2 Parameter BallotSet or BallotFile
71              
72             It is mandatory to provide either a reference to a BallotSet or to provide a BallotFile for ReadBallots to create a BallotSet.
73              
74             =head2 Paramater FloorRule, FloorValue (optional)
75              
76             A FloorRule and optional value (see Vote::Count::Floor). If no FloorRule is provide none will be used.
77              
78             =head2 Other Options
79              
80             Any other option to Vote::Count can just be passed in the arguments list
81              
82             =cut
83              
84 1     1   7 use Exporter::Easy ( EXPORT => ['StartElection'] );
  1         2  
  1         4  
85              
86             # checks for ballotfile and updates the ballotset in
87             # args. no return value because %ARGS is passed by reference
88             # and updated directly if needed.
89 2     2   4 sub _ballotset( $ARGS ) {
  2         3  
  2         4  
90 2 100       10 if ( $ARGS->{'BallotFile'} ) {
91 1         6 $ARGS->{'BallotSet'} = read_ballots $ARGS->{'BallotFile'};
92             }
93             # If
94 2 50       8 unless ( defined( $ARGS->{'BallotSet'}{'choices'} ) ) {
95             croak "A Valid BallotSet or BallotFile was not provided "
96 0         0 . $ARGS->{'BallotFile'} . "\n";
97             }
98             }
99              
100 2     2   5 sub _dofloor ( $self, %ARGS ) {
  2         3  
  2         7  
  2         2  
101 2 100       7 unless ( defined $ARGS{'FloorRule'} ) {
102 1         22 return $self->Active();
103             }
104 1         5 $self->logv(''); # log a blank line.
105 1         3 my $flr = $ARGS{'FloorRule'};
106 1         2 my $floorset = {};
107 1 50       6 if ( $flr eq 'TopCount' ) {
    50          
    50          
108 0         0 $floorset = $self->TopCountFloor( $ARGS{'FloorValue'} );
109             }
110             elsif ( $flr eq 'TCA' ) {
111 0         0 $floorset = $self->TCA();
112             }
113             elsif ( $flr eq 'Approval' ) {
114 1         8 $floorset = $self->ApprovalFloor( $ARGS{'FloorValue'} );
115             }
116             else {
117 0         0 croak "Undefined Floor rule $flr.\n";
118             }
119 1         4 $self->logv(''); # add blank line to output
120 1         3 return $floorset;
121             }
122              
123 2     2   3 sub _do_plurality ( $Election ) {
  2         5  
  2         2  
124 2         10 my $Plurality = $Election->TopCount();
125 2         9 $Election->logv(
126             ' ',
127             'Initial Top Count (Plurality)',
128             $Plurality->RankTable()
129             );
130 2         11 my $PluralityTop = $Plurality->Leader();
131 2 50       6 if ( $PluralityTop->{'winner'} ) {
132 2         12 $Election->logt( "Plurality Winner: " . $PluralityTop->{'winner'} );
133 2         20 return $PluralityTop->{'winner'};
134             }
135             else {
136             $Election->logt(
137 0         0 "Plurality Tie: " . join( ', ', $PluralityTop->{'tied'}->@* ) );
138 0         0 return '';
139             }
140             }
141              
142 2     2   4 sub _do_approval ( $Election ) {
  2         4  
  2         3  
143 2         10 my $Approval = $Election->Approval();
144 2         5 $Election->logv( "\nApproval", $Approval->RankTable() );
145 2         10 my $AWinner = $Approval->Leader();
146 2 50       16 if ( $AWinner->{'winner'} ) {
147 0         0 $Election->logt( "Approval Winner: " . $AWinner->{'winner'} );
148 0         0 return $AWinner->{'winner'};
149             }
150             else {
151             $Election->logt(
152 2         14 "Approval Tie: " . join( ', ', $AWinner->{'tied'}->@* ) );
153 2         14 return '';
154             }
155             }
156              
157 2     2   3 sub _do_borda ( $Election ) {
  2         4  
  2         2  
158 2         8 my $Borda = $Election->Approval();
159 2         9 $Election->logv( "Borda Count", $Borda->RankTable(), );
160 2         10 my $AWinner = $Borda->Leader();
161 2 50       6 if ( $AWinner->{'winner'} ) {
162 0         0 $Election->logt( "Borda Winner: " . $AWinner->{'winner'}, '' );
163 0         0 return $AWinner->{'winner'};
164             }
165             else {
166 2         13 $Election->logt( "Borda Tie: " . join( ', ', $AWinner->{'tied'}->@* ),
167             '' );
168 2         14 return '';
169             }
170             }
171              
172 2     2   5 sub _do_majority( $Election) {
  2         2  
  2         4  
173 2         12 my $majority = $Election->TopCountMajority();
174 2 50       9 if ( $majority->{'winner'} ) {
175 0         0 $Election->logv( "Majority Winner: " . $majority->{'winner'} );
176 0         0 return $majority->{'winner'};
177             }
178 2         8 else { return ''; }
179             }
180              
181 2     2   5 sub _do_matrix( $Election) {
  2         3  
  2         2  
182 2         47 my $matrix = $Election->PairMatrix();
183 2         8 $Election->logv(
184             "Pairing Results:",
185             $matrix->MatrixTable(),
186             "\nSmith Set: " . join( ', ', sort( keys $matrix->SmithSet()->%* ) )
187             );
188 2 100       14 if ( $matrix->CondorcetWinner() ) {
189 1         4 $Election->logv( "Condoret Winner: " . $matrix->CondorcetWinner() );
190 1         4 return $matrix->CondorcetWinner();
191             }
192 1         5 else { return '' }
193             }
194              
195 2     2   4 sub _do_irv ( $Election, $floorset ) {
  2         4  
  2         2  
  2         4  
196 2     2   210 my $IRVResult = try { $Election->RunIRV() }
197 2     0   21 catch { croak "RunIRV exploded" };
  0         0  
198             }
199              
200 2     2 0 1316 sub StartElection ( %ARGS ) {
  2         10  
  2         3  
201 2         4 my $winners = {};
202 2         9 _ballotset( \%ARGS );
203 2         4 delete $ARGS{'BallotFile'};
204 2         7 my $FloorValue = delete $ARGS{'FloorValue'};
205 2         4 my $FloorRule = delete $ARGS{'FloorRule'};
206 2         71 my $Election = Vote::Count->new(%ARGS);
207 2         8 $winners->{'plurality'} = _do_plurality($Election);
208 2         8 $winners->{'approval'} = _do_approval($Election);
209 2         9 my $floorset = _dofloor( $Election, 'FloorRule' => $FloorRule, 'FloorValue' => $FloorValue );
210 2         10 $Election->SetActive($floorset);
211 2         8 $winners->{'majority'} = _do_majority($Election);
212 2         6 $winners->{'borda'} = _do_borda($Election);
213 2         6 $winners->{'condorcet'} = _do_matrix($Election);
214 2         9 $winners->{'irv'} = _do_irv( $Election, $floorset );
215             # todo generate a summary from the winners hash.
216 2         53 $Election->{'startdata'} = $winners;
217             # Active gets modified from default, so reset to floorset
218 2         11 $Election->SetActive($floorset);
219 2         10 return ($Election);
220             }
221              
222             #FOOTER
223              
224             =pod
225              
226             BUG TRACKER
227              
228             L<https://github.com/brainbuz/Vote-Count/issues>
229              
230             AUTHOR
231              
232             John Karr (BRAINBUZ) brainbuz@cpan.org
233              
234             CONTRIBUTORS
235              
236             Copyright 2019-2021 by John Karr (BRAINBUZ) brainbuz@cpan.org.
237              
238             LICENSE
239              
240             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>.
241              
242             SUPPORT
243              
244             This software is provided as is, per the terms of the GNU Public License. Professional support and customisation services are available from the author.
245              
246             =cut
247