File Coverage

blib/lib/Vote/Count/Charge.pm
Criterion Covered Total %
statement 309 328 94.2
branch 57 64 89.0
condition 1 3 33.3
subroutine 42 45 93.3
pod 23 27 85.1
total 432 467 92.5


line stmt bran cond sub pod time code
1 12     12   1229463 use strict;
  12         60  
  12         416  
2 12     12   70 use warnings;
  12         32  
  12         341  
3 12     12   254 use 5.024;
  12         56  
4 12     12   65 use feature qw /postderef signatures switch/;
  12         25  
  12         1307  
5              
6             package Vote::Count::Charge;
7 12     12   2901 use namespace::autoclean;
  12         81794  
  12         87  
8 12     12   4227 use Moose;
  12         2500298  
  12         108  
9             extends 'Vote::Count';
10              
11 12     12   95671 no warnings 'experimental::signatures';
  12         33  
  12         625  
12 12     12   90 no warnings 'experimental::smartmatch';
  12         25  
  12         459  
13              
14 12     12   4460 use Sort::Hash;
  12         6290  
  12         659  
15 12     12   4261 use Data::Dumper;
  12         41380  
  12         910  
16 12     12   8481 use Time::Piece;
  12         123333  
  12         72  
17 12     12   6790 use Path::Tiny;
  12         71662  
  12         756  
18 12     12   104 use Carp;
  12         32  
  12         735  
19 12     12   4515 use JSON::MaybeXS;
  12         48148  
  12         786  
20 12     12   3779 use YAML::XS;
  12         21571  
  12         4918  
21             # use Storable 3.15 'dclone';
22              
23             our $VERSION='2.00';
24              
25             has 'Seats' => (
26             is => 'ro',
27             isa => 'Int',
28             required => 1,
29             );
30              
31             has 'FloorRule' => (
32             is => 'rw',
33             isa => 'Str',
34             default => '',
35             );
36              
37             has 'FloorThresshold' => (
38             is => 'ro',
39             isa => 'Num',
40             default => 0,
41             );
42              
43             my @choice_valid_states =
44             qw( elected pending defeated withdrawn active suspended );
45              
46 33     33   70 sub _init_choice_status ( $I ) {
  33         67  
  33         57  
47 33         120 $I->{'choice_status'} = {};
48 33         114 $I->{'pending'} = [];
49 33         92 $I->{'elected'} = [];
50 33         129 $I->{'suspended'} = [];
51 33         96 $I->{'deferred'} = [];
52 33         83 $I->{'stvlog'} = [];
53 33         125 $I->{'stvround'} = 0;
54 33         292 for my $c ( $I->GetChoices() ) {
55 284         838 $I->{'choice_status'}->{$c} = {
56             state => 'hopeful',
57             votes => 0,
58             };
59             }
60 33 100       965 if ( $I->WithdrawalList ) {
61 1         26 for my $w (path( $I->WithdrawalList )->lines({ chomp => 1})) {
62 4 100       267 $I->Withdraw($w) if defined $I->{'choice_status'}{$w};
63             }
64             }
65             }
66              
67             # Default tie breaking to Precedence,
68             # Force Precedence as fallback, and generate reproducible precedence
69             # file if one isn't provided.
70 33     33   64 sub _setTieBreaks ( $I ) {
  33         64  
  33         58  
71 12     12   120 no warnings 'uninitialized';
  12         27  
  12         44014  
72 33 100       1037 unless ( $I->TieBreakMethod() ) {
73 31         203 $I->logd('TieBreakMethod is undefined, setting to precedence');
74 31         855 $I->TieBreakMethod('precedence');
75             }
76 33 100       934 if ( $I->TieBreakMethod ne 'precedence' ) {
77 1         26 $I->logv( 'Ties will be broken by: '
78             . $I->TieBreakMethod
79             . ' with a fallback of precedence' );
80 1         38 $I->TieBreakerFallBackPrecedence(1);
81             }
82 33 100       949 unless ( stat $I->PrecedenceFile ) {
83 32         271 my @order = $I->CreatePrecedenceRandom('/tmp/precedence.txt');
84 32         1464 $I->PrecedenceFile('/tmp/precedence.txt');
85 32         361 $I->logv( "Order for Random Tie Breakers is: " . join( ", ", @order ) );
86             }
87             }
88              
89 34     34 1 69 sub ResetVoteValue ($I) {
  34         66  
  34         60  
90 34         167 my $ballots = $I->GetBallots();
91 34         2211 for my $b ( keys $ballots->%* ) {
92 10522         274448 $ballots->{$b}->{'votevalue'} = $I->VoteValue();
93 10522         18965 $ballots->{$b}->{'topchoice'} = undef;
94             }
95             }
96              
97 0     0 1 0 sub SeatsOpen ($I) { $I->Seats() - $I->Elected() }
  0         0  
  0         0  
  0         0  
