File Coverage

blib/lib/Graph/Undirected/Hamiltonicity/Tests.pm
Criterion Covered Total %
statement 117 120 97.5
branch 49 58 84.4
condition n/a
subroutine 15 15 100.0
pod 8 10 80.0
total 189 203 93.1


line stmt bran cond sub pod time code
1             package Graph::Undirected::Hamiltonicity::Tests;
2              
3 6     6   11156 use Modern::Perl;
  6         29  
  6         42  
4 6     6   863 use Exporter qw(import);
  6         15  
  6         182  
5              
6 6     6   2075 use Graph::Undirected::Hamiltonicity::Transforms qw(:all);
  6         17  
  6         916  
7 6     6   67 use Graph::Undirected::Hamiltonicity::Output qw(:all);
  6         95  
  6         10695  
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 227 output("Entering test_trivial()
");
36 40         101 my ($g) = @_;
37              
38 40         177 my $e = scalar( $g->edges );
39 40         2541 my $v = scalar( $g->vertices );
40 40         751 my $max_edges = ( $v * $v - $v ) / 2;
41              
42 40 100       158 if ( $v == 1 ) {
43 1         5 return ( $GRAPH_IS_HAMILTONIAN,
44             "By convention, a graph with a single vertex is "
45             . "considered to be Hamiltonian." );
46             }
47              
48 39 100       137 if ( $v < 3 ) {
49 1         6 return ( $GRAPH_IS_NOT_HAMILTONIAN,
50             "A graph with 0 or 2 vertices cannot be Hamiltonian." );
51             }
52              
53 38 50       139 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       149 if ( $e > ( $max_edges - $v + 2 ) ) {
66 2         5 my $reason = "If e > ( (v*(v-1)/2)-(v-2)), the graph is Hamiltonian.";
67 2         18 $reason .= " For v=$v, e > ";
68 2         7 $reason .= $max_edges - $v + 2;
69 2         9 return ( $GRAPH_IS_HAMILTONIAN, $reason );
70             }
71              
72 36         128 return $DONT_KNOW;
73              
74             }
75              
76             ##########################################################################
77              
78             sub test_canonical {
79 14     14 1 6134 output("Entering test_canonical()
");
80 14         30 my ($g) = @_;
81 14         52 my @vertices = sort { $a <=> $b } $g->vertices();
  191         576  
82 14         54 my $v = scalar(@vertices);
83              
84 14 100       70 if ( $g->has_edge( $vertices[0], $vertices[-1] ) ) {
85 12         690 for ( my $counter = 0; $counter < $v - 1; $counter++ ) {
86 50 100       1354 unless (
87             $g->has_edge(
88             $vertices[$counter], $vertices[ $counter + 1 ]
89             )
90             )
91             {
92 1         34 return ( $DONT_KNOW,
93             "This graph is not a supergraph of "
94             . "the canonical Hamiltonian Cycle." );
95             }
96             }
97 11         382 return ( $GRAPH_IS_HAMILTONIAN,
98             "This graph is a supergraph of "
99             . "the canonical Hamiltonian Cycle." );
100             } else {
101 2         75 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 200     200 1 680 output("Entering test_min_degree()
");
112              
113 200         526 my ($g, $params) = @_;
114              
115 200         574 foreach my $vertex ( $g->vertices ) {
116 2627 100       591952 if ( $g->degree($vertex) < 2 ) {
117              
118             my $reason = $params->{transformed}
119 2 50       349 ? "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 2         8 return ( $GRAPH_IS_NOT_HAMILTONIAN, $reason, $params );
124             }
125             }
126              
127 198         50030 return $DONT_KNOW;
128             }
129              
130             ##########################################################################
131              
132             sub test_articulation_vertex {
133 202     202 1 870 output("Entering test_articulation_vertex()
");
134            
135 202         480 my ($g, $params) = @_;
136 202 100       928 return $DONT_KNOW if $g->is_biconnected();
137              
138             my $reason = $params->{transformed}
139 2 50       2803 ? "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 2         10 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 201     201 1 779 output("Entering test_graph_bridge()
");
158 201         562 my ($g, $params) = @_;
159 201 100       690 return $DONT_KNOW if $g->is_edge_connected();
160              
161              
162             my $reason = $params->{transformed}
163 1 50       715 ? "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 163 output("Entering test_dirac()
");
186              
187 40         88 my ($g) = @_;
188 40         124 my $v = $g->vertices();
189 40 100       628 return $DONT_KNOW if $v < 3;
190              
191 38         90 my $half_v = $v / 2;
192              
193 38         112 foreach my $vertex ( $g->vertices() ) {
194 76 100       15166 if ( $g->degree($vertex) < $half_v ) {
195 34         10987 return $DONT_KNOW;
196             }
197             }
198              
199 4         1308 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 204     204 0 664 output("Entering test_ore()
");
213              
214 204         535 my ($g, $params) = @_;
215 204         643 my $v = $g->vertices();
216 204 100       4286 return $DONT_KNOW if $v < 3;
217              
218 203         562 foreach my $vertex1 ( $g->vertices() ) {
219 462         3590 foreach my $vertex2 ( $g->vertices() ) {
220 567 100       10712 last if $vertex1 == $vertex2;
221 306 100       898 next if $g->has_edge($vertex1, $vertex2);
222 216         9215 my $sum_of_degrees = $g->degree($vertex1) + $g->degree($vertex2);
223 216 100       110880 return $DONT_KNOW if $sum_of_degrees < $v;
224             }
225             }
226              
227 2         4 my $reason = "The sum of degrees of each pair of non-adjacent vertices";
228 2         4 $reason .= " >= v.";
229 2         5 $reason .= " ( Ore's Theorem. )";
230              
231 2         5 return ($GRAPH_IS_HAMILTONIAN, $reason, $params);
232              
233             }
234              
235             ##########################################################################
236              
237             sub test_required_max_degree {
238 187     187 1 638 output("Entering test_required_max_degree()
");
239              
240 187         516 my ($required_graph, $g, $params) = @_;
241            
242 187         662 foreach my $vertex ( $required_graph->vertices() ) {
243 2302         8087 my $degree = $required_graph->degree($vertex);
244 2302 100       424961 if ( $degree > 2 ) {
245             my $reason = $params->{transformed}
246 25 100       167 ? "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 25         77 $reason .= " It can only be required by upto 2 edges.";
251              
252 25         135 return ( $GRAPH_IS_NOT_HAMILTONIAN, $reason, $params );
253             }
254             }
255              
256 162         924 return $DONT_KNOW;
257             }
258              
259             ##########################################################################
260              
261             sub test_required_connected {
262 162     162 0 674 output("Entering test_required_connected()
");
263              
264 162         397 my ($required_graph, $g, $params) = @_;
265              
266 162 100       772 if ( $required_graph->is_connected() ) {
267             my @degree1_vertices =
268             grep
269 30         157503 { $required_graph->degree($_) == 1 }
  374         74418  
270             $required_graph->vertices();
271              
272 30 100       6325 unless ( @degree1_vertices ) {
273 19         83 _output_cycle($required_graph);
274             my $reason = $params->{transformed}
275 19 50       87 ? "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 19         102 return ( $GRAPH_IS_HAMILTONIAN, $reason, $params );
280             }
281            
282 11 100       49 if ( $g->has_edge( @degree1_vertices ) ) {
283 7 50       313 unless ( $required_graph->has_edge(@degree1_vertices) ) {
284 7         263 $required_graph->add_edge(@degree1_vertices);
285             }
286 7         938 _output_cycle($required_graph);
287              
288             my $reason = $params->{transformed}
289 7 50       36 ? "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         46 return ( $GRAPH_IS_HAMILTONIAN, $reason, $params );
294             } else {
295             my $reason = $params->{transformed}
296 4 50       176 ? "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         24 return ( $GRAPH_IS_NOT_HAMILTONIAN, $reason, $params );
300             }
301             }
302              
303 132         607335 return $DONT_KNOW;
304              
305             }
306              
307             ##########################################################################
308              
309             sub test_required_cyclic {
310 132     132 1 746 output("Entering test_required_cyclic()
");
311 132         370 my ($required_graph, $g, $params) = @_;
312              
313 132 100       643 if ( $required_graph->has_a_cycle ) {
314             my $reason = $params->{transformed}
315 14 50       37445 ? "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 14         82 return ( $GRAPH_IS_NOT_HAMILTONIAN, $reason, $params );
319             }
320              
321 118         583002 return $DONT_KNOW;
322             }
323              
324             ##########################################################################
325              
326             sub _output_cycle {
327 26     26   75 my ($g) = @_;
328 26         126 my @cycle = $g->find_a_cycle();
329 26         99185 my $cycle_string = join ', ', @cycle;
330 26         133 output( $g );
331 26         112 output("Found a cycle: [$cycle_string]
");
332             }
333              
334             ##########################################################################
335              
336             1; # End of Graph::Undirected::Hamiltonicity::Tests