File Coverage

blib/lib/Graph/MoreUtils/SSSR.pm
Criterion Covered Total %
statement 41 51 80.3
branch 24 28 85.7
condition 9 9 100.0
subroutine 5 6 83.3
pod 0 4 0.0
total 79 98 80.6


line stmt bran cond sub pod time code
1             package Graph::MoreUtils::SSSR;
2              
3             # ABSTRACT: Find the Smallest Set of Smallest Rings in graph
4             our $VERSION = '0.3.0'; # VERSION
5              
6 8     8   56 use strict;
  8         16  
  8         352  
7 8     8   44 use warnings;
  8         15  
  8         6708  
8              
9             sub SSSR
10             {
11 2     2 0 5 my( $graph, $max_depth ) = @_;
12              
13             return
14 2         10 map { detect_rings( $graph, $_, undef, undef, $max_depth ) }
  12         80  
15             $graph->vertices;
16             }
17              
18             # This subroutine will return cycle base not containing 1-vertex-connected graphs.
19             # TODO: Finish
20             sub get_cycle_base
21             {
22 0     0 0 0 my( $graph, $max_depth ) = @_;
23              
24 0         0 my @SSSR = SSSR( $graph, $max_depth );
25 0         0 my %edge_participation;
26 0         0 for my $cycle (@SSSR) {
27 0         0 for my $i (0..$#$cycle) {
28 0         0 my $edge = join '', $cycle->[$i % @$cycle],
29             $cycle->[($i+1) % @$cycle];
30 0 0       0 $edge_participation{$edge} = [] unless $edge_participation{$edge};
31 0         0 push @{$edge_participation{$edge}}, $cycle;
  0         0  
32             }
33             }
34              
35             # TODO: Cycle through all mutual edges and perform cycle addition
36             }
37              
38             sub detect_rings
39             {
40 120     120 0 251 my ( $graph, $atom, $original_atom, $previous_atom,
41             $level, $seen_atoms ) = @_;
42              
43 120 100 100     348 return () if defined $level && !$level;
44              
45 108 100       179 $seen_atoms = {} unless defined $seen_atoms;
46 108 100       165 $original_atom = $atom unless defined $original_atom;
47              
48 108         421 my %seen_atoms = ( %$seen_atoms,
49             $atom => { atom => $atom,
50             position => scalar keys %$seen_atoms } );
51              
52 108         169 my @rings;
53              
54             # First, look if we have Nachbarpunkte of the current path
55             # _different_ from the original atom. If yes, we will discard this
56             # cycle since it could be closed in a shorter way:
57              
58 108         254 for my $neighbour_atom ( $graph->neighbours( $atom ) ) {
59 216 100       7911 next if $neighbour_atom eq $original_atom;
60 180 100 100     493 next if defined $previous_atom && $previous_atom eq $neighbour_atom;
61 108 50       253 next if !exists $seen_atoms->{$neighbour_atom};
62              
63 0         0 return @rings;
64             }
65              
66             # If no Nachbarpunkte are found in the previous search, let's look
67             # if we can close the ring. If we do so, we set the
68             # $Nachbarpunkte_detected flag, so that the search for rings does
69             # not go on (the current atom and the original atom would be
70             # Nachbarpunkte in any larger cycle containing the current path:
71              
72 108 100       256 if( scalar keys %seen_atoms > 2 ) {
73 72         171 for my $neighbour_atom ( $graph->neighbours( $atom ) ) {
74 137 100       5190 next if $neighbour_atom ne $original_atom;
75              
76             # Detect a ring:
77              
78             my @sorted_ring =
79 72         102 sort_ring_elements( map { $seen_atoms{$_}->{atom} }
80 12         40 sort { $seen_atoms{$a}->{position} <=>
81 120         162 $seen_atoms{$b}->{position} }
82             keys %seen_atoms );
83 12         50 return @rings, \@sorted_ring;
84             }
85             }
86              
87             # Descend the new path in the neighbourhood graph:
88 96         209 for my $neighbour_atom ( $graph->neighbours( $atom ) ) {
89 192 100       7416 next if exists $seen_atoms->{$neighbour_atom};
90            
91 108 100       336 push @rings,
92             detect_rings( $graph,
93             $neighbour_atom,
94             $original_atom,
95             $atom,
96             defined $level ? $level - 1 : undef,
97             \%seen_atoms );
98             }
99              
100 96         297 return @rings;
101             }
102              
103             sub sort_ring_elements
104             {
105 12     12 0 41 my( @elements ) = @_;
106              
107 12 50       24 return @elements if scalar @elements <= 1;
108              
109 12         16 my $min_index;
110             my $reverse;
111 12         23 for my $i (0..$#elements) {
112 72 100 100     149 next if defined $min_index && $elements[$i] ge
113             $elements[$min_index];
114 32         35 $min_index = $i;
115 32         76 $reverse = $elements[($i-1) % scalar @elements] lt
116             $elements[($i+1) % scalar @elements];
117             }
118              
119 12 100       21 if( $reverse ) {
120 6         7 @elements = reverse @elements;
121 6         7 $min_index = $#elements - $min_index;
122             }
123              
124 12         51 return @elements[$min_index..$#elements],
125             @elements[0..$min_index-1];
126             }
127              
128             1;