rgl 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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