chiark / gitweb /
autodie
[appendix-a6.git] / parse
1 #!/usr/bin/perl -w
2 use strict;
3 use utf8;
4 use autodie;
5
6 use Data::Printer;
7 use Graph::Directed;
8
9 binmode STDIN, 'encoding(UTF-8)';
10 binmode STDOUT, 'encoding(UTF-8)';
11 binmode STDERR, 'encoding(UTF-8)';
12
13 our @choices;
14 our %choices;
15 our @invotes;
16 our $defcho;
17 our $quorum = 0;
18
19 sub addchoice {
20     my $choname = shift @_;
21     my $cho = $choices{$choname} = { @_, Index => (scalar @choices) };
22     push @choices, $choname;
23     return $cho;
24 }
25
26 while (<>) {
27     s/\s+$//;
28     next if m/^\s*\#/;
29     next unless m/\S/;
30     if (m/^([A-Z]+)\s*\=\s*(\S.*)$/) {
31         my ($choname, $desc) = ($1,$2);
32         my $cho = addchoice($choname, Desc => $desc);
33         if ($desc =~ m/\[(\d+):(\d+)\]/) {
34             $cho->{Smaj} = [$1,$2];
35         } elsif ($desc =~ m/\[default\]/) {
36             $defcho = $cho;
37         }
38     } elsif (m/^V:\s+(\S+)\s+(\S.*)/) {
39         push @invotes, [ $1, $2 ];
40     } elsif (m/^quorum = ([0-9.]+)$/) {
41         $quorum = $1+0.0;
42     } else {
43         die "invalid input";
44     }
45 }
46
47 $defcho ||= $choices{FD};
48 if (!$defcho) {
49     $defcho = addchoice('FD', Desc => "Further Discussion");
50 }
51 my $defi = $defcho->{Index};
52 die "FD has smaj?!" if $defcho->{Smaj};
53
54 our @vab;
55 # $vab[$ia][$ib] = V(A,B)
56 # Actually, it's a list of voters who prefer A to B (A.6(3)(1))
57
58 # Go through the voters and construct V(A,B)
59
60 foreach my $iv (@invotes) {
61     my ($votestr,$voter) = @$iv;
62     eval {
63         length $votestr eq @choices or die "wrong vote vector length";
64         my @vs = split //, $votestr;
65         foreach my $ix (0..$#vs) {
66             my $vchr = $vs[$ix];
67             if ($vchr eq '-') {
68                 $vs[$ix] = 1000;
69             } elsif ($vchr =~ m/[0-9a-z]/) {
70                 $vs[$ix] = ord($vchr);
71             } else {
72                 die "bad vote char";
73             }
74         }
75         foreach my $ia (0..$#vs) {
76             foreach my $ib ($ia+1..$#vs) {
77                 my $va = $vs[$ia];
78                 my $vb = $vs[$ib];
79                 if ($va < $vb) { push @{ $vab[$ia][$ib] }, $voter }
80                 elsif ($vb < $va) { push @{ $vab[$ib][$ia] }, $voter }
81             }
82         }
83     };
84     die "voter $voter $@" if $@;
85 }
86
87 our @ch = map { $choices{$_} } @choices;
88
89 # Print the counts V(A,B)
90 foreach my $iy (-2..$#ch) {
91     foreach my $ix (-2..$#ch) {
92         if ($iy==-1) {
93             if ($ix==-1) {
94                 printf "+";
95             } else {
96                 printf "------";
97             }
98         } elsif ($ix==-1) {
99             printf "|";
100         } elsif ($ix==-2 && $iy==-2) {
101             printf "V(Y,X)";
102         } elsif ($iy==-2) {
103             printf "%5s ", $choices[$ix];
104         } elsif ($ix==-2) {
105             printf "%5s ", $choices[$iy];
106         } else {
107             my $v = \( $vab[$iy][$ix] );
108             $$v ||= [ ];
109             if (@$$v) {
110                 printf "%5d ", (scalar @$$v);
111             } else {
112                 printf "%5s ", "";
113             }
114         }
115     }
116     printf "\n";
117 }
118
119 sub drop ($$) {
120     my ($i,$why) = @_;
121     print "dropping $choices[$i]: $why\n";
122     $ch[$i]{Dropped} = $why;
123 }
124
125 print "# quorum A.6(2)\n";
126
127 foreach my $i (0..$#choices) {
128     next if $ch[$i]{Dropped};
129     next if $i == $defi;
130     my $v = $vab[$i][$defi];
131     next if $v >= $quorum;
132     drop $i, "quorum ($v < $quorum)";
133 }
134
135 print "# maj. ratio A.6(3)\n";
136
137 foreach my $i (0..$#choices) {
138     next if $ch[$i]{Dropped};
139     next if $i == $defi;
140     my $majr = $ch[$i]{Smaj};
141     $majr ||= [1,1]; # A.6(3)(3)
142     my $vad = scalar @{ $vab[$i][$defi] };
143     my $vda = scalar @{ $vab[$defi][$i] };
144     next if $vad * $majr->[1] > $vda * $majr->[0];
145     drop $i, "majority ratio ($vad * $majr->[1] <= $vda * $majr->[0])";
146 }
147
148 my $defeats = Graph::Directed->new; # A.6(4)
149
150 foreach my $ia (0..$#ch) {
151     foreach my $ib (0..$#ch) {
152         my $vab = $vab[$ia][$ib];
153         my $vba = $vab[$ib][$ia];
154         next unless $vab > $vba;
155         print "defeat: $choices[$ia] beats $choices[$ib] ($vab > $vba)\n";
156         $defeats->add_vertex($ia,$ib);
157     }
158 }
159
160 print "# transitive closure A.6(5)\n";
161
162 my $tdefeats = $defeats->transitive_closure();
163
164 print "# schwartz set A.6(6)\n";
165
166 my $schwartz = $defeats->copy();
167
168 foreach my $ia (0..$#ch) {
169     foreach my $ib (0..$#ch) {
170         next if $tdefeats->has_edge($ia,$b);
171         next if !$tdefeats->has_edge($b,$ia);
172         print "not in Schwartz set: $choices[$ia] because $choices[$ib]\n";
173         $schwartz->delete_vertex($ia);
174         last;
175     }
176 }
177
178 print "# dropping weakest defeats A.6(7)\n";
179
180 my @weakest = ();
181
182 foreach my $edge (@{ $schwartz->edges() }) {
183     if (!@weakest) {
184         # no weakest edges yet
185     } elsif (weaker($edge, $weakest[0])) {
186         # this edge is weaker than previous weakest, start new set
187         @weakest = ();
188     } elsif (weaker($weakest[0], $edge)) {
189         # weakest edge is weaker than this one, ignore this one
190         next;
191     } else {
192         # weakest edge is exactly as weak as this one, add this one
193     }
194     push @weakest, $edge;
195 }
196
197 b#p %choices;
198 #p @vab;