line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Graph::Fast; |
2
|
|
|
|
|
|
|
|
3
|
6
|
|
|
6
|
|
133992
|
use strict; |
|
6
|
|
|
|
|
16
|
|
|
6
|
|
|
|
|
224
|
|
4
|
6
|
|
|
6
|
|
31
|
use warnings; |
|
6
|
|
|
|
|
13
|
|
|
6
|
|
|
|
|
163
|
|
5
|
6
|
|
|
6
|
|
155
|
use 5.010; |
|
6
|
|
|
|
|
20
|
|
|
6
|
|
|
|
|
353
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
our $VERSION = "0.02"; |
8
|
|
|
|
|
|
|
|
9
|
6
|
|
|
6
|
|
7533
|
use Data::Dumper; |
|
6
|
|
|
|
|
69029
|
|
|
6
|
|
|
|
|
473
|
|
10
|
6
|
|
|
6
|
|
8852
|
use Storable qw(dclone); |
|
6
|
|
|
|
|
25379
|
|
|
6
|
|
|
|
|
495
|
|
11
|
6
|
|
|
6
|
|
55
|
use List::Util qw(min); |
|
6
|
|
|
|
|
13
|
|
|
6
|
|
|
|
|
700
|
|
12
|
|
|
|
|
|
|
|
13
|
6
|
|
|
6
|
|
5178
|
use Hash::PriorityQueue; |
|
6
|
|
|
|
|
3823
|
|
|
6
|
|
|
|
|
9984
|
|
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
sub new { |
16
|
5
|
|
|
5
|
1
|
89
|
my ($class, %args) = @_; |
17
|
5
|
50
|
|
7
|
|
46
|
my $queue_maker = exists($args{queue_maker}) ? $args{queue_maker} : sub { Hash::PriorityQueue->new() }; |
|
7
|
|
|
|
|
30
|
|
18
|
5
|
|
|
|
|
44
|
return bless({ |
19
|
|
|
|
|
|
|
vertices => {}, |
20
|
|
|
|
|
|
|
edges => [], |
21
|
|
|
|
|
|
|
_queue_maker => $queue_maker, |
22
|
|
|
|
|
|
|
}, $class); |
23
|
|
|
|
|
|
|
} |
24
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
sub count_edges { |
26
|
21
|
|
|
21
|
1
|
55
|
my ($self) = @_; |
27
|
21
|
|
|
|
|
30
|
return scalar(@{$self->{edges}}); |
|
21
|
|
|
|
|
128
|
|
28
|
|
|
|
|
|
|
} |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
sub count_vertices { |
31
|
21
|
|
|
21
|
1
|
43
|
my ($self) = @_; |
32
|
21
|
|
|
|
|
31
|
return scalar(keys(%{$self->{vertices}})); |
|
21
|
|
|
|
|
105
|
|
33
|
|
|
|
|
|
|
} |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
sub add_vertex { |
36
|
15
|
|
|
15
|
1
|
27
|
my ($self, $name) = @_; |
37
|
|
|
|
|
|
|
|
38
|
15
|
50
|
|
|
|
47
|
if (!exists($self->{vertices}->{$name})) { |
39
|
15
|
|
|
|
|
83
|
$self->{vertices}->{$name} = { name => $name, edges_in => {}, edges_out => {} }; |
40
|
|
|
|
|
|
|
} |
41
|
15
|
|
|
|
|
98
|
return $self->{vertices}->{$name}; |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
sub del_vertex { |
45
|
6
|
|
|
6
|
1
|
14
|
my ($self, $name) = @_; |
46
|
|
|
|
|
|
|
|
47
|
6
|
50
|
|
|
|
28
|
if (exists($self->{vertices}->{$name})) { |
48
|
6
|
100
|
|
|
|
10
|
@{$self->{edges}} = grep { $_->{from} ne $name and $_->{to} ne $name } @{$self->{edges}}; |
|
6
|
|
|
|
|
20
|
|
|
23
|
|
|
|
|
140
|
|
|
6
|
|
|
|
|
18
|
|
49
|
6
|
|
|
|
|
9
|
foreach my $in_edge (keys %{$self->{vertices}->{$name}->{edges_in}}) { |
|
6
|
|
|
|
|
27
|
|
50
|
8
|
|
|
|
|
27
|
delete($self->{vertices}->{$in_edge}->{edges_out}->{$name}); |
51
|
|
|
|
|
|
|
} |
52
|
6
|
|
|
|
|
12
|
foreach my $out_edge (keys %{$self->{vertices}->{$name}->{edges_out}}) { |
|
6
|
|
|
|
|
38
|
|
53
|
4
|
|
|
|
|
11
|
delete($self->{vertices}->{$out_edge}->{edges_in}->{$name}); |
54
|
|
|
|
|
|
|
} |
55
|
6
|
|
|
|
|
38
|
delete($self->{vertices}->{$name}); |
56
|
|
|
|
|
|
|
} |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
sub dijkstra_worker { |
60
|
9
|
|
|
9
|
0
|
15
|
my ($self, $from, $to) = @_; |
61
|
|
|
|
|
|
|
|
62
|
9
|
|
|
|
|
15
|
my $vert = $self->{vertices}; |
63
|
9
|
|
|
|
|
21
|
my $suboptimal = $self->{_queue_maker}->(); |
64
|
9
|
|
|
|
|
60
|
$suboptimal->insert($_, $self->{d_suboptimal}->{$_}) foreach (keys(%{$self->{d_suboptimal}})); |
|
9
|
|
|
|
|
49
|
|
65
|
9
|
|
|
|
|
124
|
$self->{d_dist}->{$_} = -1 foreach (@{$self->{d_unvisited}}); |
|
9
|
|
|
|
|
69
|
|
66
|
9
|
|
|
|
|
20
|
$self->{d_dist}->{$from} = 0; |
67
|
|
|
|
|
|
|
|
68
|
9
|
|
|
|
|
10
|
while (1) { |
69
|
|
|
|
|
|
|
# find the smallest unvisited node |
70
|
31
|
|
100
|
|
|
218
|
my $current = $suboptimal->pop() // last; |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# update all neighbors |
73
|
22
|
|
|
|
|
455
|
foreach my $edge (values %{$vert->{$current}->{edges_out}}) { |
|
22
|
|
|
|
|
67
|
|
74
|
19
|
100
|
100
|
|
|
186
|
if (($self->{d_dist}->{$edge->{to}} == -1) || |
75
|
|
|
|
|
|
|
($self->{d_dist}->{$edge->{to}} > ($self->{d_dist}->{$current} + $edge->{weight}) )) { |
76
|
16
|
|
|
|
|
65
|
$suboptimal->update( |
77
|
|
|
|
|
|
|
$edge->{to}, |
78
|
|
|
|
|
|
|
$self->{d_dist}->{$edge->{to}} = $self->{d_dist}->{$current} + $edge->{weight} |
79
|
|
|
|
|
|
|
); |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
} |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# trace the path from the destination to the start |
85
|
9
|
|
|
|
|
54
|
my @path = (); |
86
|
9
|
|
|
|
|
12
|
my $current = $to; |
87
|
9
|
|
|
|
|
20
|
NODE: while ($current ne $from) { |
88
|
11
|
|
|
|
|
12
|
foreach my $edge (values %{$vert->{$current}->{edges_in}}) { |
|
11
|
|
|
|
|
32
|
|
89
|
14
|
100
|
|
|
|
49
|
if ($self->{d_dist}->{$current} == $self->{d_dist}->{$edge->{from}} + $edge->{weight}) { |
90
|
7
|
|
|
|
|
10
|
$current = $edge->{from}; |
91
|
7
|
|
|
|
|
9
|
unshift(@path, $edge); |
92
|
7
|
|
|
|
|
39
|
next NODE; |
93
|
|
|
|
|
|
|
} |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
# getting here means we found no predecessor - there is none. |
96
|
|
|
|
|
|
|
# so there's no path. |
97
|
4
|
|
|
|
|
36
|
return (); |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
5
|
|
|
|
|
71
|
return @path; |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
sub dijkstra_first { |
104
|
9
|
|
|
9
|
0
|
15
|
my ($self, $from, $to) = @_; |
105
|
9
|
|
|
|
|
13
|
$self->{d_from} = $from; |
106
|
9
|
|
|
|
|
19
|
$self->{d_dist} = {}; |
107
|
9
|
|
|
|
|
35
|
$self->{d_unvisited} = [ grep { $_ ne $from } keys(%{$self->{vertices}}) ]; |
|
42
|
|
|
|
|
92
|
|
|
9
|
|
|
|
|
29
|
|
108
|
9
|
|
|
|
|
30
|
$self->{d_suboptimal} = { $from => 0 }; |
109
|
|
|
|
|
|
|
|
110
|
9
|
|
|
|
|
52
|
dijkstra_worker($self, $from, $to); |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
sub dijkstra_continue { |
114
|
5
|
|
|
5
|
0
|
10
|
my ($self, $from, $to, $del_to) = @_; |
115
|
|
|
|
|
|
|
# instead of reinitializing, it should invoke the worker after initializing |
116
|
|
|
|
|
|
|
# to a state that assumes that an edge to $del_to has just been deleted. |
117
|
5
|
|
|
|
|
12
|
goto &dijkstra_first; |
118
|
|
|
|
|
|
|
} |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
sub dijkstra { |
121
|
9
|
|
|
9
|
1
|
1012
|
my ($self, $from, $to, $del_to) = @_; |
122
|
9
|
100
|
100
|
|
|
49
|
if (!defined($self->{d_from}) or $self->{d_from} ne $from) { |
123
|
4
|
|
|
|
|
15
|
goto &dijkstra_first; |
124
|
|
|
|
|
|
|
} else { |
125
|
5
|
|
|
|
|
16
|
goto &dijkstra_continue; |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
} |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
sub recursive_dijkstra { |
130
|
0
|
|
|
0
|
0
|
0
|
my ($self, $from, $to, $level, $del_to) = @_; |
131
|
0
|
|
|
|
|
0
|
my @d = ([ $self->dijkstra($from, $to, $del_to) ]); |
132
|
|
|
|
|
|
|
|
133
|
0
|
0
|
|
|
|
0
|
if (!defined($d[0]->[0])) { |
134
|
0
|
|
|
|
|
0
|
return (); |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
0
|
0
|
|
|
|
0
|
if ($level > 0) { |
138
|
0
|
|
|
|
|
0
|
foreach (0..(@{$d[0]}-1)) { |
|
0
|
|
|
|
|
0
|
|
139
|
|
|
|
|
|
|
# from copies of the graph, remove one edge from the result path, |
140
|
|
|
|
|
|
|
# and continue finding paths on that tree. |
141
|
0
|
|
|
|
|
0
|
my $ffffuuuu = $self->{_queue_maker}; |
142
|
0
|
|
|
|
|
0
|
$self->{_queue_maker} = "omg"; |
143
|
0
|
|
|
|
|
0
|
my $g2 = dclone($self); |
144
|
0
|
|
|
|
|
0
|
$g2->{_queue_maker} = $self->{_queue_maker} = $ffffuuuu; |
145
|
|
|
|
|
|
|
|
146
|
0
|
|
|
|
|
0
|
$g2->del_edge($d[0]->[$_]->{from}, $d[0]->[$_]->{to}); |
147
|
0
|
|
|
|
|
0
|
my @new = $g2->recursive_dijkstra($from, $to, $level - 1, $d[0]->[$_]->{to}); |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
# add all new paths, unless they are already present in the result set |
150
|
0
|
|
|
|
|
0
|
foreach my $n (@new) { |
151
|
0
|
0
|
|
|
|
0
|
push(@d, $n) unless (grep { $n ~~ $_ } @d); |
|
0
|
|
|
|
|
0
|
|
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
0
|
|
|
|
|
0
|
@d; |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
sub add_edge { |
160
|
31
|
|
|
31
|
1
|
93
|
my ($self, $from, $to, $weight, $user_data) = @_; |
161
|
31
|
|
|
|
|
76
|
$self->del_edge($from => $to); |
162
|
|
|
|
|
|
|
|
163
|
31
|
50
|
|
|
|
211
|
my $edge = { from => $from, to => $to, weight => $weight, (defined($user_data) ? (user_data => $user_data) : ()) }; |
164
|
|
|
|
|
|
|
|
165
|
31
|
|
|
|
|
43
|
push(@{$self->{edges}}, $edge); |
|
31
|
|
|
|
|
59
|
|
166
|
31
|
|
33
|
|
|
124
|
($self->{vertices}->{$from} // $self->add_vertex($from))->{edges_out}->{$to} = $edge; |
167
|
31
|
|
66
|
|
|
149
|
($self->{vertices}->{$to } // $self->add_vertex($to ))->{edges_in }->{$from} = $edge; |
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
sub del_edge { |
171
|
33
|
|
|
33
|
1
|
53
|
my ($self, $from, $to) = @_; |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
# find the edge. assume it only exists once -> only delete the first. |
174
|
|
|
|
|
|
|
# while we're at it, delete the edge from the source vertex... |
175
|
33
|
|
|
|
|
102
|
my $e = $self->{vertices}->{$from}->{edges_out}->{$to}; |
176
|
33
|
100
|
|
|
|
97
|
return undef if (!defined($e)); |
177
|
2
|
|
|
|
|
6
|
delete($self->{vertices}->{$from}->{edges_out}->{$to}); |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
# now search it in the destination vertex' list, delete it there |
180
|
|
|
|
|
|
|
# also only delete the first matching one here (though now there |
181
|
|
|
|
|
|
|
# shouldn't be any duplicates at all because now we're matching the |
182
|
|
|
|
|
|
|
# actual edge, not just its endpoints like above. |
183
|
2
|
|
|
|
|
4
|
delete($self->{vertices}->{$to}->{edges_in}->{$from}); |
184
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
# and remove it from the graph's vertex list |
186
|
2
|
|
|
|
|
5
|
@{$self->{edges}} = grep { $_ != $e } @{$self->{edges}} |
|
2
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
8
|
|
|
2
|
|
|
|
|
5
|
|
187
|
|
|
|
|
|
|
} |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
1; |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
__END__ |