File Coverage

lib/BalanceOfPower/Relations/RelPack.pm
Criterion Covered Total %
statement 186 249 74.7
branch 42 72 58.3
condition 3 10 30.0
subroutine 24 31 77.4
pod 0 27 0.0
total 255 389 65.5


line stmt bran cond sub pod time code
1             package BalanceOfPower::Relations::RelPack;
2             $BalanceOfPower::Relations::RelPack::VERSION = '0.400110';
3 13     13   46 use strict;
  13         13  
  13         283  
4 13     13   91 use v5.10;
  13         29  
5              
6 13     13   38 use Moo;
  13         14  
  13         53  
7 13     13   7889 use Module::Load;
  13         10725  
  13         66  
8              
9             has links => (
10             is => 'rw',
11             default => sub { [] }
12             );
13             has links_grid => (
14             is => 'rw',
15             default => sub { {} }
16             );
17             has distance_cache => (
18             is => 'rw',
19             default => sub { {} }
20             );
21              
22              
23             sub all
24             {
25 322     322 0 233 my $self = shift;
26 322         199 return @{$self->links};
  322         792  
27             }
28             sub reset
29             {
30 1     1 0 2 my $self = shift;
31 1         3 $self->links([]);
32 1         17 $self->links_grid({});
33 1         3 $self->distance_cache({});
34             }
35              
36             sub exists_link
37             {
38 3138     3138 0 89648 my $self = shift;
39 3138         2245 my $node1 = shift;
40 3138         1972 my $node2 = shift;
41 3138 100       5070 if(exists $self->links_grid->{$node1}->{$node2})
42             {
43 1643         3921 return $self->links_grid->{$node1}->{$node2}
44             }
45             else
46             {
47 1495         3434 return undef;
48             }
49             }
50              
51             sub add_link
52             {
53 253     253 0 34720 my $self = shift;
54 253         254 my $link = shift;
55 253         424 my $node1 = $link->node1;
56 253         299 my $node2 = $link->node2;
57 253 100       364 if(! $self->exists_link($node1, $node2))
58             {
59 252         198 push @{$self->links}, $link;
  252         438  
60 252         374 $self->links_grid->{$node1}->{$node2} = $link;
61 252         352 $self->links_grid->{$node2}->{$node1} = $link;
62 252         716 return 1;
63             }
64             else
65             {
66 1         3 return 0;
67             }
68             }
69             sub update_link
70             {
71 0     0 0 0 my $self = shift;
72 0         0 my $link = shift;
73 0         0 $self->delete_link($link->node1, $link->node2);
74 0         0 $self->add_link($link);
75             }
76             sub delete_references
77             {
78 8     8 0 11 my $self = shift;
79 8         10 my $node1 = shift;
80 8         22 my $node2 = shift;
81 8         30 $self->links_grid->{$node1}->{$node2} = undef;
82 8         33 $self->links_grid->{$node2}->{$node1} = undef;
83             }
84             sub delete_references_for_node
85             {
86 15     15 0 9 my $self = shift;
87 15         13 my $node1 = shift;
88 15         15 foreach my $k (%{$self->links_grid->{$node1}})
  15         47  
89             {
90 2 50       6 if($k)
91             {
92 2         5 $self->links_grid->{$k}->{$node1} = undef;
93             }
94             }
95 15         38 $self->links_grid->{$node1} = undef;
96             }
97             sub delete_link
98             {
99 8     8 0 2527 my $self = shift;
100 8         11 my $node1 = shift;
101 8         13 my $node2 = shift;
102 8         12 @{$self->links} = grep { ! $_->involve($node1, $node2) } @{$self->links};
  8         22  
  9         33  
  8         28  
103 8         24 $self->delete_references($node1, $node2);
104             }
105              
106             sub delete_link_for_node
107             {
108 15     15 0 4556 my $self = shift;
109 15         18 my $n1 = shift;
110 15         11 @{$self->links} = grep { ! $_->has_node($n1) } @{$self->links};
  15         44  
  3         9  
  15         33  
111 15         33 $self->delete_references_for_node($n1);
112             }
113             sub garbage_collector
114             {
115 69     69 0 66 my $self = shift;
116 69         63 my $query = shift;
117 69         83 my @new = ();
118 69         57 for(@{$self->links})
  69         142  
119             {
120 14 50       27 if(! $query->($_))
121             {
122 14         26 push @new, $_;
123             }
124             else
125             {
126 0         0 $self->delete_references($_->node1, $_->node2);
127             }
128             }
129 69         63 @{$self->links} = @new;
  69         148  
130             }
131             sub links_for_node
132             {
133 526     526 0 36825 my $self = shift;
134 526         477 my $node = shift;
135 526 50       797 return $self->all() if(! $node);
136 526         549 my @out = ();
137 526         366 foreach my $k (keys %{$self->links_grid->{$node}})
  526         1629  
138             {
139 438 50       532 if($k)
140             {
141 438         459 my $r = $self->links_grid->{$node}->{$k};
142 438 100       711 push @out, $r if($r);
143             }
144             }
145 526         1124 return @out;
146             }
147             sub links_for_node1
148             {
149 326     326 0 16833 my $self = shift;
150 326         275 my $node = shift;
151 326 50       479 return $self->all() if(! $node);
152 326         331 my @out = ();
153 326         211 foreach my $r (@{$self->links})
  326         648  
154             {
155 71 50       169 if($r->bidirectional)
156             {
157 0         0 return $self->links_for_node($node);
158             }
159 71 100       185 if($r->node1 eq $node)
160             {
161 18         27 push @out, $r;
162             }
163             }
164 326         562 return @out;
165             }
166             sub links_for_node2
167             {
168 162     162 0 121 my $self = shift;
169 162         112 my $node = shift;
170 162 50       253 return $self->all() if(! $node);
171 162         148 my @out = ();
172 162         114 foreach my $r (@{$self->links})
  162         317  
173             {
174 43 50       83 if($r->bidirectional)
175             {
176 0         0 return $self->links_for_node($node);
177             }
178 43 100       114 if($r->node2 eq $node)
179             {
180 18         23 push @out, $r;
181             }
182             }
183 162         203 return @out;
184             }
185             sub first_link_for_node
186             {
187 642     642 0 29071 my $self = shift;
188 642         554 my $node = shift;
189 642         454 foreach my $r (@{$self->links})
  642         1224  
190             {
191 115 100       267 if($r->has_node($node))
192             {
193 42         137 return $r;
194             }
195             }
196 600         1777 return undef;
197             }
198             sub first_link_for_node1
199             {
200 0     0 0 0 my $self = shift;
201 0         0 my $node = shift;
202 0         0 my @links = $self->links_for_node1($node);
203 0 0       0 if(@links)
204             {
205 0         0 return $links[0]
206             }
207             else
208             {
209 0         0 return undef;
210             }
211             }
212             sub first_link_for_node2
213             {
214 162     162 0 10770 my $self = shift;
215 162         139 my $node = shift;
216 162         237 my @links = $self->links_for_node2($node);
217 162 100       214 if(@links)
218             {
219 18         40 return $links[0]
220             }
221             else
222             {
223 144         314 return undef;
224             }
225             }
226              
227             sub link_destinations_for_node
228             {
229 5     5 0 442 my $self = shift;
230 5         7 my $node1 = shift;
231 5         7 my @out = ();
232 5         4 for(keys %{$self->links_grid->{$node1}})
  5         19  
233             {
234 10         19 my $r = $self->links_grid->{$node1}->{$_};
235 10 50       14 if($r)
236             {
237 10         22 push @out, $r->destination($node1);
238             }
239             }
240 5         14 return @out;
241             }
242              
243             sub query
244             {
245 771     771 0 527 my $self = shift;
246 771         503 my $query = shift;
247 771         524 my $node1 = shift;
248 771         635 my @out = ();
249 771         509 for(@{$self->links})
  771         1075  
250             {
251 447 100       535 if($query->($_))
252             {
253 140 100       155 if($node1)
254             {
255 19 50       33 if($_->has_node($node1))
256             {
257 19         28 push @out, $_;
258             }
259             }
260             else
261             {
262 121         140 push @out, $_;
263             }
264             }
265             }
266 771         1028 return @out;
267             }
268             sub output_links
269             {
270 0     0 0 0 my $self = shift;
271 0         0 my $n = shift;
272 0   0     0 my $mode = shift || 'print';
273 0 0       0 if($mode eq 'print')
    0          
274             {
275 0         0 return $self->print_links($n);
276             }
277             elsif($mode eq 'html')
278             {
279 0         0 return $self->html_links($n);
280             }
281             }
282              
283             sub print_links
284             {
285 0     0 0 0 my $self = shift;
286 0         0 my $n = shift;
287 0         0 my $out = "";
288 0         0 foreach my $b (@{$self->links})
  0         0  
289             {
290 0 0       0 if($n)
291             {
292 0 0       0 if($b->has_node($n))
293             {
294 0         0 $out .= $b->print($n) . "\n";
295             }
296             }
297             else
298             {
299 0         0 $out .= $b->print($n) . "\n";
300             }
301             }
302 0         0 return $out;
303             }
304             sub html_links
305             {
306 0     0 0 0 my $self = shift;
307 0         0 my $n = shift;
308 0         0 my $out = "";
309 0         0 foreach my $b (@{$self->links})
  0         0  
310             {
311 0 0       0 if($n)
312             {
313 0 0       0 if($b->has_node($n))
314             {
315 0         0 $out .= $b->html($n) . "<br />";
316             }
317             }
318             else
319             {
320 0         0 $out .= $b->html($n) . "<br />";
321             }
322             }
323 0         0 return "<p>$out</p>";
324             }
325              
326             #BFS implementation
327             sub distance
328             {
329 177     177 0 10200 my $self = shift;
330 177         164 my $node1 = shift;
331 177         119 my $node2 = shift;
332 177         130 my $nodes_list = shift;
333 177         279 my %nodes = $self->get_cached_nodes($node1, $nodes_list);
334 177         178 my $log;
335 177 100       322 if($nodes{$node2}->{distance} != -1)
336             {
337 75         207 return $nodes{$node2}->{distance};
338             }
339 102 50       147 if(my $cached_distance = $self->get_cached_distance($node2, $node1))
340             {
341 0         0 $nodes{$node2}->{distance} = $cached_distance;
342 0         0 $self->distance_cache->{$node1}->{nodes} = \%nodes;
343 0         0 return $cached_distance;
344             }
345              
346 102         157 my @queue = ( $node1 );
347 102 100       215 if(exists $self->distance_cache->{$node1}->{queue})
348             {
349 42         37 @queue = @{$self->distance_cache->{$node1}->{queue}};
  42         92  
350             }
351 102         171 while(@queue)
352             {
353            
354 167         147 my $n = shift @queue;
355 167         220 foreach my $near ($self->near($n, $nodes_list))
356             {
357 322 100       469 if($nodes{$near}->{distance} == -1)
358             {
359 143         128 my $d = $nodes{$n}->{distance} + 1;
360 143         126 $nodes{$near}->{distance} = $nodes{$n}->{distance} + 1;
361 143         167 push @queue, $near;
362             }
363             }
364 167 100       378 if($nodes{$node2}->{distance} != -1)
365             {
366 43         87 $self->distance_cache->{$node1}->{nodes} = \%nodes;
367 43         73 $self->distance_cache->{$node1}->{queue} = \@queue;
368 43         117 return $nodes{$node2}->{distance};
369             }
370             }
371 59         71 $nodes{$node2}->{distance} = 100;
372 59         102 $self->distance_cache->{$node1}->{nodes} = \%nodes;
373 59         112 $self->distance_cache->{$node1}->{queue} = \@queue;
374 59         145 return 100;
375             }
376             sub get_cached_distance
377             {
378 102     102 0 78 my $self = shift;
379 102         80 my $node1 = shift;
380 102         78 my $node2 = shift;
381 102 50 66     289 if(exists $self->distance_cache->{$node1} &&
      33        
382             exists $self->distance_cache->{$node1}->{$node2} &&
383             $self->distance_cache->{$node1}->{$node2} != -1)
384             {
385 0         0 return $self->distance_cache->{$node1}->{$node2};
386             }
387             else
388             {
389 102         192 return undef;
390             }
391             }
392             sub get_cached_nodes
393             {
394 177     177 0 122 my $self = shift;
395 177         148 my $node1 = shift;
396 177         117 my $nodes_list = shift;
397 177         225 my %nodes = ();
398 177 100       353 if(exists $self->distance_cache->{$node1})
399             {
400 117         94 %nodes = %{$self->distance_cache->{$node1}->{nodes}};
  117         468  
401             }
402             else
403             {
404 60         48 foreach(@{$nodes_list})
  60         104  
405             {
406 302         371 $nodes{$_}->{distance} = -1;
407             }
408 60         71 $nodes{$node1}->{distance} = 0;
409             }
410 177         610 return %nodes;
411             }
412             sub near
413             {
414 184     184 0 1898 my $self = shift;
415 184         123 my $node = shift;
416 184         130 my $nodes = shift;
417 184 100       122 return grep { $self->exists_link($node, $_) && $node ne $_ } @{$nodes};
  931         882  
  184         186  
418             }
419             sub dump
420             {
421 0     0 0   my $self = shift;
422 0           my $io = shift;
423 0   0       my $indent = shift || "";
424 0           foreach my $l (@{$self->links})
  0            
425             {
426 0           $l->dump($io, $indent);
427             }
428             }
429             sub load_pack
430             {
431 0     0 0   my $self = shift;
432 0           my $class = shift;
433 0           my $data = shift;
434 0           $data .= "EOF\n";
435 0           my @lines = split "\n", $data;
436 0           load $class;
437 0           my $rel_data = "";
438 0           foreach my $l (@lines)
439             {
440 0 0         if($l !~ /^\s/)
441             {
442 0 0         if($rel_data)
443             {
444 0           my $rel = $class->load($rel_data);
445 0           $self->add_link($rel);
446             }
447 0           $rel_data = $l . "\n";
448             }
449             else
450             {
451 0           $rel_data .= $l . "\n";
452             }
453             }
454             }
455              
456             1;