rgl 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. data/ChangeLog +74 -0
  2. data/Makefile +72 -0
  3. data/README +240 -0
  4. data/Rakefile +210 -0
  5. data/TAGS +209 -0
  6. data/examples/canvas.rb +103 -0
  7. data/examples/codegraph +238 -0
  8. data/examples/example.jpg +0 -0
  9. data/examples/examples.rb +112 -0
  10. data/examples/graph.dot +54 -0
  11. data/examples/graph.png +0 -0
  12. data/examples/module_graph.jpg +0 -0
  13. data/examples/north.rb +12 -0
  14. data/examples/north/Graph.log +128 -0
  15. data/examples/north/g.10.0.graphml +28 -0
  16. data/examples/north/g.10.1.graphml +28 -0
  17. data/examples/north/g.10.11.graphml +31 -0
  18. data/examples/north/g.10.12.graphml +27 -0
  19. data/examples/north/g.10.13.graphml +27 -0
  20. data/examples/north/g.10.14.graphml +27 -0
  21. data/examples/north/g.10.15.graphml +26 -0
  22. data/examples/north/g.10.16.graphml +26 -0
  23. data/examples/north/g.10.17.graphml +26 -0
  24. data/examples/north/g.10.19.graphml +37 -0
  25. data/examples/north/g.10.2.graphml +28 -0
  26. data/examples/north/g.10.20.graphml +38 -0
  27. data/examples/north/g.10.22.graphml +43 -0
  28. data/examples/north/g.10.24.graphml +30 -0
  29. data/examples/north/g.10.25.graphml +45 -0
  30. data/examples/north/g.10.27.graphml +38 -0
  31. data/examples/north/g.10.28.graphml +30 -0
  32. data/examples/north/g.10.29.graphml +38 -0
  33. data/examples/north/g.10.3.graphml +26 -0
  34. data/examples/north/g.10.30.graphml +34 -0
  35. data/examples/north/g.10.31.graphml +42 -0
  36. data/examples/north/g.10.34.graphml +42 -0
  37. data/examples/north/g.10.37.graphml +28 -0
  38. data/examples/north/g.10.38.graphml +38 -0
  39. data/examples/north/g.10.39.graphml +36 -0
  40. data/examples/north/g.10.4.graphml +26 -0
  41. data/examples/north/g.10.40.graphml +37 -0
  42. data/examples/north/g.10.41.graphml +37 -0
  43. data/examples/north/g.10.42.graphml +26 -0
  44. data/examples/north/g.10.45.graphml +28 -0
  45. data/examples/north/g.10.46.graphml +32 -0
  46. data/examples/north/g.10.5.graphml +31 -0
  47. data/examples/north/g.10.50.graphml +30 -0
  48. data/examples/north/g.10.56.graphml +29 -0
  49. data/examples/north/g.10.57.graphml +32 -0
  50. data/examples/north/g.10.58.graphml +32 -0
  51. data/examples/north/g.10.6.graphml +26 -0
  52. data/examples/north/g.10.60.graphml +32 -0
  53. data/examples/north/g.10.61.graphml +34 -0
  54. data/examples/north/g.10.62.graphml +34 -0
  55. data/examples/north/g.10.68.graphml +30 -0
  56. data/examples/north/g.10.69.graphml +32 -0
  57. data/examples/north/g.10.7.graphml +29 -0
  58. data/examples/north/g.10.70.graphml +26 -0
  59. data/examples/north/g.10.71.graphml +27 -0
  60. data/examples/north/g.10.72.graphml +28 -0
  61. data/examples/north/g.10.74.graphml +29 -0
  62. data/examples/north/g.10.75.graphml +29 -0
  63. data/examples/north/g.10.78.graphml +27 -0
  64. data/examples/north/g.10.79.graphml +34 -0
  65. data/examples/north/g.10.8.graphml +29 -0
  66. data/examples/north/g.10.80.graphml +34 -0
  67. data/examples/north/g.10.82.graphml +35 -0
  68. data/examples/north/g.10.83.graphml +32 -0
  69. data/examples/north/g.10.85.graphml +34 -0
  70. data/examples/north/g.10.86.graphml +34 -0
  71. data/examples/north/g.10.88.graphml +37 -0
  72. data/examples/north/g.10.89.graphml +29 -0
  73. data/examples/north/g.10.9.graphml +26 -0
  74. data/examples/north/g.10.90.graphml +32 -0
  75. data/examples/north/g.10.91.graphml +31 -0
  76. data/examples/north/g.10.92.graphml +26 -0
  77. data/examples/north/g.10.93.graphml +32 -0
  78. data/examples/north/g.10.94.graphml +34 -0
  79. data/examples/north/g.12.8.graphml +40 -0
  80. data/examples/north/g.14.9.graphml +36 -0
  81. data/examples/north2.rb +21 -0
  82. data/examples/rdep-rgl.rb +395 -0
  83. data/install.rb +49 -0
  84. data/lib/rgl/adjacency.rb +151 -0
  85. data/lib/rgl/base.rb +299 -0
  86. data/lib/rgl/connected_components.rb +125 -0
  87. data/lib/rgl/dot.rb +63 -0
  88. data/lib/rgl/graphxml.rb +52 -0
  89. data/lib/rgl/implicit.rb +151 -0
  90. data/lib/rgl/mutable.rb +54 -0
  91. data/lib/rgl/rdot.rb +264 -0
  92. data/lib/rgl/topsort.rb +61 -0
  93. data/lib/rgl/transitiv_closure.rb +34 -0
  94. data/lib/rgl/traversal.rb +296 -0
  95. data/tests/TestComponents.rb +67 -0
  96. data/tests/TestDirectedGraph.rb +100 -0
  97. data/tests/TestEdge.rb +33 -0
  98. data/tests/TestGraphXML.rb +57 -0
  99. data/tests/TestImplicit.rb +52 -0
  100. data/tests/TestTransitiveClosure.rb +29 -0
  101. data/tests/TestTraversal.rb +222 -0
  102. data/tests/TestUnDirectedGraph.rb +98 -0
  103. metadata +163 -0