98              
99             sub BUILD {
100 34     34 0 118 my $self = shift;
101 34 100       230 unless ( $self->BallotSetType() eq 'rcv' ) {
102 1         266 croak "Charge only supports rcv Ballot Type";
103             }
104 33         167 $self->_setTieBreaks();
105 33         189 $self->ResetVoteValue();
106 33         849 $self->_init_choice_status();
107 33         1146 $self->FloorRounding('down');
108             }
109              
110             =pod
111              
112             CountAbandoned
113              
114             =cut
115              
116 28     28 0 3246 sub CountAbandoned ($I) {
  28         58  
  28         46  
117 28         129 my @continuing = ( $I->Deferred(), $I->GetActiveList );
118 28         137 my $set = $I->GetBallots();
119 28         152 my %res = ( count_abandoned => 0, value_abandoned => 0, );
120 28         4097 for my $k ( keys $set->%* ) {
121 17412 50       45473 if ( $set->{$k}{'votevalue'} == 0 ) {
122 0         0 $res{count_abandoned} += $set->{$k}{'count'};
123 0         0 next;
124             }
125 17412         22861 my $continue = 0;
126 17412         25733 for my $c (@continuing) {
127 82806         906349 $continue += ( grep /$c/, $set->{$k}{'votes'}->@* );
128             }
129 17412 100       45694 unless ($continue) {
130 631         1434 $res{count_abandoned} += $set->{$k}{'count'};
131 631         1374 $res{value_abandoned} += $set->{$k}{'count'} * $set->{$k}{'votevalue'};
132             }
133             }
134             $res{message} =
135 28         1823 "Votes with no Choice left: $res{count_abandoned}, Value: $res{value_abandoned}";
136 28         344 return \%res;
137             }
138              
139 29     29 1 5261 sub GetChoiceStatus ( $I, $choice = 0 ) {
  29         58  
  29         67  
  29         48  
140 29 100       93 if ($choice) { return $I->{'choice_status'}{$choice} }
  24         142  
141 5         28 else { return $I->{'choice_status'} }
142             }
143              
144 12     12 1 38 sub SetChoiceStatus ( $I, $choice, $status ) {
  12         33  
  12         28  
  12         41  
  12         22  
145 12 100       63 if ( $status->{'state'} ) {
146 2 50       77 unless ( grep ( /^$status->{'state'}$/, @choice_valid_states ) ) {
147 0         0 croak "invalid state *$status->{'state'}* assigned to choice $choice";
148             }
149 2         9 $I->{'choice_status'}->{$choice}{'state'} = $status->{'state'};
150             }
151 12 100       46 if ( $status->{'votes'} ) {
152 11         48 $I->{'choice_status'}->{$choice}{'votes'} = int $status->{'votes'};
153             }
154             }
155              
156 3     3 1 10 sub VCUpdateActive ($I) {
  3         7  
  3         4  
157 3         8 my $active = {};
158 3         11 for my $k ( keys $I->GetChoiceStatus()->%* ) {
159 24 100       49 $active->{$k} = 1 if $I->{'choice_status'}->{$k}{'state'} eq 'hopeful';
160 24 100       52 $active->{$k} = 1 if $I->{'choice_status'}->{$k}{'state'} eq 'pending';
161             }
162 3         27 $I->SetActive($active);
163             }
164              
165 39     39 1 3511 sub Elect ( $I, $choice ) {
  39         69  
  39         75  
  39         62  
166 39         107 delete $I->{'Active'}{$choice};
167 39         131 $I->{'choice_status'}->{$choice}{'state'} = 'elected';
168 39         197 $I->{'pending'} = [ grep ( !/^$choice$/, $I->{'pending'}->@* ) ];
169 39         116 push $I->{'elected'}->@*, $choice;
170 39         240 return $I->{'elected'}->@*;
171             }
172              
173 21     21 1 80 sub Elected ($I) { return $I->{'elected'}->@* }
  21         46  
  21         132  
  21         138  
