File Coverage

blib/lib/Vote/Count/Charge.pm
Criterion Covered Total %
statement 306 327 93.5
branch 52 62 83.8
condition 1 3 33.3
subroutine 42 45 93.3
pod 23 27 85.1
total 424 464 91.3


line stmt bran cond sub pod time code
1 13     13   1140630 use strict;
  13         56  
  13         420  
2 13     13   72 use warnings;
  13         33  
  13         345  
3 13     13   247 use 5.024;
  13         47  
4 13     13   66 use feature qw /postderef signatures switch/;
  13         31  
  13         1421  
5              
6             package Vote::Count::Charge;
7 13     13   2579 use namespace::autoclean;
  13         75248  
  13         79  
8 13     13   3764 use Moose;
  13         2254512  
  13         92  
9             extends 'Vote::Count';
10              
11 13     13   90554 no warnings 'experimental::signatures';
  13         35  
  13         602  
12 13     13   69 no warnings 'experimental::smartmatch';
  13         24  
  13         482  
13              
14 13     13   4135 use Sort::Hash;
  13         5525  
  13         661  
15 13     13   3917 use Data::Dumper;
  13         37539  
  13         788  
16 13     13   7523 use Time::Piece;
  13         109097  
  13         72  
17 13     13   5732 use Path::Tiny;
  13         61859  
  13         731  
18 13     13   82 use Carp;
  13         28  
  13         682  
19 13     13   4051 use JSON::MaybeXS;
  13         46894  
  13         736  
20 13     13   3071 use YAML::XS;
  13         17530  
  13         4746  
