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