From: Ian Jackson Date: Mon, 1 Aug 2016 13:30:49 +0000 (+0100) Subject: stv: wip, delay transfer X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=appendix-a6.git;a=commitdiff_plain;h=2dfa9d6f7edfa662fda11d29727c6a2941fe8b70 stv: wip, delay transfer --- diff --git a/stv b/stv index 9e0acee..bd4a6b3 100755 --- a/stv +++ b/stv @@ -7,6 +7,7 @@ use strict; use utf8; use autodie; use bigrat; +use List::MoreUtils qw(nsort_by); # vote is # { Voter => opaque, @@ -51,53 +52,115 @@ sub filterout ($$) { } } +our @elected; # $elected[] = $candidate + +our @unsorted = @allvotes; + +our %sorted; +# $sorted{$firstpref}{Votes} = [ $vote, ... ] +# $sorted{$firstpref}{Cand} = $firstpref +# $sorted{$firstpref}{Total} = $totalweight +our @sorted; # values same as %sorted +our @surpluses; # values same as %sorted + +our @exhausted; # votes + +our %continuing; # $continuing{$candidate}=1 + +our @stagerecord; # $stagerecord[]{$candidate} = $total + +foreach my $vote (@allvotes) { + $continuing{$_}=1 foreach @{ $vote->{Prefs} }; +} + for (;;) { $stage++; - my %sorted; - # $sorted{firstpref}{Votes} = [ $vote, ... ] - # $sorted{firstpref}{Total} = [ $vote, ... ] - for my $vote (@allvotes) { - next unless @{ $vote->{Prefs} }; - my $firstpref = $vote->{Prefs}[0]; - push @{ $sorted{$firstpref}{Votes} }, $vote; + while (my $vote = shift @unsorted) { + my ($firstpref) = grep { $continuing{$_} } @{ $vote->{Prefs} }; + if (!defined $firstpref) { + votelog $vote, "ballot exhausted"; + push @exhausted, $vote; + } else { + push @{ $sorted{$firstpref}{Votes} }, $vote; + } } + $sorted{$_}{Cand} = $_ foreach keys %sorted; foreach my $firstpref (sort keys %sorted) { foreach my $vote (@{ $sorted{$firstpref}{Votes} }) { votelog $vote, "counted $vote->{Weight} for $firstpref"; $sorted{$firstpref}{Total} += $vote->{Weight}; } } - my @sorted = sort { - $sorted{$b}{Total} <=> $sorted{$a}{Total}; - } keys %sorted; + my @sorted = nsort_by { -$_->{Total} } values %sorted; + + push @stagerecord, { map { ($_->{Cand}, $_->{Total}) } @sorted }; my $totalvalid = 0; - foreach my $firstpref (@sorted) { - $sorted{$firstpref}{Total} += $_->{Weight} - foreach @{ $sorted{$firstpref}{Votes} }; - printf "%10s %s\n", $firstpref, pr $sorted{$firstpref}{Total}; - $totalvalid += $sorted{$firstpref}{Total}; + my $countvalid = sub { + my ($l, $what) = @_; + foreach my $s (@$l) { + printf "%-7s %10s %s\n", $what, $s->{Cand}, pr $s->{Total}; + $totalvalid += $s->{Total}; + } + }; + $countvalid->(\@sorted, '1stpref'); + $countvalid->(\@surpluses, 'surplus'); + + printf "%7s %10s %s\n", 'TOTAL', '', pr $totalvalid; + + unless ($totalvalid > 0) { + printf "No more votes!\n"; + last; + } + + my $placesremain = $places - @elected; + + unless ($placesremain > 0) { + printf "Complete.\n"; + last; } - printf "%10s %s\n", 'TOTAL', pr $totalvalid; - last unless $totalvalid > 0; + my $quota = $totalvalid / ($placesremain + 1); + printf "%7s %10s %s\n", '', 'quota', pr $quota; - my $quota = $totalvalid / ($places + 1); - printf "\nquota = %10s\n", pr $quota; + my $need_to_transfer_surplus = 1; - my $topfirst = $sorted[0]; - my $topvoters = $sorted{$topfirst}{Weight}; - my $surplus = $topvoters - $quota; - if ($surplus >= 0) { - printf "%10s ELECTED\n", $sorted[0]; + # Look for people to elect. + # We elect as many as we can, rather than recomputing the (lower) quota + # (ERS rules 5.4.9) + for (;;) { + my $s = $sorted[0]; + my $topvoters = $s->{Weight}; + my $surplus = $topvoters - $quota; + last unless $surplus > 0; - # transfer the surplus - my $weightmult = $surplus / $topvoters; - foreach my $vote (@{ $sorted{$topfirst}{Votes} }) { - votelog $vote, "part of surplus of weight $weightmult"; - $vote->{Weight} *= $weightmult; + printf "%7s %10s\n", 'ELECTED', $s->{Cand}; + push @elected, $s->{Cand}; + + my $derate = $topvoters / $surplus; + printf "%7s %10s\n", 'derate', $s->{Cand}, pr $derate; + + foreach my $vote (@{ $s->{Votes} }) { + votelog $vote, "elected $s->{Cand}, derated $weightmult"; + $vote->{Weight} /= $derate; } + shift @sorted; + push @surpluses, $s; + delete %sorted->{ $s->{Cand} }; + delete $continuing{ $s->{Cand} }; + + $need_to_transfer_surplus = 0; + # before actually transferring a surplus, we will consider + # eliminating, and then reconsider with a lower quota + } + + # Look for people to eliminate + # We eliminate before trying to transfer surpluses + # ERS 5.2.5 + + + # transfer the surplus filterout $topfirst, "candidate was elected"; } else { my $elim = $sorted{ $#sorted };