21             # use Storable 3.15 'dclone';
22              
23             our $VERSION='2.01';
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 31     31   58 sub _init_choice_status ( $I ) {
  31         83  
  31         299  
47 31         105 $I->{'choice_status'} = {};
48 31         91 $I->{'pending'} = [];
49 31         88 $I->{'elected'} = [];
50 31         71 $I->{'suspended'} = [];
51 31         73 $I->{'deferred'} = [];
52 31         91 $I->{'stvlog'} = [];
53 31         75 $I->{'stvround'} = 0;
54 31         195 for my $c ( $I->GetChoices() ) {
55 268         706 $I->{'choice_status'}->{$c} = {
56             state => 'hopeful',
57             votes => 0,
58             };
59             }
60 31 100       833 if ( $I->WithdrawalList ) {
61 1         27 for my $w (path( $I->WithdrawalList )->lines({ chomp => 1})) {
62 4 100       253 $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 31     31   56 sub _setTieBreaks ( $I ) {
  31         69  
  31         50  
71 13     13   100 no warnings 'uninitialized';
  13         29  
  13         42135  
72 31 50       814 unless ( $I->TieBreakMethod() ) {
73 31         183 $I->logd('TieBreakMethod is undefined, setting to precedence');
74 31         780 $I->TieBreakMethod('precedence');
75             }
76 31 50       786 if ( $I->TieBreakMethod ne 'precedence' ) {
77 0         0 $I->logv( 'Ties will be broken by: '
78             . $I->TieBreakMethod
79             . ' with a fallback of precedence' );
80 0         0 $I->TieBreakerFallBackPrecedence(1);
81             }
82 31 50       812 unless ( stat $I->PrecedenceFile ) {
83 31         242 my @order = $I->CreatePrecedenceRandom('/tmp/precedence.txt');
84 31         754 $I->PrecedenceFile('/tmp/precedence.txt');
85 31         352 $I->logv( "Order for Random Tie Breakers is: " . join( ", ", @order ) );
86             }
87             }
88              
89 32     32 1 53 sub ResetVoteValue ($I) {
  32         105  
  32         56  
90 32         204 my $ballots = $I->GetBallots();
91 32         1601 for my $b ( keys $ballots->%* ) {
92 10510         260441 $ballots->{$b}->{'votevalue'} = $I->VoteValue();
93 10510         18077 $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 32     32 0 96 my $self = shift;
101 32 100       149 unless ( $self->BallotSetType() eq 'rcv' ) {
102 1         221 croak "Charge only supports rcv Ballot Type";
103             }
104 31         147 $self->_setTieBreaks();
105 31         145 $self->ResetVoteValue();
106 31         647 $self->_init_choice_status();
107 31         927 $self->FloorRounding('down');
108             }
109              
110             =pod
111              
112             CountAbandoned
113              
114             =cut
115              
116 28     28 0 3342 sub CountAbandoned ($I) {
  28         48  
  28         39  
117 28         111 my @continuing = ( $I->Deferred(), $I->GetActiveList );
118 28         118 my $set = $I->GetBallots();
119 28         111 my %res = ( count_abandoned => 0, value_abandoned => 0, );
120 28         1935 for my $k ( keys $set->%* ) {
121 17412 50       34182 if ( $set->{$k}{'votevalue'} == 0 ) {
122 0         0 $res{count_abandoned} += $set->{$k}{'count'};
123 0         0 next;
124             }
125 17412         18763 my $continue = 0;
126 17412         21749 for my $c (@continuing) {
127 82806         732901 $continue += ( grep /$c/, $set->{$k}{'votes'}->@* );
128             }
129 17412 100       37926 unless ($continue) {
130 631         1086 $res{count_abandoned} += $set->{$k}{'count'};
131 631         1120 $res{value_abandoned} += $set->{$k}{'count'} * $set->{$k}{'votevalue'};
132             }
133             }
134             $res{message} =
135 28         1445 "Votes with no Choice left: $res{count_abandoned}, Value: $res{value_abandoned}";
136 28         249 return \%res;
137             }
138              
139 29     29 1 5193 sub GetChoiceStatus ( $I, $choice = 0 ) {
  29         43  
  29         54  
  29         42  
140 29 100       69 if ($choice) { return $I->{'choice_status'}{$choice} }
  24         111  
141 5         21 else { return $I->{'choice_status'} }
142             }
143              
144 12     12 1 26 sub SetChoiceStatus ( $I, $choice, $status ) {
  12         20  
  12         22  
  12         18  
  12         16  
145 12 100       35 if ( $status->{'state'} ) {
146 2 50       48 unless ( grep ( /^$status->{'state'}$/, @choice_valid_states ) ) {
147 0         0 croak "invalid state *$status->{'state'}* assigned to choice $choice";
148             }
149 2         7 $I->{'choice_status'}->{$choice}{'state'} = $status->{'state'};
150             }
151 12 100       41 if ( $status->{'votes'} ) {
152 11         40 $I->{'choice_status'}->{$choice}{'votes'} = int $status->{'votes'};
153             }
154             }
155              
156 3     3 1 5 sub VCUpdateActive ($I) {
  3         6  
  3         4  
157 3         4 my $active = {};
158 3         7 for my $k ( keys $I->GetChoiceStatus()->%* ) {
159 24 100       43 $active->{$k} = 1 if $I->{'choice_status'}->{$k}{'state'} eq 'hopeful';
160             # $active->{$k} = 1 if $I->{'choice_status'}->{$k}{'state'} eq 'pending';
161             }
162 3         12 $I->SetActive($active);
163             }
164              
165 39     39 1 2831 sub Elect ( $I, $choice ) {
  39         63  
  39         71  
  39         59  
166 39         106 delete $I->{'Active'}{$choice};
167 39         100 $I->{'choice_status'}->{$choice}{'state'} = 'elected';
168 39         145 $I->{'pending'} = [ grep ( !/^$choice$/, $I->{'pending'}->@* ) ];
169 39         105 push $I->{'elected'}->@*, $choice;
170 39         142 return $I->{'elected'}->@*;
171             }
172              
173 21     21 1 53 sub Elected ($I) { return $I->{'elected'}->@* }
  21         37  
  21         28  
  21         108  
174              
175 23     23 1 3916 sub Defeat ( $I, $choice ) {
  23         43  
  23         43  
  23         33  
176 23         57 delete $I->{'Active'}{$choice};
177 23         75 $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 674 sub Withdrawn ($I) {
  12         22  
  12         15  
191 12         22 my @withdrawn = ();
192 12         41 for my $c ( keys $I->{'choice_status'}->%* ) {
193 128 100       238 if ( $I->{'choice_status'}{$c}{'state'} eq 'withdrawn') {
194 25         42 push @withdrawn, $c;
195             }
196             }
197 12         47 return sort(@withdrawn);
198             }
199              
200 9     9 1 14 sub Withdraw ( $I, $choice ) {
  9         15  
  9         14  
  9         21  
201 9         18 delete $I->{'Active'}{$choice};
202 9         21 $I->{'choice_status'}->{$choice}{'state'} = 'withdrawn';
203 9         19 return $I->Withdrawn();
204             }
205              
206 8     8 1 24 sub Suspend ( $I, $choice ) {
  8         11  
  8         10  
  8         11  
207 8         15 delete $I->{'Active'}{$choice};
208 8         17 $I->{'choice_status'}->{$choice}{'state'} = 'suspended';
209 8 100       76 unless ( grep ( /^$choice$/, $I->{'suspended'}->@* ) ) {
210 7         17 push $I->{'suspended'}->@*, $choice;
211             }
212 8         22 return $I->Suspended();
213             }
214              
215 11     11 1 15 sub Suspended ($I ) {
  11         12  
  11         11  
216 11         37 return $I->{'suspended'}->@*;
217             }
218              
219 2     2 1 6 sub Defer ( $I, $choice ) {
  2         4  
  2         3  
  2         4  
220 2         5 delete $I->{'Active'}{$choice};
221 2         5 $I->{'choice_status'}->{$choice}{'state'} = 'deferred';
222 2 50       11 unless ( grep ( /^$choice$/, $I->{'deferred'}->@* ) ) {
223 2         6 push $I->{'deferred'}->@*, $choice;
224             }
225 2         7 return $I->Deferred();
226             }
227              
228 31     31 1 51 sub Deferred ($I ) {
  31         57  
  31         38  
229 31         164 return $I->{'deferred'}->@*;
230             }
231              
232 2     2 1 5 sub Pending ( $I, $choice = undef ) {
  2         3  
  2         4  
  2         2  
233 2 100       6 if ($choice) {
234 1 50       5 unless ( grep /^$choice$/, $I->{'pending'}->@* ) {
235 1         3 $I->{'choice_status'}->{$choice}{'state'} = 'pending';
236 1         3 push $I->{'pending'}->@*, $choice;
237 1         2 delete $I->{'Active'}{$choice};
238             }
239             }
240 2         9 return $I->{'pending'}->@*;
241             }
242              
243 4     4 1 6 sub Reinstate ( $I, @choices ) {
  4         7  
  4         5  
  4         6  
244             # if no choices are given reinstate all.
245 4 100       13 @choices = ($I->{'suspended'}->@*, $I->{'deferred'}->@* ) unless @choices;
246 4         7 my @reinstated = ();
247             REINSTATELOOP:
248 4         6 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         11 given ($I->{'choice_status'}{$choice}{'state'}){
252 6         18 when ( 'suspended') { }
253 2         4 when ( 'deferred' ) { }
254 1         2 default { next REINSTATELOOP }
  1         3  
255             };
256 5         60 ($I->{'suspended'}->@*) = grep ( !/^$choice$/, $I->{'suspended'}->@* );
257 5         30 ($I->{'deferred'}->@*) = grep ( !/^$choice$/, $I->{'deferred'}->@* );
258 5         10 $I->{'choice_status'}->{$choice}{'state'} = 'hopeful';
259 5         6 $I->{'Active'}{$choice} = 1;
260 5         10 push @reinstated, $choice;
261             }
262 4         12 return @reinstated;
263             }
264              
265 22     22 1 4187 sub Charge ( $I, $choice, $quota, $charge=$I->VoteValue() ) {
  22         44  
  22         38  
  22         44  
  22         225  
  22         38  
266 22         45 my $charged = 0;
267 22         34 my $surplus = 0;
268 22         45 my @ballotschrgd = ();
269 22         32 my $cntchrgd = 0;
270 22         578 my $active = $I->Active();
271 22         477 my $ballots = $I->BallotSet()->{'ballots'};
272             # warn Dumper $ballots;
273             CHARGECHECKBALLOTS:
274 22         2312 for my $B ( keys $ballots->%* ) {
275 11820 100       22846 next CHARGECHECKBALLOTS if ( $I->TopChoice($B) ne $choice );
276 2501         3726 my $ballot = $ballots->{$B};
277 2501 100       5199 if ( $charge == 0 ) {
    100          
278 1         2 $charged += $ballot->{'votevalue'} * $ballot->{'count'};
279 1         3 $ballot->{'charged'}{$choice} = $ballot->{'votevalue'};
280 1         1 $ballot->{'votevalue'} = 0;
281             }
282             elsif ( $ballot->{'votevalue'} >= $charge ) {
283 2344         3420 my $over = $ballot->{'votevalue'} - $charge;
284 2344         3948 $charged += ( $ballot->{'votevalue'} - $over ) * $ballot->{'count'};
285 2344         2808 $ballot->{'votevalue'} -= $charge;
286 2344         4879 $ballot->{'charged'}{$choice} = $charge;
287             }
288             else {
289 156         237 $charged += $ballot->{'votevalue'} * $ballot->{'count'};
290 156         271 $ballot->{'charged'}{$choice} = $ballot->{'votevalue'};
291 156         186 $ballot->{'votevalue'} = 0;
292             }
293 2501         4696 push @ballotschrgd, $B;
294 2501         3864 $cntchrgd += $ballot->{'count'};
295             }
296 22         930 $I->{'choice_status'}->{$choice}{'votes'} += $charged;
297 22         53 $surplus = $I->{'choice_status'}->{$choice}{'votes'} - $quota;
298 22         59 $I->{'choice_status'}->{$choice}{'votes'} = $charged;
299             return (
300             {
301 22         203 choice => $choice,
302             surplus => $surplus,
303             ballotschrgd => \@ballotschrgd,
304             cntchrgd => $cntchrgd,
305             quota => $quota
306             }
307             );
308             }
309              
310 24     24 1 150 sub STVEvent ( $I, $data = 0 ) {
  24         49  
  24         43  
  24         52  
311 24 100       674 return $I->{'stvlog'} unless $data;
312 19         76 push $I->{'stvlog'}->@*, $data;
313             }
314              
315 2     2 1 389 sub WriteSTVEvent( $I) {
  2         5  
  2         4  
316 2         61 my $jsonpath = $I->LogTo . '_stvevents.json';
317 2         43 my $yamlpath = $I->LogTo . '_stvevents.yaml';
318             # my $yaml = ;
319 2         13 my $coder = JSON->new->ascii->pretty;
320 2         44 path($jsonpath)->spew( $coder->encode( $I->STVEvent() ) );
321 2         828 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 22 sub NextSTVRound( $I) { return ++$I->{'stvround'} }
  12         22  
  12         25  
  12         37  
327              
328 2     2 0 23 sub TCStats( $I ) {
  2         4  
  2         3  
329 2         33 my $tc = $I->TopCount;
330 2         12 $tc->{'total_votes'} = $I->VotesCast;
331 2         48 $tc->{'total_vote_value'} = $tc->{'total_votes'} * $I->VoteValue;
332 2         6 $tc->{'abandoned'} = $I->CountAbandoned;
333             $tc->{'active_vote_value'} =
334 2         7 $tc->{'total_vote_value'} - $tc->{'abandoned'}{'value_abandoned'};
335 2         24 return $tc;
336             }
337              
338 2     2 0 15 sub STVFloor ( $I, $action='Withdraw' ) {
  2         4  
  2         5  
  2         2  
339 2 50 33     55 if ( $I->FloorRule() && $I->FloorThresshold() ) {
340 2 100       51 $I->FloorRule('ApprovalFloor') if $I->FloorRule() eq 'Approval';
341 2 100       50 $I->FloorRule('TopCountFloor') if $I->FloorRule() eq 'TopCount';
342 2         4 my @withdrawn =();
343 2         50 my $newactive =
344             $I->ApplyFloor(
345             $I->FloorRule(),
346             $I->FloorThresshold()
347             );
348 2         9 for my $choice (sort $I->GetChoices()) {
349 24 100       44 unless( $newactive->{$choice}) {
350 9         24 $I->$action( $choice );
351 9         17 push @withdrawn, $choice;
352             }
353             }
354 2         8 @withdrawn = sort (@withdrawn);
355 2         5 my $done = $action;
356 2 100       5 $done = 'Withdrawn' if $action eq 'Withdraw';
357 2 100       5 $done = 'Defeated' if $action eq 'Defeat';
358 2         18 return @withdrawn;
359             }
360             }
361              
362 9     9 1 58 sub SetQuota ($I, $style='droop') {
  9         19  
  9         20  
  9         15  
363 9         31 my $abandoned = $I->CountAbandoned();
364 9         24 my $abndnvotes = $abandoned->{'value_abandoned'};
365 9         346 my $cast = $I->BallotSet->{'votescast'};
366 9         266 my $numerator = ( $cast * $I->VoteValue ) - $abndnvotes;
367 9         242 my $denominator = $I->Seats();
368 9         15 my $adjust = 0;
369 9 100       30 if ( $style eq 'droop' ) {
370 7         16 $denominator++;
371 7         14 $adjust = 1;
372             }
373 9         83 return ( $adjust + int( $numerator / $denominator ) );
374             }
375              
376             =head1 NAME
377              
378             Vote::Count::Charge
379              
380             =head1 VERSION 2.01
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'.
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