gratr 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Grater.xcf +0 -0
- data/README +328 -0
- data/Rakefile +220 -0
- data/examples/graph_self.rb +54 -0
- data/examples/module_graph.jpg +0 -0
- data/examples/module_graph.rb +12 -0
- data/examples/self_graph.jpg +0 -0
- data/examples/visualize.jpg +0 -0
- data/examples/visualize.rb +8 -0
- data/install.rb +49 -0
- data/lib/gratr.rb +33 -0
- data/lib/gratr/adjacency_graph.rb +230 -0
- data/lib/gratr/base.rb +34 -0
- data/lib/gratr/biconnected.rb +116 -0
- data/lib/gratr/chinese_postman.rb +123 -0
- data/lib/gratr/common.rb +73 -0
- data/lib/gratr/comparability.rb +92 -0
- data/lib/gratr/digraph.rb +113 -0
- data/lib/gratr/digraph_distance.rb +185 -0
- data/lib/gratr/dot.rb +90 -0
- data/lib/gratr/edge.rb +145 -0
- data/lib/gratr/graph.rb +315 -0
- data/lib/gratr/graph_api.rb +82 -0
- data/lib/gratr/import.rb +44 -0
- data/lib/gratr/labels.rb +103 -0
- data/lib/gratr/maximum_flow.rb +107 -0
- data/lib/gratr/rdot.rb +326 -0
- data/lib/gratr/search.rb +409 -0
- data/lib/gratr/strong_components.rb +127 -0
- data/lib/gratr/undirected_graph.rb +153 -0
- data/tests/TestBiconnected.rb +53 -0
- data/tests/TestChinesePostman.rb +53 -0
- data/tests/TestComplement.rb +54 -0
- data/tests/TestDigraph.rb +333 -0
- data/tests/TestDigraphDistance.rb +138 -0
- data/tests/TestEdge.rb +171 -0
- data/tests/TestInspection.rb +57 -0
- data/tests/TestMultiEdge.rb +57 -0
- data/tests/TestNeighborhood.rb +64 -0
- data/tests/TestProperties.rb +160 -0
- data/tests/TestSearch.rb +257 -0
- data/tests/TestStrongComponents.rb +85 -0
- data/tests/TestTriagulated.rb +137 -0
- data/tests/TestUndirectedGraph.rb +219 -0
- metadata +92 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/ruby -I../lib
|
2
|
+
|
3
|
+
require 'gratr/import'
|
4
|
+
require 'gratr/dot'
|
5
|
+
|
6
|
+
# This program gives an example of dynamic analysis of a program's call stack,
|
7
|
+
# that exports to dot and creates a jpg visualization of the call diagram.
|
8
|
+
|
9
|
+
class GraphSelf
|
10
|
+
|
11
|
+
# Setup some data to call Dijkstra's Algorithm
|
12
|
+
def initialize
|
13
|
+
@d = Digraph[ [:a,:b] => 9, [:a,:e] => 3,
|
14
|
+
[:b,:c] => 2, [:b,:e] => 6,
|
15
|
+
[:c,:d] => 1,
|
16
|
+
[:d,:c] => 2,
|
17
|
+
[:e,:b] => 2, [:e,:f] => 1,
|
18
|
+
[:f,:c] => 2, [:f,:d] => 7, [:f,:e] => 2 ]
|
19
|
+
|
20
|
+
@call_stack = []
|
21
|
+
@call_graph = Digraph.new
|
22
|
+
end
|
23
|
+
|
24
|
+
# Get the call graph variable
|
25
|
+
def call_graph() @call_graph; end
|
26
|
+
|
27
|
+
# Turn capturing of call graph on
|
28
|
+
def capture_func
|
29
|
+
Proc.new do |event, f, l, id, b, klass|
|
30
|
+
# Only interested in the GRATR library itself
|
31
|
+
if ( klass.to_s =~ /GRATR/ )
|
32
|
+
case event.to_s
|
33
|
+
when /call/
|
34
|
+
method = "#{klass.to_s.split('::')[1]}.#{id}" # Removes GRATR::
|
35
|
+
@call_graph.add_edge!(@call_stack[-1],method) if @call_stack[-1]
|
36
|
+
@call_stack.push(method)
|
37
|
+
when /return/ : @call_stack.pop
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Run a capture of the call graph for Dijkstra's algorithm
|
44
|
+
def run
|
45
|
+
set_trace_func capture_func
|
46
|
+
@d.dijkstras_algorithm(:a)
|
47
|
+
set_trace_func nil
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
# Run a capture and generate the resulting jpg file
|
54
|
+
GraphSelf.new.run.call_graph.write_to_graphic_file('jpg','self_graph')
|
Binary file
|
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/ruby -I../lib
|
2
|
+
|
3
|
+
require 'gratr/import'
|
4
|
+
require 'gratr/dot'
|
5
|
+
|
6
|
+
module_graph=Digraph.new
|
7
|
+
ObjectSpace.each_object(Module) do |m|
|
8
|
+
m.ancestors.each {|a| module_graph.add_edge!(m,a) if m != a}
|
9
|
+
end
|
10
|
+
|
11
|
+
gv = module_graph.vertices.select {|v| v.to_s.match(/GRATR/)}
|
12
|
+
module_graph.induced_subgraph(gv).write_to_graphic_file('jpg','module_graph')
|
Binary file
|
Binary file
|
data/install.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'getoptlong'
|
4
|
+
require 'rbconfig'
|
5
|
+
require 'ftools'
|
6
|
+
require 'find'
|
7
|
+
|
8
|
+
SRC_BASE = 'lib'
|
9
|
+
SRC = 'gratr'
|
10
|
+
|
11
|
+
|
12
|
+
INSTDIR = File.join Config::CONFIG['sitedir']
|
13
|
+
DESTDIR = File.join INSTDIR, SRC
|
14
|
+
|
15
|
+
opts = GetoptLong.new( [ "--uninstall", "-u", GetoptLong::NO_ARGUMENT ] )
|
16
|
+
|
17
|
+
def install
|
18
|
+
begin
|
19
|
+
File.makedirs( DESTDIR )
|
20
|
+
pwd = Dir.pwd
|
21
|
+
Dir.chdir(SRC_BASE)
|
22
|
+
Dir['*.rb'].each do |file|
|
23
|
+
dst = File.join( INSTDIR, file )
|
24
|
+
File.install(file, dst, 0644, true)
|
25
|
+
end
|
26
|
+
Find.find(SRC) do |file|
|
27
|
+
dst = File.join( INSTDIR, file )
|
28
|
+
File.install(file, dst, 0644, true) if file =~ /.rb$/
|
29
|
+
end
|
30
|
+
Dir.chdir(pwd)
|
31
|
+
rescue
|
32
|
+
puts $!
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def uninstall
|
37
|
+
begin
|
38
|
+
puts "Deleting:"
|
39
|
+
Find.find(DESTDIR) { |file| File.rm_f file,true }
|
40
|
+
Dir.delete DESTDIR
|
41
|
+
rescue
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
if (opt = opts.get) and opt[0] =~ /^-?-u/
|
46
|
+
uninstall
|
47
|
+
else
|
48
|
+
install
|
49
|
+
end
|
data/lib/gratr.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2006 Shawn Patrick Garbett
|
3
|
+
# Copyright (c) 2002,2004,2005 by Horst Duchene
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
6
|
+
# are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright notice(s),
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
# * Neither the name of the Shawn Garbett nor the names of its contributors
|
14
|
+
# may be used to endorse or promote products derived from this software
|
15
|
+
# without specific prior written permission.
|
16
|
+
#
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
18
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
19
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
20
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
21
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
23
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
24
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
25
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
26
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27
|
+
#++
|
28
|
+
|
29
|
+
|
30
|
+
require 'gratr/base'
|
31
|
+
require 'gratr/digraph'
|
32
|
+
require 'gratr/undirected_graph'
|
33
|
+
require 'gratr/common'
|
@@ -0,0 +1,230 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2006 Shawn Patrick Garbett
|
3
|
+
# Copyright (c) 2002,2004,2005 by Horst Duchene
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
6
|
+
# are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright notice(s),
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
# * Neither the name of the Shawn Garbett nor the names of its contributors
|
14
|
+
# may be used to endorse or promote products derived from this software
|
15
|
+
# without specific prior written permission.
|
16
|
+
#
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
18
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
19
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
20
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
21
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
23
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
24
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
25
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
26
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27
|
+
#++
|
28
|
+
|
29
|
+
|
30
|
+
require 'gratr/edge'
|
31
|
+
require 'gratr/graph'
|
32
|
+
require 'set'
|
33
|
+
|
34
|
+
module GRATR
|
35
|
+
|
36
|
+
# This provides the basic routines needed to implement the Digraph, UndirectedGraph,
|
37
|
+
# PseudoGraph, DirectedPseudoGraph, MultiGraph and DirectedPseudoGraph class.
|
38
|
+
module AdjacencyGraph
|
39
|
+
|
40
|
+
include Graph
|
41
|
+
|
42
|
+
class ArrayWithAdd < Array # :nodoc:
|
43
|
+
alias add push
|
44
|
+
end
|
45
|
+
|
46
|
+
# Initialization parameters can include an Array of edges to add, Graphs to
|
47
|
+
# copy (will merge if multiple)
|
48
|
+
# :parallel_edges denotes that duplicate edges are allowed
|
49
|
+
# :loops denotes that loops are allowed
|
50
|
+
def initialize(*params)
|
51
|
+
@vertex_dict = Hash.new
|
52
|
+
raise ArgumentError if params.any? do |p|
|
53
|
+
!(p.kind_of? GRATR::Graph or
|
54
|
+
p.kind_of? Array or
|
55
|
+
p == :parallel_edges or
|
56
|
+
p == :loops)
|
57
|
+
end
|
58
|
+
clear_all_labels
|
59
|
+
|
60
|
+
# Basic configuration of adjacency
|
61
|
+
@allow_loops = params.any? {|p| p == :loops}
|
62
|
+
@parallel_edges = params.any? {|p| p == :parallel_edges}
|
63
|
+
@edgelist_class = @parallel_edges ? ArrayWithAdd : Set
|
64
|
+
if @parallel_edges
|
65
|
+
@edge_number = Hash.new
|
66
|
+
@next_edge_number = 0
|
67
|
+
end
|
68
|
+
|
69
|
+
# Copy any given graph into this graph
|
70
|
+
params.select {|p| p.kind_of? GRATR::Graph}.each do |g|
|
71
|
+
g.edges.each do |e|
|
72
|
+
add_edge!(e)
|
73
|
+
edge_label_set(e, edge_label(e)) if edge_label(e)
|
74
|
+
end
|
75
|
+
g.vertices.each do |v|
|
76
|
+
vertex_label_set(v, vertex_label(v)) if vertex_label(v)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Add all array edges specified
|
81
|
+
params.select {|p| p.kind_of? Array}.each do |a|
|
82
|
+
0.step(a.size-1, 2) {|i| add_edge!(a[i], a[i+1])}
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
# Returns true if v is a vertex of this Graph
|
88
|
+
# An O(1) implementation of vertex?
|
89
|
+
def vertex?(v) @vertex_dict.has_key?(v); end
|
90
|
+
|
91
|
+
# Returns true if [u,v] or u is an Arc
|
92
|
+
# An O(1) implementation
|
93
|
+
def edge?(u, v=nil)
|
94
|
+
u, v = u.source, u.target if u.kind_of? GRATR::Arc
|
95
|
+
vertex?(u) and @vertex_dict[u].include?(v)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Adds a vertex to the graph with an optional label
|
99
|
+
def add_vertex!(vertex, label=nil)
|
100
|
+
@vertex_dict[vertex] ||= @edgelist_class.new
|
101
|
+
self[vertex] = label if label
|
102
|
+
self
|
103
|
+
end
|
104
|
+
|
105
|
+
# Adds an edge to the graph
|
106
|
+
# Can be called in two basic ways, label is optional
|
107
|
+
# * add_edge!(Arc[source,target], "Label")
|
108
|
+
# * add_edge!(source,target, "Label")
|
109
|
+
def add_edge!(u, v=nil, l=nil, n=nil)
|
110
|
+
n = u.number if u.class.include? ArcNumber and n.nil?
|
111
|
+
u, v, l = u.source, u.target, u.label if u.kind_of? GRATR::Arc
|
112
|
+
return self if not @allow_loops and u == v
|
113
|
+
n = (@next_edge_number+=1) unless n if @parallel_edges
|
114
|
+
add_vertex!(u); add_vertex!(v)
|
115
|
+
@vertex_dict[u].add(v)
|
116
|
+
(@edge_number[u] ||= @edgelist_class.new).add(n) if @parallel_edges
|
117
|
+
unless directed?
|
118
|
+
@vertex_dict[v].add(u)
|
119
|
+
(@edge_number[v] ||= @edgelist_class.new).add(n) if @parallel_edges
|
120
|
+
end
|
121
|
+
self[n ? edge_class[u,v,n] : edge_class[u,v]] = l if l
|
122
|
+
self
|
123
|
+
end
|
124
|
+
|
125
|
+
# Removes a given vertex from the graph
|
126
|
+
def remove_vertex!(v)
|
127
|
+
# FIXME This is broken for multi graphs
|
128
|
+
@vertex_dict.delete(v)
|
129
|
+
@vertex_dict.each_value { |adjList| adjList.delete(v) }
|
130
|
+
@vertex_dict.keys.each do |u|
|
131
|
+
delete_label(edge_class[u,v])
|
132
|
+
delete_label(edge_class[v,u])
|
133
|
+
end
|
134
|
+
delete_label(v)
|
135
|
+
self
|
136
|
+
end
|
137
|
+
|
138
|
+
# Removes an edge from the graph, can be called with source and target or with
|
139
|
+
# and object of GRATR::Arc derivation
|
140
|
+
def remove_edge!(u, v=nil)
|
141
|
+
unless u.kind_of? GRATR::Arc
|
142
|
+
raise ArgumentError if @parallel_edges
|
143
|
+
u = edge_class[u,v]
|
144
|
+
end
|
145
|
+
raise ArgumentError if @parallel_edges and (u.number || 0) == 0
|
146
|
+
return self unless @vertex_dict[u.source] # It doesn't exist
|
147
|
+
delete_label(u) # Get rid of label
|
148
|
+
if @parallel_edges
|
149
|
+
index = @edge_number[u.source].index(u.number)
|
150
|
+
raise NoArcError unless index
|
151
|
+
@vertex_dict[u.source].delete_at(index)
|
152
|
+
@edge_number[u.source].delete_at(index)
|
153
|
+
else
|
154
|
+
@vertex_dict[u.source].delete(u.target)
|
155
|
+
end
|
156
|
+
self
|
157
|
+
end
|
158
|
+
|
159
|
+
# Returns an array of vertices that the graph has
|
160
|
+
def vertices() @vertex_dict.keys; end
|
161
|
+
|
162
|
+
# Returns an array of edges, most likely of class Arc or Edge depending
|
163
|
+
# upon the type of graph
|
164
|
+
def edges
|
165
|
+
@vertex_dict.keys.inject(Set.new) do |a,v|
|
166
|
+
if @parallel_edges and @edge_number[v]
|
167
|
+
@vertex_dict[v].zip(@edge_number[v]).each do |w|
|
168
|
+
s,t,n = v,w[0],w[1]
|
169
|
+
a.add( edge_class[ s,t,n, edge_label(s,t,n) ] )
|
170
|
+
end
|
171
|
+
else
|
172
|
+
@vertex_dict[v].each do |w|
|
173
|
+
a.add(edge_class[v,w,edge_label(v,w)])
|
174
|
+
end
|
175
|
+
end; a
|
176
|
+
end.to_a
|
177
|
+
end
|
178
|
+
|
179
|
+
alias graph_adjacent adjacent
|
180
|
+
def adjacent(x, options={})
|
181
|
+
options[:direction] ||= :out
|
182
|
+
if !x.kind_of?(GRATR::Arc) and (options[:direction] == :out || !directed?)
|
183
|
+
if options[:type] == :edges
|
184
|
+
i = -1
|
185
|
+
@parallel_edges ?
|
186
|
+
@vertex_dict[x].map {|v| e=edge_class[x,v,@edge_number[x][i+=1]]; e.label = self[e]; e} :
|
187
|
+
@vertex_dict[x].map {|v| e=edge_class[x,v]; e.label = self[e]; e}
|
188
|
+
else
|
189
|
+
@vertex_dict[x].to_a
|
190
|
+
end
|
191
|
+
else
|
192
|
+
graph_adjacent(x,options)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
public
|
198
|
+
|
199
|
+
def self.included(cl)
|
200
|
+
# Shortcut for creating a Graph
|
201
|
+
#
|
202
|
+
# Example: GRATR::Digraph[1,2, 2,3, 2,4, 4,5].edges.to_a.to_s =>
|
203
|
+
# "(1-2)(2-3)(2-4)(4-5)"
|
204
|
+
#
|
205
|
+
# Or as a Hash for specifying lables
|
206
|
+
# GRATR::Digraph[ [:a,:b] => 3, [:b,:c] => 4 ] (Note: Do not use for Multi or Pseudo graphs)
|
207
|
+
def cl.[] (*a)
|
208
|
+
result = new
|
209
|
+
if a.size == 1 and a[0].kind_of? Hash
|
210
|
+
# Convert to edge class
|
211
|
+
a[0].each do |k,v|
|
212
|
+
if result.edge_class.include? GRATR::ArcNumber
|
213
|
+
result.add_edge!(result.edge_class[k[0],k[1],nil,v])
|
214
|
+
else
|
215
|
+
result.add_edge!(result.edge_class[k[0],k[1],v])
|
216
|
+
end
|
217
|
+
end
|
218
|
+
elsif a[0].kind_of? GRATR::Arc
|
219
|
+
a.each{|e| result.add_edge!(e); result[e] = e.label}
|
220
|
+
elsif a.size % 2 == 0
|
221
|
+
0.step(a.size-1, 2) {|i| result.add_edge!(a[i], a[i+1])}
|
222
|
+
else
|
223
|
+
raise ArgumentError
|
224
|
+
end
|
225
|
+
result
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
end # Adjacency Graph
|
230
|
+
end # GRATR
|
data/lib/gratr/base.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2006 Shawn Patrick Garbett
|
3
|
+
# Copyright (c) 2002,2004,2005 by Horst Duchene
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
6
|
+
# are permitted provided that the following conditions are met:
|
7
|
+
#
|
8
|
+
# * Redistributions of source code must retain the above copyright notice(s),
|
9
|
+
# this list of conditions and the following disclaimer.
|
10
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
# this list of conditions and the following disclaimer in the documentation
|
12
|
+
# and/or other materials provided with the distribution.
|
13
|
+
# * Neither the name of the Shawn Garbett nor the names of its contributors
|
14
|
+
# may be used to endorse or promote products derived from this software
|
15
|
+
# without specific prior written permission.
|
16
|
+
#
|
17
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
18
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
19
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
20
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
21
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
22
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
23
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
24
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
25
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
26
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
27
|
+
#++
|
28
|
+
|
29
|
+
GRATR_VERSION = "0.4.2"
|
30
|
+
|
31
|
+
module GRATR
|
32
|
+
class NoVertexError < IndexError; end
|
33
|
+
class NoArcError < IndexError; end
|
34
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2006 Shawn Patrick Garbett
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
# are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# * Redistributions of source code must retain the above copyright notice(s),
|
8
|
+
# this list of conditions and the following disclaimer.
|
9
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer in the documentation
|
11
|
+
# and/or other materials provided with the distribution.
|
12
|
+
# * Neither the name of the Shawn Garbett nor the names of its contributors
|
13
|
+
# may be used to endorse or promote products derived from this software
|
14
|
+
# without specific prior written permission.
|
15
|
+
#
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
17
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
18
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
19
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
20
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
21
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
22
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
23
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
24
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
25
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
26
|
+
#++
|
27
|
+
|
28
|
+
|
29
|
+
require 'set'
|
30
|
+
|
31
|
+
module GRATR
|
32
|
+
module Graph
|
33
|
+
# Biconnected is a module for adding the biconnected algorithm to
|
34
|
+
# UndirectedGraphs
|
35
|
+
module Biconnected
|
36
|
+
|
37
|
+
# biconnected computes the biconnected subgraphs
|
38
|
+
# of a graph using Tarjan's algorithm based on DFS. See: Robert E. Tarjan
|
39
|
+
# _Depth_First_Search_and_Linear_Graph_Algorithms_. SIAM Journal on
|
40
|
+
# Computing, 1(2):146-160, 1972
|
41
|
+
#
|
42
|
+
# The output of the algorithm is a pair, the first value is an
|
43
|
+
# array of biconnected subgraphs. The second is the set of
|
44
|
+
# articulation vertices.
|
45
|
+
#
|
46
|
+
# A connected graph is biconnected if the removal of any single vertex
|
47
|
+
# (and all edges incident on that vertex) cannot disconnect the graph.
|
48
|
+
# More generally, the biconnected components of a graph are the maximal
|
49
|
+
# subsets of vertices such that the removal of a vertex from a particular
|
50
|
+
# component will not disconnect the component. Unlike connected components,
|
51
|
+
# vertices may belong to multiple biconnected components: those vertices
|
52
|
+
# that belong to more than one biconnected component are called articulation
|
53
|
+
# points or, equivalently, cut vertices. Articulation points are vertices
|
54
|
+
# whose removal would increase the number of connected components in the graph.
|
55
|
+
# Thus, a graph without articulation points is biconnected.
|
56
|
+
def biconnected
|
57
|
+
dfs_num = 0
|
58
|
+
number = {}; predecessor = {}; low_point = {}
|
59
|
+
stack = []; result = []; articulation= []
|
60
|
+
|
61
|
+
root_vertex = Proc.new {|v| predecessor[v]=v }
|
62
|
+
enter_vertex = Proc.new {|u| number[u]=low_point[u]=(dfs_num+=1) }
|
63
|
+
tree_edge = Proc.new do |e|
|
64
|
+
stack.push(e)
|
65
|
+
predecessor[e.target] = e.source
|
66
|
+
end
|
67
|
+
back_edge = Proc.new do |e|
|
68
|
+
if e.target != predecessor[e.source]
|
69
|
+
stack.push(e)
|
70
|
+
low_point[e.source] = [low_point[e.source], number[e.target]].min
|
71
|
+
end
|
72
|
+
end
|
73
|
+
exit_vertex = Proc.new do |u|
|
74
|
+
parent = predecessor[u]
|
75
|
+
is_articulation_point = false
|
76
|
+
if number[parent] > number[u]
|
77
|
+
parent = predecessor[parent]
|
78
|
+
is_articulation_point = true
|
79
|
+
end
|
80
|
+
if parent == u
|
81
|
+
is_articulation_point = false if (number[u] + 1) == number[predecessor[u]]
|
82
|
+
else
|
83
|
+
low_point[parent] = [low_point[parent], low_point[u]].min
|
84
|
+
if low_point[u] >= number[parent]
|
85
|
+
if number[parent] > number[predecessor[parent]]
|
86
|
+
predecessor[u] = predecessor[parent]
|
87
|
+
predecessor[parent] = u
|
88
|
+
end
|
89
|
+
result << (component = self.class.new)
|
90
|
+
while number[stack[-1].source] >= number[u]
|
91
|
+
component.add_edge!(stack.pop)
|
92
|
+
end
|
93
|
+
component.add_edge!(stack.pop)
|
94
|
+
if stack.empty?
|
95
|
+
predecessor[u] = parent
|
96
|
+
predecessor[parent] = u
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
articulation << u if is_articulation_point
|
101
|
+
end
|
102
|
+
|
103
|
+
# Execute depth first search
|
104
|
+
dfs({:root_vertex => root_vertex,
|
105
|
+
:enter_vertex => enter_vertex,
|
106
|
+
:tree_edge => tree_edge,
|
107
|
+
:back_edge => back_edge,
|
108
|
+
:exit_vertex => exit_vertex})
|
109
|
+
|
110
|
+
[result, articulation]
|
111
|
+
end # biconnected
|
112
|
+
|
113
|
+
end # Biconnected
|
114
|
+
|
115
|
+
end # Graph
|
116
|
+
end # GRATR
|