File Coverage

blib/lib/Graph/Undirected/Hamiltonicity/Tests.pm
Criterion Covered Total %
statement 117 120 97.5
branch 50 58 86.2
condition n/a
subroutine 15 15 100.0
pod 8 10 80.0
total 190 203 93.6


line stmt bran cond sub pod time code
1             package Graph::Undirected::Hamiltonicity::Tests;
2              
3 6     6   11011 use Modern::Perl;
  6         18  
  6         34  
4 6     6   713 use Exporter qw(import);
  6         12  
  6         178  
5              
6 6     6   2112 use Graph::Undirected::Hamiltonicity::Transforms qw(:all);
  6         16  
  6         834  
7 6     6   42 use Graph::Undirected::Hamiltonicity::Output qw(:all);
  6         12  
  6         10611  
8              
9             our $DONT_KNOW = 0;
10             our $GRAPH_IS_HAMILTONIAN = 1;
11             our $GRAPH_IS_NOT_HAMILTONIAN = 2;
12              
13             our @EXPORT = qw($DONT_KNOW $GRAPH_IS_HAMILTONIAN $GRAPH_IS_NOT_HAMILTONIAN);
14              
15             our @EXPORT_OK = (
16             @EXPORT, qw(
17             &test_articulation_vertex
18             &test_canonical
19             &test_dirac
20             &test_graph_bridge
21             &test_min_degree
22             &test_ore
23             &test_required_max_degree
24             &test_required_connected
25             &test_required_cyclic
26             &test_trivial
27             )
28             );
29              
30             our %EXPORT_TAGS = ( all => \@EXPORT_OK );
31              
32             ##########################################################################
33              
34             sub test_trivial {
35 40     40 1 243 output("Entering test_trivial()
");
36 40         100 my ($g) = @_;
37              
38 40         164 my $e = scalar( $g->edges );
39 40         2388 my $v = scalar( $g->vertices );
40 40         719 my $max_edges = ( $v * $v - $v ) / 2;
41              
42 40 100       141 if ( $v == 1 ) {
43 1         4 return ( $GRAPH_IS_HAMILTONIAN,
44             "By convention, a graph with a single vertex is "
45             . "considered to be Hamiltonian." );
46             }
47              
48 39 100       128 if ( $v < 3 ) {
49 1         4 return ( $GRAPH_IS_NOT_HAMILTONIAN,
50             "A graph with 0 or 2 vertices cannot be Hamiltonian." );
51             }
52              
53 38 50       129 if ( $e < $v ) {
54 0         0 foreach my $vertex ( $g->vertices ) {
55 0         0 say "vertex=[$vertex]"; ### DEBUG: REMOVE!
56             }
57              
58            
59 0         0 return ( $GRAPH_IS_NOT_HAMILTONIAN,
60             "e < v, therefore the graph is not Hamiltonian. e=$e, v=$v" );
61             }
62              
63             ### If e > ( ( v * ( v - 1 ) / 2 ) - ( v - 2 ) )
64             ### the graph definitely has an HC.
65 38 100       174 if ( $e > ( $max_edges - $v + 2 ) ) {
66 2         4 my $reason = "If e > ( (v*(v-1)/2)-(v-2)), the graph is Hamiltonian.";
67 2         16 $reason .= " For v=$v, e > ";
68 2         6 $reason .= $max_edges - $v + 2;
69 2         7 return ( $GRAPH_IS_HAMILTONIAN, $reason );
70             }
71              
72 36         111 return $DONT_KNOW;
73              
74             }
75              
76             ##########################################################################
77              
78             sub test_canonical {
79 14     14 1 4870 output("Entering test_canonical()
");
80 14         23 my ($g) = @_;
81 14         37 my @vertices = sort { $a <=> $b } $g->vertices();
  189         460  
82 14         40 my $v = scalar(@vertices);
83              
84 14 100       40 if ( $g->has_edge( $vertices[0], $vertices[-1] ) ) {
85 12         507 for ( my $counter = 0; $counter < $v - 1; $counter++ ) {
86 50 100       1340 unless (
87             $g->has_edge(
88             $vertices[$counter], $vertices[ $counter + 1 ]
89             )
90             )
91             {
92 1         35 return ( $DONT_KNOW,
93             "This graph is not a supergraph of "
94             . "the canonical Hamiltonian Cycle." );
95             }
96             }
97 11         354 return ( $GRAPH_IS_HAMILTONIAN,
98             "This graph is a supergraph of "
99             . "the canonical Hamiltonian Cycle." );
100             } else {
101 2         77 return ( $DONT_KNOW,
102             "This graph is not a supergraph of "
103             . "the canonical Hamiltonian Cycle." );
104             }
105             }
106              
107             ##########################################################################
108              
109             sub test_min_degree {
110              
111 215     215 1 782 output("Entering test_min_degree()
");
112              
113 215         522 my ($g, $params) = @_;
114              
115 215         577 foreach my $vertex ( $g->vertices ) {
116 2854 100       650436 if ( $g->degree($vertex) < 2 ) {
117              
118             my $reason = $params->{transformed}
119 5 50       958 ? "After removing edges according to constraints, this graph "
120             . "was found to have a vertex ($vertex) with degree < 2"
121             : "This graph has a vertex ($vertex) with degree < 2";
122              
123 5         26 return ( $GRAPH_IS_NOT_HAMILTONIAN, $reason, $params );
124             }
125             }
126              
127 210         51492 return $DONT_KNOW;
128             }
129              
130             ##########################################################################
131              
132             sub test_articulation_vertex {
133 214     214 1 973 output("Entering test_articulation_vertex()
");
134            
135 214         570 my ($g, $params) = @_;
136 214 100       1012 return $DONT_KNOW if $g->is_biconnected();
137              
138             my $reason = $params->{transformed}
139 7 100       8441 ? "After removing edges according to constraints, the graph was no" .
140             " longer biconnected, therefore not Hamiltonian."
141             : "This graph is not biconnected, therefore not Hamiltonian. ";
142            
143 7         26 return ( $GRAPH_IS_NOT_HAMILTONIAN, $reason, $params );
144              
145             # my $vertices_string = join ',', $g->articulation_points();
146             #
147             # return ( $GRAPH_IS_NOT_HAMILTONIAN,
148             # "This graph is not biconnected, therefore not Hamiltonian. "
149             # . "It contains the following articulation vertices: "
150             # . "($vertices_string)" );
151              
152             }
153              
154             ##########################################################################
155              
156             sub test_graph_bridge {
157 208     208 1 835 output("Entering test_graph_bridge()
");
158 208         503 my ($g, $params) = @_;
159 208 100       766 return $DONT_KNOW if $g->is_edge_connected();
160              
161              
162             my $reason = $params->{transformed}
163 1 50       684 ? "After removing edges according to constraints, the graph was " .
164             "found to have a bridge, and is therefore, not Hamiltonian."
165             : "This graph has a bridge, and is therefore not Hamiltonian.";
166              
167 1         4 return ( $GRAPH_IS_NOT_HAMILTONIAN, $reason, $params );
168              
169             # my $bridge_string = join ',', map { sprintf "%d=%d", @$_ } $g->bridges();
170             #
171             # return ( $GRAPH_IS_NOT_HAMILTONIAN,
172             # "This graph is not edge-connected, therefore not Hamiltonian. "
173             # . " It contains the following bridges ($bridge_string)." );
174              
175             }
176              
177             ##########################################################################
178              
179             ### A simple graph with n vertices (n >= 3) is Hamiltonian if every vertex
180             ### has degree n / 2 or greater. -- Dirac (1952)
181             ### https://en.wikipedia.org/wiki/Hamiltonian_path
182              
183             sub test_dirac {
184              
185 40     40 1 149 output("Entering test_dirac()
");
186              
187 40         111 my ($g) = @_;
188 40         118 my $v = $g->vertices();
189 40 100       629 return $DONT_KNOW if $v < 3;
190              
191 38         91 my $half_v = $v / 2;
192              
193 38         106 foreach my $vertex ( $g->vertices() ) {
194 47 100       3058 if ( $g->degree($vertex) < $half_v ) {
195 36         11599 return $DONT_KNOW;
196             }
197             }
198              
199 2         453 return ($GRAPH_IS_HAMILTONIAN,
200             "Every vertex has degree $half_v or more.");
201              
202             }
203              
204             ##########################################################################
205              
206             ### A graph with n vertices (n >= 3) is Hamiltonian if,
207             ### for every pair of non-adjacent vertices, the sum of their degrees
208             ### is n or greater (see Ore's theorem).
209             ### https://en.wikipedia.org/wiki/Ore%27s_theorem
210              
211             sub test_ore {
212 219     219 0 781 output("Entering test_ore()
");
213              
214 219         546 my ($g, $params) = @_;
215 219         719 my $v = $g->vertices();
216 219 100       4505 return $DONT_KNOW if $v < 3;
217              
218 218         556 foreach my $vertex1 ( $g->vertices() ) {
219 483         3911 foreach my $vertex2 ( $g->vertices() ) {
220 556 100       9867 last if $vertex1 == $vertex2;
221 289 100       864 next if $g->has_edge($vertex1, $vertex2);
222 227         9646 my $sum_of_degrees = $g->degree($vertex1) + $g->degree($vertex2);
223 227 100       116078 return $DONT_KNOW if $sum_of_degrees < $v;
224             }
225             }
226              
227 2         5 my $reason = "The sum of degrees of each pair of non-adjacent vertices";
228 2         5 $reason .= " >= v.";
229 2         4 $reason .= " ( Ore's Theorem. )";
230              
231 2         7 return ($GRAPH_IS_HAMILTONIAN, $reason, $params);
232              
233             }
234              
235             ##########################################################################
236              
237             sub test_required_max_degree {
238 194     194 1 659 output("Entering test_required_max_degree()
");
239              
240 194         545 my ($required_graph, $g, $params) = @_;
241            
242 194         753 foreach my $vertex ( $required_graph->vertices() ) {
243 2377         8165 my $degree = $required_graph->degree($vertex);
244 2377 100       442720 if ( $degree > 2 ) {
245             my $reason = $params->{transformed}
246 31 100       214 ? "After removing edges according to rules, the vertex $vertex "
247             . "was found to be required by $degree edges."
248             : "Vertex $vertex is required by $degree edges.";
249              
250 31         109 $reason .= " It can only be required by upto 2 edges.";
251              
252 31         156 return ( $GRAPH_IS_NOT_HAMILTONIAN, $reason, $params );
253             }
254             }
255              
256 163         902 return $DONT_KNOW;
257             }
258              
259             ##########################################################################
260              
261             sub test_required_connected {
262 163     163 0 716 output("Entering test_required_connected()
");
263              
264 163         491 my ($required_graph, $g, $params) = @_;
265              
266 163 100       835 if ( $required_graph->is_connected() ) {
267             my @degree1_vertices =
268             grep
269 32         169158 { $required_graph->degree($_) == 1 }
  397         79830  
270             $required_graph->vertices();
271              
272 32 100       6805 unless ( @degree1_vertices ) {
273 21         101 _output_cycle($required_graph);
274             my $reason = $params->{transformed}
275 21 50       91 ? "After removing edges according to rules, the required graph was "
276             . "found to be connected, with no vertices of degree 1."
277             : "The required graph is connected, and has no vertices with degree 1.";
278              
279 21         112 return ( $GRAPH_IS_HAMILTONIAN, $reason, $params );
280             }
281            
282 11 100       52 if ( $g->has_edge( @degree1_vertices ) ) {
283 7 50       304 unless ( $required_graph->has_edge(@degree1_vertices) ) {
284 7         262 $required_graph->add_edge(@degree1_vertices);
285             }
286 7         780 _output_cycle($required_graph);
287              
288             my $reason = $params->{transformed}
289 7 50       34 ? "After removing edges according to rules, the required graph was "
290             . "found to contain a Hamiltonian Cycle."
291             : "The required graph contains a Hamiltonian Cycle";
292              
293 7         38 return ( $GRAPH_IS_HAMILTONIAN, $reason, $params );
294             } else {
295             my $reason = $params->{transformed}
296 4 50       194 ? "After removing edges according to rules, the required graph was "
297             . "found to be connected, but not cyclic."
298             : "The required graph is connected, but not cyclic";
299 4         32 return ( $GRAPH_IS_NOT_HAMILTONIAN, $reason, $params );
300             }
301             }
302              
303 131         621790 return $DONT_KNOW;
304              
305             }
306              
307             ##########################################################################
308              
309             sub test_required_cyclic {
310 131     131 1 686 output("Entering test_required_cyclic()
");
311 131         368 my ($required_graph, $g, $params) = @_;
312              
313 131 100       591 if ( $required_graph->has_a_cycle ) {
314             my $reason = $params->{transformed}
315 15 50       43348 ? "After removing edges according to rules, the required graph was "
316             . "found to be cyclic, but not connected."
317             : "The required graph is cyclic, but not connected.";
318 15         88 return ( $GRAPH_IS_NOT_HAMILTONIAN, $reason, $params );
319             }
320              
321 116         595479 return $DONT_KNOW;
322             }
323              
324             ##########################################################################
325              
326             sub _output_cycle {
327 28     28   85 my ($g) = @_;
328 28         138 my @cycle = $g->find_a_cycle();
329 28         105146 my $cycle_string = join ', ', @cycle;
330 28         155 output( $g );
331 28         132 output("Found a cycle: [$cycle_string]
");
332             }
333              
334             ##########################################################################
335              
336             1; # End of Graph::Undirected::Hamiltonicity::Tests