174              
175 23     23 1 4384 sub Defeat ( $I, $choice ) {
  23         44  
  23         105  
  23         46  
176 23         61 delete $I->{'Active'}{$choice};
177 23         89 $I->{'choice_status'}->{$choice}{'state'} = 'defeated';
178             }
179              
180 0     0 1 0 sub Defeated ($I) {
  0         0  
  0         0  
181 0         0 my @defeated = ();
182 0         0 for my $c ( keys $I->{'choice_status'}->%* ) {
183 0 0       0 if ( $I->{'choice_status'}{$c}{'state'} eq 'defeated') {
184 0         0 push @defeated, $c;
185             }
186             }
187 0         0 return sort(@defeated);
188             }
189              
190 12     12 1 559 sub Withdrawn ($I) {
  12         20  
  12         14  
191 12         25 my @withdrawn = ();
192 12         46 for my $c ( keys $I->{'choice_status'}->%* ) {
193 128 100       255 if ( $I->{'choice_status'}{$c}{'state'} eq 'withdrawn') {
194 25         43 push @withdrawn, $c;
195             }
196             }
197 12         64 return sort(@withdrawn);
198             }
199              
200 9     9 1 18 sub Withdraw ( $I, $choice ) {
  9         13  
  9         16  
  9         12  
201 9         18 delete $I->{'Active'}{$choice};
202 9         20 $I->{'choice_status'}->{$choice}{'state'} = 'withdrawn';
203 9         23 return $I->Withdrawn();
204             }
205              
206 8     8 1 38 sub Suspend ( $I, $choice ) {
  8         16  
  8         17  
  8         15  
207 8         21 delete $I->{'Active'}{$choice};
208 8         22 $I->{'choice_status'}->{$choice}{'state'} = 'suspended';
209 8 100       131 unless ( grep ( /^$choice$/, $I->{'suspended'}->@* ) ) {
210 7         22 push $I->{'suspended'}->@*, $choice;
211             }
212 8         40 return $I->Suspended();
213             }
214              
215 11     11 1 21 sub Suspended ($I ) {
  11         20  
  11         16  
216 11         57 return $I->{'suspended'}->@*;
217             }
218              
219 2     2 1 6 sub Defer ( $I, $choice ) {
  2         6  
  2         5  
  2         5  
220 2         7 delete $I->{'Active'}{$choice};
221 2         21 $I->{'choice_status'}->{$choice}{'state'} = 'deferred';
222 2 50       16 unless ( grep ( /^$choice$/, $I->{'deferred'}->@* ) ) {
223 2         9 push $I->{'deferred'}->@*, $choice;
224             }
225 2         10 return $I->Deferred();
226             }
227              
228 31     31 1 58 sub Deferred ($I ) {
  31         60  
  31         56  
229 31         167 return $I->{'deferred'}->@*;
230             }
231              
232 2     2 1 6 sub Pending ( $I, $choice = undef ) {
  2         5  
  2         4  
  2         5  
233 2 100       8 if ($choice) {
234 1 50       5 unless ( grep /^$choice$/, $I->{'pending'}->@* ) {
235 1         4 $I->{'choice_status'}->{$choice}{'state'} = 'pending';
236 1         3 push $I->{'pending'}->@*, $choice;
237 1         3 delete $I->{'Active'}{$choice};
238             }
239             }
240 2         32 return $I->{'pending'}->@*;
241             }
242              
243 4     4 1 12 sub Reinstate ( $I, @choices ) {
  4         6  
  4         11  
  4         7  
244             # if no choices are given reinstate all.
245 4 100       17 @choices = ($I->{'suspended'}->@*, $I->{'deferred'}->@* ) unless @choices;
246 4         10 my @reinstated = ();
247             REINSTATELOOP:
248 4         8 for my $choice (@choices) {
249             # I'm a fan of the give/when construct, but go to lengths not to use it
250             # because of past issues and that after 15 years it is still experimental.
251 6         16 given ($I->{'choice_status'}{$choice}{'state'}){
252 6         24 when ( 'suspended') { }
253 2         7 when ( 'deferred' ) { }
254 1         2 default { next REINSTATELOOP }
  1         4  
255             };
256 5         112 ($I->{'suspended'}->@*) = grep ( !/^$choice$/, $I->{'suspended'}->@* );
257 5         39 ($I->{'deferred'}->@*) = grep ( !/^$choice$/, $I->{'deferred'}->@* );
258 5         13 $I->{'choice_status'}->{$choice}{'state'} = 'hopeful';
259 5         13 $I->{'Active'}{$choice} = 1;
260 5         13 push @reinstated, $choice;
261             }
262 4         18 return @reinstated;
263             }
264              
265 22     22 1 4962 sub Charge ( $I, $choice, $quota, $charge=$I->VoteValue() ) {
  22         58  
  22         46  
  22         40  
  22         302  
  22         33  
266 22         44 my $charged = 0;
267 22         42 my $surplus = 0;
268 22         46 my @ballotschrgd = ();
269 22         41 my $cntchrgd = 0;
270 22         666 my $active = $I->Active();
271 22         550 my $ballots = $I->BallotSet()->{'ballots'};
272             # warn Dumper $ballots;
273             CHARGECHECKBALLOTS:
274 22         2868 for my $B ( keys $ballots->%* ) {
275 11820 100       25586 next CHARGECHECKBALLOTS if ( $I->TopChoice($B) ne $choice );
276 2501         4042 my $ballot = $ballots->{$B};
277 2501 100       5725 if ( $charge == 0 ) {
    100          
278 1         4 $charged += $ballot->{'votevalue'} * $ballot->{'count'};
279 1         2 $ballot->{'charged'}{$choice} = $ballot->{'votevalue'};
280 1         4 $ballot->{'votevalue'} = 0;
281             }
282             elsif ( $ballot->{'votevalue'} >= $charge ) {
283 2344         3562 my $over = $ballot->{'votevalue'} - $charge;
284 2344         3983 $charged += ( $ballot->{'votevalue'} - $over ) * $ballot->{'count'};
285 2344         3235 $ballot->{'votevalue'} -= $charge;
286 2344         5537 $ballot->{'charged'}{$choice} = $charge;
287             }
288             else {
289 156         296 $charged += $ballot->{'votevalue'} * $ballot->{'count'};
290 156         345 $ballot->{'charged'}{$choice} = $ballot->{'votevalue'};
291 156         248 $ballot->{'votevalue'} = 0;
292             }
293 2501         4886 push @ballotschrgd, $B;
294 2501         4146 $cntchrgd += $ballot->{'count'};
295             }
296 22         1109 $I->{'choice_status'}->{$choice}{'votes'} += $charged;
297 22         78 $surplus = $I->{'choice_status'}->{$choice}{'votes'} - $quota;
298 22         69 $I->{'choice_status'}->{$choice}{'votes'} = $charged;
299             return (
300             {
301 22         235 choice => $choice,
302             surplus => $surplus,
303             ballotschrgd => \@ballotschrgd,
304             cntchrgd => $cntchrgd,
305             quota => $quota
306             }
307             );
308             }
309              
310 24     24 1 213 sub STVEvent ( $I, $data = 0 ) {
  24         45  
  24         54  
  24         51  
311 24 100       858 return $I->{'stvlog'} unless $data;
312 19         94 push $I->{'stvlog'}->@*, $data;
313             }
314              
315 2     2 1 480 sub WriteSTVEvent( $I) {
  2         28  
  2         7  
316 2         82 my $jsonpath = $I->LogTo . '_stvevents.json';
317 2         53 my $yamlpath = $I->LogTo . '_stvevents.yaml';
318             # my $yaml = ;
319 2         16 my $coder = JSON->new->ascii->pretty;
320 2         44 path($jsonpath)->spew( $coder->encode( $I->STVEvent() ) );
321 2         991 path($yamlpath)->spew( Dump $I->STVEvent() );
322             }
323              
324 0     0 1 0 sub STVRound($I) { return $I->{'stvround'} }
  0         0  
  0         0  
  0         0  