@@ -0,0 +1,264 @@
1
+ #
2
+ # $Id: rdot.rb,v 1.2 2002/11/13 21:53:26 monora Exp $
3
+ #
4
+ # This is a modified version of dot.rb from Dave Thomas rdoc project. I renamed
5
+ # it to rdot.rb to avoid collision with an installed rdoc/dot.
6
+ #
7
+ # It also supports undirected edges.
8
+
9
+ module DOT
10
+
11
+ # these glogal vars are used to make nice graph source
12
+ $tab = ' '
13
+ $tab2 = $tab * 2
14
+
15
+ # if we don't like 4 spaces, we can change it any time
16
+ def change_tab( t )
17
+ $tab = t
18
+ $tab2 = t * 2
19
+ end
20
+
21
+ # options for node declaration
22
+ NODE_OPTS = [
23
+ 'bgcolor',
24
+ 'color',
25
+ 'fontcolor',
26
+ 'fontname',
27
+ 'fontsize',
28
+ 'heigth',
29
+ 'width',
30
+ 'label',
31
+ 'layer',
32
+ 'rank',
33
+ 'shape',
34
+ 'shapefile',
35
+ 'style',
36
+ ]
37
+
38
+ # options for edge declaration
39
+ EDGE_OPTS = [
40
+ 'color',
41
+ 'decorate',
42
+ 'dir',
43
+ 'fontcolor',
44
+ 'fontname',
45
+ 'fontsize',
46
+ 'id',
47
+ 'label',
48
+ 'layer',
49
+ 'minlen',
50
+ 'style',
51
+ 'weight'
52
+ ]
53
+
54
+ # options for graph declaration
55
+ GRAPH_OPTS = [
56
+ 'bgcolor',
57
+ 'center',
58
+ 'clusterrank',
59
+ 'color',
60
+ 'concentrate',
61
+ 'fontcolor',
62
+ 'fontname',
63
+ 'fontsize',
64
+ 'label',
65
+ 'layerseq',
66
+ 'margin',
67
+ 'mclimit',
68
+ 'nodesep',
69
+ 'nslimit',
70
+ 'ordering',
71
+ 'orientation',
72
+ 'page',
73
+ 'rank',
74
+ 'rankdir',
75
+ 'ranksep',
76
+ 'ratio',
77
+ 'size'
78
+ ]
79
+
80
+ # a root class for any element in dot notation
81
+ class DOTSimpleElement
82
+ attr_accessor :name
83
+
84
+ def initialize( params = {} )
85
+ @label = params['name'] ? params['name'] : ''
86
+ end
87
+
88
+ def to_s
89
+ @name
90
+ end
91
+ end
92
+
93
+ # an element that have options ( node, edge or graph )
94
+ class DOTElement < DOTSimpleElement
95
+ #attr_reader :parent
96
+ attr_accessor :name, :options
97
+
98
+ def initialize( params = {}, option_list = [] )
99
+ super( params )
100
+ @name = params['name'] ? params['name'] : nil
101
+ @parent = params['parent'] ? params['parent'] : nil
102
+ @options = {}
103
+ option_list.each{ |i|
104
+ @options[i] = params[i] if params[i]
105
+ }
106
+ @options['label'] ||= @name if @name != 'node'
107
+ end
108
+
109
+ def each_option
110
+ @options.each{ |i| yield i }
111
+ end
112
+
113
+ def each_option_pair
114
+ @options.each_pair{ |key, val| yield key, val }
115
+ end
116
+
117
+ #def parent=( thing )
118
+ # @parent.delete( self ) if defined?( @parent ) and @parent
119
+ # @parent = thing
120
+ #end
121
+ end
122
+
123
+
124
+ # this is used when we build nodes that have shape=record
125
+ # ports don't have options :)
126
+ class DOTPort < DOTSimpleElement
127
+ attr_accessor :label
128
+
129
+ def initialize( params = {} )
130
+ super( params )
131
+ @name = params['label'] ? params['label'] : ''
132
+ end
133
+ def to_s
134
+ ( @name && @name != "" ? "<#{@name}>" : "" ) + "#{@label}"
135
+ end
136
+ end
137
+
138
+ # node element
139
+ class DOTNode < DOTElement
140
+ @ports
141
+
142
+ def initialize( params = {}, option_list = NODE_OPTS )
143
+ super( params, option_list )
144
+ @ports = params['ports'] ? params['ports'] : []
145
+ end
146
+
147
+ def each_port
148
+ @ports.each{ |i| yield i }
149
+ end
150
+
151
+ def << ( thing )
152
+ @ports << thing
153
+ end
154
+
155
+ def push ( thing )
156
+ @ports.push( thing )
157
+ end
158
+
159
+ def pop
160
+ @ports.pop
161
+ end
162
+
163
+ def to_s( t = '' )
164
+
165
+ label = @options['shape'] != 'record' && @ports.length == 0 ?
166
+ @options['label'] ?
167
+ t + $tab + "label = \"#{@options['label']}\"\n" :
168
+ '' :
169
+ t + $tab + 'label = "' + " \\\n" +
170
+ t + $tab2 + "#{@options['label']}| \\\n" +
171
+ @ports.collect{ |i|
172
+ t + $tab2 + i.to_s
173
+ }.join( "| \\\n" ) + " \\\n" +
174
+ t + $tab + '"' + "\n"
175
+
176
+ t + "#{@name} [\n" +
177
+ @options.to_a.collect{ |i|
178
+ i[1] && i[0] != 'label' ?
179
+ t + $tab + "#{i[0]} = #{i[1]}" : nil
180
+ }.compact.join( ",\n" ) + ( label != '' ? ",\n" : "\n" ) +
181
+ label +
182
+ t + "]\n"
183
+ end
184
+ end
185
+
186
+ # subgraph element is the same to graph, but has another header in dot
187
+ # notation
188
+ class DOTSubgraph < DOTElement
189
+ @nodes
190
+ @dot_string
191
+
192
+ def initialize( params = {}, option_list = GRAPH_OPTS )
193
+ super( params, option_list )
194
+ @nodes = params['nodes'] ? params['nodes'] : []
195
+ @dot_string = 'graph'
196
+ end
197
+
198
+ def each_node
199
+ @nodes.each{ |i| yield i }
200
+ end
201
+
202
+ def << ( thing )
203
+ @nodes << thing
204
+ end
205
+
206
+ def push( thing )
207
+ @nodes.push( thing )
208
+ end
209
+
210
+ def pop
211
+ @nodes.pop
212
+ end
213
+
214
+ def to_s( t = '' )
215
+ hdr = t + "#{@dot_string} #{@name} {\n"
216
+
217
+ options = @options.to_a.collect{ |name, val|
218
+ val && name != 'label' ?
219
+ t + $tab + "#{name} = #{val}" :
220
+ name ? t + $tab + "#{name} = \"#{val}\"" : nil
221
+ }.compact.join( "\n" ) + "\n"
222
+
223
+ nodes = @nodes.collect{ |i|
224
+ i.to_s( t + $tab )
225
+ }.join( "\n" ) + "\n"
226
+ hdr + options + nodes + t + "}\n"
227
+ end
228
+ end
229
+
230
+ # this is graph
231
+ class DOTDigraph < DOTSubgraph
232
+ def initialize( params = {}, option_list = GRAPH_OPTS )
233
+ super( params, option_list )
234
+ @dot_string = 'digraph'
235
+ end
236
+ end
237
+
238
+ # this is edge
239
+ class DOTEdge < DOTElement
240
+ attr_accessor :from, :to
241
+ def initialize( params = {}, option_list = EDGE_OPTS )
242
+ super( params, option_list )
243
+ @from = params['from'] ? params['from'] : nil
244
+ @to = params['to'] ? params['to'] : nil
245
+ end
246
+
247
+ def edge_link; '--'; end
248
+ def to_s( t = '' )
249
+ t + "#{@from} #{edge_link} #{to} [\n" +
250
+ @options.to_a.collect{ |i|
251
+ i[1] && i[0] != 'label' ?
252
+ t + $tab + "#{i[0]} = #{i[1]}" :
253
+ i[1] ? t + $tab + "#{i[0]} = \"#{i[1]}\"" : nil
254
+ }.compact.join( "\n" ) + "\n" + t + "]\n"
255
+ end
256
+ end
257
+
258
+ class DOTDirectedEdge < DOTEdge
259
+ def edge_link; '->'; end
260
+ end
261
+ end
262
+
263
+
264
+
@@ -0,0 +1,61 @@
1
+ require 'rgl/traversal'
2
+
3
+ module RGL
4
+ # Topological Sort Iterator
5
+ #
6
+ # The topological sort algorithm creates a linear ordering
7
+ # of the vertices such that if edge (u,v) appears in the graph,
8
+ # then u comes before v in the ordering. The graph must
9
+ # be a directed acyclic graph (DAG).
10
+ #
11
+ # The iterator can also be applied to undirected graph or a DG graph which
12
+ # contains a cycle. In this case the Iterator does not reach all vertices. The
13
+ # implementation of acyclic? uses this fact.
14
+ class TopsortIterator
15
+ include GraphIterator
16
+ def initialize(g)
17
+ super g
18
+ set_to_begin
19
+ end
20
+
21
+ def set_to_begin # :nodoc:
22
+ @waiting = Array.new
23
+ @inDegrees = Hash.new(0)
24
+
25
+ graph.each_vertex do |u|
26
+ @inDegrees[u] = 0 unless @inDegrees.has_key? u
27
+ graph.each_adjacent(u) do |v|
28
+ @inDegrees[v] += 1
29
+ end
30
+ end
31
+ @inDegrees.each_pair do |v, indegree |
32
+ @waiting.push(v) if indegree.zero?
33
+ end
34
+ end
35
+
36
+ def basic_forward # :nodoc:
37
+ u = @waiting.pop
38
+ graph.each_adjacent(u) do |v|
39
+ @inDegrees[v] -= 1
40
+ @waiting.push(v) if @inDegrees[v].zero?
41
+ end
42
+ u
43
+ end
44
+
45
+ def at_beginning?; true; end # :nodoc: FIXME
46
+ def at_end?; @waiting.empty?; end # :nodoc:
47
+ end
48
+
49
+ module Graph
50
+ # Returns a TopsortIterator.
51
+ def topsort_iterator
52
+ TopsortIterator.new(self)
53
+ end
54
+
55
+ # Returns true if the graph contains no cycle. This is only meaningful for
56
+ # directed graphs. Returns false for undirected graph.
57
+ def acyclic?
58
+ topsort_iterator.length == num_vertices
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,34 @@
1
+ # == transitive_closure
2
+ #
3
+ # The transitive closure of a graph G = (V,E) is a graph G* = (V,E*) such that E*
4
+ # contains an edge (u,v) if and only if G contains a path (of at least one edge)
5
+ # from u to v. The transitive_closure() function transforms the input graph g into
6
+ # the transitive closure graph tc.
7
+ require 'rgl/adjacency'
8
+
9
+ module RGL
10
+ module Graph
11
+ # Floyd-Warshal algorithm which should be O(n^3) where n is the number of
12
+ # nodes. We can probably work a bit on the constant factors!
13
+ #
14
+ # In BGL there is an algorithm with time complexity (worst-case) O(|V||E|)
15
+ # (see BOOST_DOC/transitive_closure.html) based on the detection of strong components.
16
+ def transitive_closure_floyd_warshal
17
+ raise NotDirectedError, "transitive_closure makes only sense for directed graphs." unless directed?
18
+ tc = to_adjacency # direct links
19
+
20
+ # indirect links
21
+ each_vertex do |vi|
22
+ each_vertex do |vj|
23
+ each_vertex do |vk|
24
+ unless tc.has_edge?(vi,vj)
25
+ tc.add_edge(vi, vj) if has_edge?(vi,vk) and has_edge?(vk,vj)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ tc
31
+ end
32
+ alias_method :transitive_closure, :transitive_closure_floyd_warshal
33
+ end
34
+ end
@@ -0,0 +1,296 @@
1
+ # This files defines the basic graph traversal algorithm DFS and BFS
2
+ # search. They are implemented as an RGL::GraphIterator which is a Stream of
3
+ # vertices of a given graph. The streams are not reversable.
4
+ #
5
+ # Beside being an iterator in the sense of the Stream mixin RGL::BFSIterator and
6
+ # RGL::DFSIterator follow the BGL Visitor Concepts (see
7
+ # BOOST_DOC/visitor_concepts.html) in a slightly modified fashion (especially
8
+ # for the DFSIterator).
9
+
10
+ require 'rgl/base'
11
+ require 'rubygems' rescue LoadError # If using stream gem
12
+ require 'stream'
13
+
14
+ module RGL
15
+ module GraphWrapper # :nodoc:
16
+ attr_accessor :graph
17
+
18
+ # Creates a new GraphWrapper on _graph_.
19
+ def initialize(graph); @graph = graph; end
20
+ end
21
+
22
+ # A GraphIterator is the abstract superclass of all Iterators on graphs. Each
23
+ # such iterator should implement the protocol defined in module Stream.
24
+ module GraphIterator
25
+ include Stream
26
+ include GraphWrapper
27
+ end
28
+
29
+ # Module GraphVisitor defines the BGL BFS Visitor Concept (see
30
+ # BOOST_DOC/BFSVisitor.html).
31
+ #
32
+ # Visitors provide a mechanism for extending an algorithm; for customizing
33
+ # what is done at each step of the algorithm. They allow the user to insert
34
+ # their own operations at various steps within a graph algorithm.
35
+ #
36
+ # Graph algorithms typically have multiple event points where one may want to
37
+ # insert a call-back. Therefore visitors have several methods that correspond
38
+ # to the various event points. Each algorithm has a different set of event
39
+ # points. The following are are common to both DFS and BFS search.
40
+ #
41
+ # * examine_vertex
42
+ # * finish_vertex
43
+ # * examine_edge
44
+ # * tree_edge
45
+ # * back_edge
46
+ # * forward_edge
47
+ #
48
+ # These methods are all called handle_* and can be set to appropriate blocks
49
+ # using the methods set_*_event_handler, which are defined for each event
50
+ # mentioned above.
51
+ #
52
+ # As an alternative you can also overide the handle_* methods
53
+ # in a subclass to configure the algorithm (as an example see
54
+ # TarjanSccVisitor).
55
+ #
56
+ # During a graph traversal vertices are *colored* using the colors :GRAY
57
+ # (when waiting) and :BLACK when finished. All other vertices are :WHITE.
58
+ # The color_map is also maintained in the visitor.
59
+ module GraphVisitor
60
+ include GraphWrapper
61
+ attr_reader :color_map
62
+
63
+ # Create a new GraphVisitor on _graph_.
64
+ def initialize(graph)
65
+ super graph
66
+ reset
67
+ end
68
+
69
+ # Mark each vertex unvisited (i.e. :WHITE)
70
+ def reset
71
+ @color_map = Hash.new(:WHITE)
72
+ end
73
+
74
+ # Returns true if vertex _v_ is colored :BLACK (i.e. finished)
75
+ def finished_vertex? v
76
+ @color_map[v] == :BLACK
77
+ end
78
+
79
+ # Attach a map to the visitor which records the distance of a visited
80
+ # vertex to the start vertex.
81
+ #
82
+ # This is similar to BGLs distance_recorder (see
83
+ # BOOST_DOC/distance_recorder.html).
84
+ #
85
+ # After the distance_map is attached, the visitor has a new method
86
+ # distance_to_root, which answers the distance to the start vertex.
87
+ def attach_distance_map(map=Hash.new(0))
88
+ @dist_map = map
89
+ class << self
90
+ def handle_tree_edge(u,v)
91
+ super
92
+ @dist_map[v] = @dist_map[u] + 1
93
+ end
94
+
95
+ # Answer the distance to the start vertex.
96
+ def distance_to_root(v)
97
+ @dist_map[v]
98
+ end
99
+ end
100
+ end
101
+
102
+ # Shell we follow the edge (u,v) i.e. v has color :WHITE
103
+ def follow_edge?(u,v) # :nodoc:
104
+ @color_map[v] == :WHITE
105
+ end
106
+
107
+ # == Visitor Event Points
108
+ def self.def_event_handler(m)
109
+ params = m =~ /edge/ ? "u,v" : "u"
110
+ self.class_eval %{
111
+ def handle_#{m}(#{params})
112
+ @#{m}_event_handler.call(#{params}) if defined? @#{m}_event_handler
113
+ end
114
+ def set_#{m}_event_handler(&b)
115
+ @#{m}_event_handler = b
116
+ end
117
+ }
118
+ end
119
+
120
+ %w[examine_vertex finish_vertex examine_edge tree_edge back_edge forward_edge].each do |m|
121
+ def_event_handler(m)
122
+ end
123
+ end # GraphVisitor
124
+
125
+ # A BFSIterator can be used to traverse a graph from a given start vertex in
126
+ # breath first search order. Since the Iterator also mixins the GraphVisitor
127
+ # it provides all event points defined there.
128
+ #
129
+ # The vertices which are not yet visited are hold in the queue
130
+ # @waiting. During the traversal vertices are *colored* using the colors :GRAY
131
+ # (when waiting) and :BLACK when finished. All other vertices are :WHITE.
132
+ #
133
+ # For more doc see the BGL BFS Visitor Concept
134
+ # BOOST_DOC/BFSVisitor.html.
135
+ #
136
+ # See the implementation of bfs_search_tree_from for an example usage.
137
+ class BFSIterator
138
+ include GraphIterator
139
+ include GraphVisitor
140
+ attr_accessor :start_vertex
141
+
142
+ # Create a new BFSIterator on _graph_ starting at vertex _start_.
143
+ def initialize(graph, start=graph.detect{|x| true})
144
+ super graph
145
+ @start_vertex = start
146
+ set_to_begin
147
+ end
148
+
149
+ # Returns true if the @color_map has only one entry (for the start vertex).
150
+ def at_beginning?; @color_map.size == 1; end # :nodoc:
151
+
152
+ # Returns true if @waiting is empty.
153
+ def at_end?; @waiting.empty?; end
154
+
155
+ # Reset the iterator to the initial state (i.e. at_beginning? == true).
156
+ def set_to_begin
157
+ color_map[@start_vertex] = :GRAY
158
+ @waiting = [@start_vertex] # a queue
159
+ handle_tree_edge(nil,@start_vertex) # discovers start vertex
160
+ end
161
+
162
+ def basic_forward # :nodoc:
163
+ u = next_vertex
164
+ handle_examine_vertex(u)
165
+ graph.each_adjacent(u) { |v|
166
+ handle_examine_edge(u,v)
167
+ if follow_edge? u,v # (u,v) is a tree edge
168
+ handle_tree_edge(u,v) # also discovers v
169
+ color_map[v] = :GRAY # color of v was :WHITE
170
+ @waiting.push v
171
+ else # (u,v) is a non tree edge
172
+ if color_map[v] == :GRAY
173
+ handle_back_edge(u,v) # (u,v) has gray target
174
+ else
175
+ handle_forward_edge(u,v) # (u,v) has black target
176
+ end
177
+ end
178
+ }
179
+ color_map[u] = :BLACK
180
+ handle_finish_vertex(u) # finish vertex
181
+ u
182
+ end
183
+
184
+ protected
185
+
186
+ def next_vertex () # :nodoc:
187
+ # waiting is a queue
188
+ @waiting.shift
189
+ end
190
+ end # BFSIterator
191
+
192
+ module Graph
193
+ # Returns a BFSIterator staring at vertex _v_.
194
+ def bfs_iterator (v=self.detect {|x| true})
195
+ BFSIterator.new(self,v)
196
+ end
197
+
198
+ # Returns a DirectedAdjacencyGraph which represents a BFS search tree
199
+ # starting at _v_. This methods uses the tree_edge_event of BFSIterator to
200
+ # record all tree edges of the search tree in the result.
201
+ def bfs_search_tree_from(v)
202
+ require 'rgl/adjacency'
203
+ bfs = bfs_iterator(v)
204
+ tree = DirectedAdjacencyGraph.new
205
+ bfs.set_tree_edge_event_handler {|from, to|
206
+ tree.add_edge from, to
207
+ }
208
+ bfs.set_to_end # does the search
209
+ tree
210
+ end
211
+ end
212
+
213
+ # Iterator for a depth first search starting at a given vertex. The only
214
+ # difference to BFSIterator is that @waiting is a stack instead of a queue.
215
+ #
216
+ # Note that this is different to DFSVisitor which is used in the recursive
217
+ # version for depth first search (see depth_first_search).
218
+ class DFSIterator < BFSIterator
219
+ def next_vertex
220
+ # waiting is a stack
221
+ @waiting.pop
222
+ end
223
+ end
224
+
225
+ # A DFSVisitor is needed by the depth_first_search and
226
+ # depth_first_visit methods of a graph. Besides the eventpoint of GraphVisitor
227
+ # it provides an additional eventpoint start_vertex, which is called when a
228
+ # depth_first_search starts a new subtree of the depth first forest defined by
229
+ # the search.
230
+ #
231
+ # Note that the discover_vertex event defined in the BGL DFSVisitor
232
+ # (BOOST_DOC/DFSVisitor.html) is not provided. Use examine_vertex instead
233
+ # which is also defined in the common mixin GraphVisitor of DFSVisitor,
234
+ # DFSIterator and BFSIterator.
235
+ class DFSVisitor
236
+ include GraphVisitor
237
+
238
+ GraphVisitor.def_event_handler("start_vertex")
239
+ end
240
+
241
+ module Graph
242
+ # Returns a DFSIterator staring at vertex _v_.
243
+ def dfs_iterator (v=self.detect {|x| true})
244
+ DFSIterator.new(self,v)
245
+ end
246
+
247
+ # Do a recursive DFS search on the whole graph. If a block is passed, it is
248
+ # called on each _finish_vertex_ event. See strongly_connected_components for
249
+ # an example usage.
250
+ def depth_first_search (vis = DFSVisitor.new(self),&b)
251
+ each_vertex do |u|
252
+ unless vis.finished_vertex? u
253
+ vis.handle_start_vertex u
254
+ depth_first_visit(u,vis,&b)
255
+ end
256
+ end
257
+ end
258
+
259
+ # Start a depth first search at vertex _u_. The block _b_ is called on each
260
+ # finish_vertex event.
261
+ def depth_first_visit (u, vis = DFSVisitor.new(self), &b)
262
+ vis.color_map[u] = :GRAY
263
+ vis.handle_examine_vertex(u)
264
+ each_adjacent(u) { |v|
265
+ vis.handle_examine_edge(u,v)
266
+ if vis.follow_edge? u,v # (u,v) is a tree edge
267
+ vis.handle_tree_edge(u,v) # also discovers v
268
+ vis.color_map[v] = :GRAY # color of v was :WHITE
269
+ depth_first_visit(v, vis, &b)
270
+ else # (u,v) is a non tree edge
271
+ if vis.color_map[v] == :GRAY
272
+ vis.handle_back_edge(u,v) # (u,v) has gray target
273
+ else
274
+ vis.handle_forward_edge(u,v) # (u,v) is a cross or forward edge
275
+ end
276
+ end
277
+ }
278
+ vis.color_map[u] = :BLACK
279
+ vis.handle_finish_vertex(u) # finish vertex
280
+ b.call(u)
281
+ end
282
+ end
283
+
284
+ =begin
285
+ def acyclic?
286
+ has_cycle = false
287
+ dfs = DFSIterator.new(self)
288
+ dfs.set_back_edge_event {has_cycle = true}
289
+ dfs_each(dfs) do |x|
290
+ puts x,has_cycle,dfs.inspect
291
+ return false if has_cycle
292
+ end
293
+ true
294
+ end
295
+ =end
296
+ end