chiark / gitweb /
Demonstrate bug in Graph::Undirected
[ypp-sc-tools.main.git] / yarrg / yppedia-chart-parser
1 #!/usr/bin/perl
2
3 use strict (qw(vars));
4 use warnings;
5
6 use Graph::Undirected;
7
8 use CommodsDatabase;
9
10 my $widists= Graph::Undirected->new();
11 my $wiarchs= Graph::Undirected->new(unionfind => 1);
12 my @wiarchlabels;
13 my %wiisland2node;
14 my %winode2island;
15 my %wiisland2arch;
16 my %winode2lines;
17 my %wiccix2arch;
18
19 my $dbdists= Graph::Undirected->new();
20 my %dbisland2arch;
21
22 my $warnings=0;
23 sub warning ($) {
24     my ($m) = @_;
25     print STDERR "warning: $m\n";
26     $warnings++;
27 }
28 my $errors=0;
29 sub error ($) {
30     my ($m) = @_;
31     print STDERR "error: $m\n";
32     $errors++;
33 }
34
35 open PO, ">/dev/null" or die $!;
36 #open PO, ">&STDOUT" or die $!;
37 select(PO); $|=1;
38 select(STDOUT); $|=1;
39
40 my $parity;
41 sub nn_xy ($$) {
42     my ($x,$y) = @_;
43     my $tp= (0+$x ^ 0+$y) & 1;
44     defined $parity or $parity=$tp;
45     $tp==$parity or warning("line $.: parity error $x,$y is $tp not $parity");
46     my $n= "$_[0],$_[1]";
47     $winode2lines{$n}{$.}++;
48     return $n;
49 }
50
51 sub parse_yppedia_map () {
52     # We don't even bother with tag soup; instead we do line-oriented parsing.
53
54     while (<>) {
55         s/\<--.*--\>//g;
56         s/^\s*//; chomp; s/\s+$//; s/\s+/ /g;
57         s/\<\/?(?:b|em)\>//g;
58         s/\{\{Chart\ style\|[^{}]*\}\}//g;
59         next unless m/\{\{/; # only interested in chart template stuff
60
61         my ($x,$y, $arch,$island,$solid,$dirn);
62         my $nn= sub { return nn_xy($x,$y) };
63     
64         if (($x,$y,$arch) =
65             m/^\{\{ chart\ label \|(\d+)\|(\d+)\| .*
66                     \'\[\[ [^][\']* \| (\S+)\ archipelago \]\]\'*\}\}$/xi) {
67             printf PO "%d,%d arch %s\n", $x,$y,$arch;
68             push @wiarchlabels, [ $x,$y,$arch ];
69         } elsif (($x,$y,$island) =
70             m/^\{\{ chart\ island\ icon \|(\d+)\|(\d+)\|
71                     ([^| ][^|]*[^| ]) \| .*\}\}$/xi) {
72             my $n= $nn->();
73             $wiisland2node{$island}= $n;
74             $winode2island{$n}= $island;
75             $widists->add_vertex($n);
76             $wiarchs->add_vertex($n);
77 print "\$g->add_vertex('$n');\n";
78             printf PO "%d,%d island %s\n", $x,$y,$island;
79         } elsif (($solid,$x,$y,$dirn) =
80             m/^\{\{ chart\ league((?:\ solid)?) \|(\d+)\|(\d+)\|
81                     ([-\/\\o]) \| .*\}\}$/xi) {
82             next if $dirn eq 'o';
83
84             my ($bx,$by) = ($x,$y);
85             if ($dirn eq '-') { $bx+=2; }
86             elsif ($dirn eq '\\') { $bx++; $by++; }
87             elsif ($dirn eq '/') { $x++; $by++; }
88             else { die; }
89
90             $widists->add_weighted_edge($nn->(), nn_xy($bx,$by), 1);
91             $wiarchs->add_edge($nn->(), nn_xy($bx,$by)) if $solid;
92             $wiarchs->add_edge($nn->(), nn_xy($bx,$by)) if $solid;
93 print "\$g->add_edge('".$nn->()."','".nn_xy($bx,$by)."');\n" if $solid;
94
95             printf PO "%d,%d league %s %s \n", $x,$y,
96                 $solid?'solid':'dotted', $dirn;
97         } elsif (
98             m/^\{\{ chart\ head \}\}$/xi
99                  ) {
100             next;
101         } else {
102             warning("line $.: ignoring incomprehensible: $_");
103         }
104     }
105 }
106
107 sub parse_database_map () {
108     my ($row,$sth);
109     $sth= $dbh->prepare('SELECT islandname, archipelago FROM islands');
110     $sth->execute();
111     foreach $row ($sth->fetchrow_hashref) {
112         $dbisland2arch{$row->{'islandname'}}= $row->{'archipelago'};
113     }
114     $sth= $dbh->prepare('SELECT dist, a.islandname a, b.islandname b
115                                 FROM dists
116                                 JOIN islands AS a ON dists.aiid==a.islandid
117                                 JOIN islands AS b ON dists.biid==b.islandid');
118     $sth->execute();
119     foreach $row ($sth->fetchrow_hashref) {
120         $dbdists->add_weighted_edge($row->{'a'}, $row->{'b'}, $row->{'dist'});
121     }
122 }                        
123
124 sub process_yppedia_graphs () {
125     # Prune the LP database by eliminating boring intermediate vertices
126     foreach my $delete ($widists->vertices()) {
127         next if exists $winode2island{$delete};
128         my @neigh= $widists->neighbours($delete);
129         next unless @neigh==2;
130 #       my @aneigh= $wiarchs->has_vertex($delete)
131 #           ? $wiarchs->neighbours($delete) : ();
132 #       next unless @aneigh==0 || @aneigh==2;
133         my $weight= 0;
134         map { $weight += $widists->get_edge_weight($delete, $_) } @neigh;
135         $widists->add_weighted_edge(@neigh, $weight);
136         $widists->delete_vertex($delete);
137 #       print PO "$delete elide $weight\n";
138     }
139
140     # Check that it's connected.
141     foreach my $cc ($widists->connected_components()) {
142         next if 2*@$cc > $widists->vertices();
143         my $m= "disconnected league point(s):";
144         foreach my $n (@$cc) {
145             $m .= "\n    LP $n, def. yppedia line(s): ".
146                 join(',', sort keys %{ $winode2lines{$n} });
147         }
148         warning($m);
149     }
150
151     # Compute all-pairs-shortest-paths on dist, which is the
152     # actual distances between all LPs.
153     my $wialldists= $widists->APSP_Floyd_Warshall();
154
155     # Compute arch's
156     foreach my $label (@wiarchlabels) {
157         my ($ax,$ay,$arch) = @$label;
158         my $d2best= 9999999;
159         my $best;
160         foreach my $vertex ($wiarchs->vertices()) {
161             next unless exists $winode2island{$vertex};
162             my ($vx,$vy) = split /,/, $vertex;
163             my $d2= ($vx-$ax)*($vx-$ax) + ($vy-$ay)*($vy-$ay);
164             next unless $d2 < $d2best;
165             $best= $vertex;
166             $d2best= $d2;
167         }
168         die 'no island vertices?!' unless defined $best;
169         printf PO "%d,%d arch-select-island %s %s\n",
170             $ax,$ay, $arch, $winode2island{$best};
171         my $ccix= $wiarchs->connected_component_by_vertex($best);
172         my $desc= join "\n", map {
173             my $in= $winode2island{$_};
174             "    LP $_". (defined $in ? ", $in" : "");
175         } sort $wiarchs->connected_component_by_index($ccix);
176
177         if (exists $wiccix2arch{$ccix}) {
178             error("architecture determination failed:\n".
179                   "    archipelago $arch\n".
180                   "    archipelago $wiccix2arch{$ccix}\n".
181                   $desc);
182             next;
183         }
184         $wiccix2arch{$ccix}= $arch;
185 #       print "$ccix $arch ::\n$desc\n";
186     }
187 }
188
189 sub compare_island_lists () {
190 #    foreach my $island (keys %dbisland2arch) {
191 #       next if exists $winode2island
192 #       error("
193 }
194
195 db_setocean('Midnight');
196 db_connect();
197 parse_yppedia_map();
198 parse_database_map();
199 process_yppedia_graphs();
200 compare_island_lists();
201
202 printf "%d %d %d %d\n", $wiarchs->has_edge('32,17','33,18'),
203     $wiarchs->connected_component_by_vertex('32,17'),
204     $wiarchs->connected_component_by_vertex('33,18'),
205     $wiarchs->same_connected_components('32,17','33,18');