325              
326 12     12 1 27 sub NextSTVRound( $I) { return ++$I->{'stvround'} }
  12         24  
  12         22  
  12         37  
327              
328 2     2 0 10 sub TCStats( $I ) {
  2         4  
  2         96  
329 2         11 my $tc = $I->TopCount;
330 2         19 $tc->{'total_votes'} = $I->VotesCast;
331 2         60 $tc->{'total_vote_value'} = $tc->{'total_votes'} * $I->VoteValue;
332 2         10 $tc->{'abandoned'} = $I->CountAbandoned;
333             $tc->{'active_vote_value'} =
334 2         7 $tc->{'total_vote_value'} - $tc->{'abandoned'}{'value_abandoned'};
335 2         16 return $tc;
336             }
337              
338 2     2 0 17 sub STVFloor ( $I, $action='Withdraw' ) {
  2         3  
  2         4  
  2         4  
339 2 50 33     52 if ( $I->FloorRule() && $I->FloorThresshold() ) {
340 2 100       54 $I->FloorRule('ApprovalFloor') if $I->FloorRule() eq 'Approval';
341 2 100       48 $I->FloorRule('TopCountFloor') if $I->FloorRule() eq 'TopCount';
342 2         5 my @withdrawn =();
343 2         49 my $newactive =
344             $I->ApplyFloor(
345             $I->FloorRule(),
346             $I->FloorThresshold()
347             );
348 2         9 for my $choice (sort $I->GetChoices()) {
349 24 100       45 unless( $newactive->{$choice}) {
350 9         24 $I->$action( $choice );
351 9         15 push @withdrawn, $choice;
352             }
353             }
354 2         13 @withdrawn = sort (@withdrawn);
355 2         4 my $done = $action;
356 2 100       7 $done = 'Withdrawn' if $action eq 'Withdraw';
357 2 100       5 $done = 'Defeated' if $action eq 'Defeat';
358 2         19 return @withdrawn;
359             }
360             }
361              
362 9     9 1 52 sub SetQuota ($I, $style='droop') {
  9         18  
  9         20  
  9         15  
363 9         35 my $abandoned = $I->CountAbandoned();
364 9         23 my $abndnvotes = $abandoned->{'value_abandoned'};
365 9         365 my $cast = $I->BallotSet->{'votescast'};
366 9         301 my $numerator = ( $cast * $I->VoteValue ) - $abndnvotes;
367 9         249 my $denominator = $I->Seats();
368 9         20 my $adjust = 0;
369 9 100       42 if ( $style eq 'droop' ) {
370 7         18 $denominator++;
371 7         16 $adjust = 1;
372             }
373 9         80 return ( $adjust + int( $numerator / $denominator ) );
374             }
375              
376             =head1 NAME
377              
378             Vote::Count::Charge
379              
380             =head1 VERSION 2.00
381              
382             =cut
383              
384             # ABSTRACT: Vote::Charge - implementation of STV.
385              
386             =pod
387              
388             =head1 SYNOPSIS
389              
390             my $E = Vote::Count::Charge->new(
391             Seats => 3,
392             VoteValue => 1000,
393             BallotSet => read_ballots('t/data/data1.txt', ) );
394              
395             $E->Elect('SOMECHOICE');
396             $E->Charge('SOMECHOICE', $quota, $perCharge );
397             say E->GetChoiceStatus( 'CARAMEL'),
398             > { state => 'withdrawn', votes => 0 }
399              
400             =head1 Vote Charge implementation of Surplus Transfer
401              
402             Vote Charge is how Vote::Count implements Surplus Transfer. The wording is chosen to make the concept more accessible to a general audience. It also uses integer math and imposes truncation as the rounding rule.
403              
404             Vote Charge describes the process of Single Transferable Vote as:
405              
406             The Votes are assigned a value, based on the number of seats and the total value of all of the votes, a cost is determined for electing a choice. The votes supporting that choice are then charged to pay that cost. The remainder of the value for the vote, if any, is available for the next highest choice of the vote.
407              
408             When value is transferred back to the vote, Vote Charge refers to it as a Rebate.
409              
410             Vote Charge uses integer math and truncates all remainders. Setting the Vote Value is equivalent to setting a number of decimal places, a Vote Value of 100,000 is the same as a 5 decimal place precision.
411              
412             =head1 Description
413              
414             This module provides methods that can be shared between Charge implementations and does not present a complete tool for conducting STV elections. Look at the Methods that have been implemented as part of Vote::Count.
415              
416             =head1 Candidate / Choices States
417              
418             Single Transferable Vote rules have more states than Active, Eliminated and Elected. Not all methods need all of the possible states. The SetChoiceStatus method is not linked to the underlying Vote::Count objects Active Set, the action methods: Elect, Defeat, Suspend, Defer, Reinstate, Withdraw do update the Active Set.
419              
420             Active choices are referred to as Hopeful. The normal methods for accessing the Active list are available. Although not prevented from doing so, STV Methods should not directly set the active list, but rely on methods that manipulate candidate state. The VCUpdateActive method will sync the Active set with the STV choice states corresponding to active.
421              
422             =over
423              
424             =item *
425              
426             hopeful: The default active state of a choice.
427              
428             =item *
429              
430             withdrawn: A choice that will be treated as not present.
431              
432             =item *
433              
434             defeated: A choice that will no longer be considered for election.
435              
436             =item *
437              
438             deferred and suspended:
439              
440             A choice that is temporarily removed from consideration. Suspended is treated the same as Defeated, but is eligible for reinstatement. Deferred is removed from the ActiveSet, but treated as present when calculating Quota and Non-Continuing Votes.
441              
442             =item *
443              
444             elected and pending:
445              
446             Elected and Pending choices are removed from the Active Set, but Pending choices are not yet considered elected. The Pending state is available to hold newly elected choices for a method that will not immediately complete processing their election.
447              
448             =back
449              
450             =head3 GetChoiceStatus
451              
452             When called with the argument of a Choice, returns a hashref with the keys 'state' and 'votes'. When called without argument returns a hashref with the Choices as keys, and the values a hashref with the 'state' and 'votes' keys.
453              
454             =head3 SetChoiceStatus
455              
456             Takes the arguments of a Choice and a hashref with the keys 'state' and 'votes'. This method does not keep the underlying active list in Sync. Use either the targeted methods such as Suspend and Defeat or use VCUpdateActive to force the update.
457              
458             =head3 VCUpdateActive
459              
460             Update the ActiveSet of the underlying Vote::Count object to match the set of Choices that are currently 'hopeful' or 'pending'.
461              
462             =head2 Elected and Pending
463              
464             In addition to Elected, there is a Pending State. Pending means a Choice has reached the Quota, but not completed its Charges and Rebates. The distinction is for the benefit of methods that need choices held in pending, both Pending and Elected choices are removed from the active set.
465              
466             =head3 Elect, Elected
467              
468             Set Choice as Elected. Elected returns the list of currently elected choices.
469              
470             =head3 Pending
471              
472             Takes an Choice to set as Pending. Returns the list of Pending Choices.
473              
474             =head2 Eliminated: Withdrawn, Defeated, or Suspended
475              
476             In methods that set the Quota only once, choices eliminated before setting Quota are Withdrawn and may result in null ballots that can be exluded. Choices eliminated after setting Quota are Defeated. Some rules bring eliminated Choices back in later Rounds, Suspended distinguishes those eligible to return.
477              
478             =head3 Defeat, Defer, Withdraw, Suspend
479              
480             Perform the corresponding action for a Choice.
481              
482             $Election->Defeat('MARMALADE');
483              
484             =head3 Defeated, Deferred, Withdrawn, Suspended
485              
486             Returns a list of choices in that state.
487              
488             =head3 Reinstate
489              
490             Will reinstate all currently suspended choices or may be given a list of suspended choices that will be reinstated.
491              
492             =head2 STVRound, NextSTVRound
493              
494             STVRound returns the current Round, NextSTVRound advances the Round Counter and returns the new Round number.
495              
496             =head2 STVEvent
497              
498             Takes a reference as argument to add that reference to an Event History. This needs to be done seperately from logI<x> because STVEvent holds a list of data references instead of readably formatted events.
499              
500             =head2 WriteSTVEvent
501              
502             Writes JSON and YAML logs (path based on LogTo) of the STVEvents.
503              
504             =head2 SetQuota
505              
506             Calculate the Hare or Droop Quota. After the Division the result is rounded down and 1 is added to the result. The default is the Droop Quota, but either C<'hare'> or C<'droop'> may be requested as an optional parameter.
507              
508             my $droopquota = $Election->SetQuota();
509             my $harequota = $Election->SetQuota('hare');
510              
511             The Hare formula is Active Votes divided by number of Seats. Droop adds 1 to the number of seats, and to the result after rounding, resulting in a lower quota. The Droop Quota is the smallest for which it is impossible for more choices than the number of seats to reach the quota.
512              
513             =head2 Charge
514              
515             Charges Ballots for election of choice, parameters are $choice, $quota and $charge (defaults to VoteValue ).
516              
517             =head2 ResetVoteValue
518              
519             Resets all Ballots to their initial Vote Value.
520              
521             =head2 SeatsOpen
522              
523             Calculate and return the number of seats remaining to fill.
524              
525             =cut
526              
527             __PACKAGE__->meta->make_immutable;
528             1;
529              
530             #FOOTER
531              
532             =pod
533              
534             BUG TRACKER
535              
536             L<https://github.com/brainbuz/Vote-Count/issues>
537              
538             AUTHOR
539              
540             John Karr (BRAINBUZ) brainbuz@cpan.org
541              
542             CONTRIBUTORS
543              
544             Copyright 2019-2021 by John Karr (BRAINBUZ) brainbuz@cpan.org.
545              
546             LICENSE
547              
548             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>.
549              
550             SUPPORT
551              
552             This software is provided as is, per the terms of the GNU Public License. Professional support and customisation services are available from the author.
553              
554             =cut
555