jumoku 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. data/Gemfile +0 -1
  2. data/lib/jumoku.rb +2 -3
  3. data/lib/jumoku/builders/extended.rb +15 -23
  4. data/lib/jumoku/builders/raw_directed_tree.rb +15 -0
  5. data/lib/jumoku/builders/raw_undirected_tree.rb +15 -0
  6. data/lib/jumoku/builders/shared.rb +2 -5
  7. data/lib/jumoku/support/ruby_compatibility.rb +19 -0
  8. data/lib/jumoku/version.rb +1 -1
  9. data/spec/arborescence_spec.rb +14 -0
  10. data/spec/behaviors/core_tree.rb +281 -0
  11. data/spec/behaviors/extended.rb +530 -0
  12. data/spec/raw_directed_tree_spec.rb +14 -0
  13. data/spec/raw_undirected_tree_spec.rb +9 -310
  14. data/spec/spec_helper.rb +2 -0
  15. data/spec/tree_spec.rb +8 -535
  16. metadata +21 -86
  17. data/lib/jumoku/tree_api.rb +0 -27
  18. data/vendor/git/plexus/CREDITS.md +0 -31
  19. data/vendor/git/plexus/Gemfile +0 -3
  20. data/vendor/git/plexus/Gemfile.lock +0 -28
  21. data/vendor/git/plexus/LICENSE +0 -37
  22. data/vendor/git/plexus/README.md +0 -208
  23. data/vendor/git/plexus/Rakefile +0 -25
  24. data/vendor/git/plexus/TODO.md +0 -20
  25. data/vendor/git/plexus/VERSION +0 -1
  26. data/vendor/git/plexus/examples/graph_self.rb +0 -56
  27. data/vendor/git/plexus/examples/module_graph.jpg +0 -0
  28. data/vendor/git/plexus/examples/module_graph.rb +0 -14
  29. data/vendor/git/plexus/examples/self_graph.jpg +0 -0
  30. data/vendor/git/plexus/examples/visualize.jpg +0 -0
  31. data/vendor/git/plexus/examples/visualize.rb +0 -10
  32. data/vendor/git/plexus/lib/plexus.rb +0 -90
  33. data/vendor/git/plexus/lib/plexus/adjacency_graph.rb +0 -224
  34. data/vendor/git/plexus/lib/plexus/arc.rb +0 -59
  35. data/vendor/git/plexus/lib/plexus/arc_number.rb +0 -52
  36. data/vendor/git/plexus/lib/plexus/biconnected.rb +0 -84
  37. data/vendor/git/plexus/lib/plexus/chinese_postman.rb +0 -91
  38. data/vendor/git/plexus/lib/plexus/classes/graph_classes.rb +0 -28
  39. data/vendor/git/plexus/lib/plexus/common.rb +0 -63
  40. data/vendor/git/plexus/lib/plexus/comparability.rb +0 -63
  41. data/vendor/git/plexus/lib/plexus/directed_graph.rb +0 -78
  42. data/vendor/git/plexus/lib/plexus/directed_graph/algorithms.rb +0 -95
  43. data/vendor/git/plexus/lib/plexus/directed_graph/distance.rb +0 -167
  44. data/vendor/git/plexus/lib/plexus/dot.rb +0 -94
  45. data/vendor/git/plexus/lib/plexus/edge.rb +0 -36
  46. data/vendor/git/plexus/lib/plexus/ext.rb +0 -79
  47. data/vendor/git/plexus/lib/plexus/graph.rb +0 -626
  48. data/vendor/git/plexus/lib/plexus/graph_api.rb +0 -35
  49. data/vendor/git/plexus/lib/plexus/labels.rb +0 -113
  50. data/vendor/git/plexus/lib/plexus/maximum_flow.rb +0 -77
  51. data/vendor/git/plexus/lib/plexus/ruby_compatibility.rb +0 -17
  52. data/vendor/git/plexus/lib/plexus/search.rb +0 -510
  53. data/vendor/git/plexus/lib/plexus/strong_components.rb +0 -93
  54. data/vendor/git/plexus/lib/plexus/support/support.rb +0 -9
  55. data/vendor/git/plexus/lib/plexus/undirected_graph.rb +0 -56
  56. data/vendor/git/plexus/lib/plexus/undirected_graph/algorithms.rb +0 -90
  57. data/vendor/git/plexus/lib/plexus/version.rb +0 -6
  58. data/vendor/git/plexus/plexus.gemspec +0 -24
  59. data/vendor/git/plexus/spec/biconnected_spec.rb +0 -27
  60. data/vendor/git/plexus/spec/chinese_postman_spec.rb +0 -27
  61. data/vendor/git/plexus/spec/community_spec.rb +0 -44
  62. data/vendor/git/plexus/spec/complement_spec.rb +0 -27
  63. data/vendor/git/plexus/spec/digraph_distance_spec.rb +0 -121
  64. data/vendor/git/plexus/spec/digraph_spec.rb +0 -339
  65. data/vendor/git/plexus/spec/dot_spec.rb +0 -48
  66. data/vendor/git/plexus/spec/edge_spec.rb +0 -158
  67. data/vendor/git/plexus/spec/inspection_spec.rb +0 -38
  68. data/vendor/git/plexus/spec/multi_edge_spec.rb +0 -32
  69. data/vendor/git/plexus/spec/neighborhood_spec.rb +0 -36
  70. data/vendor/git/plexus/spec/properties_spec.rb +0 -146
  71. data/vendor/git/plexus/spec/search_spec.rb +0 -227
  72. data/vendor/git/plexus/spec/spec.opts +0 -4
  73. data/vendor/git/plexus/spec/spec_helper.rb +0 -59
  74. data/vendor/git/plexus/spec/strong_components_spec.rb +0 -61
  75. data/vendor/git/plexus/spec/triangulated_spec.rb +0 -125
  76. data/vendor/git/plexus/spec/undirected_graph_spec.rb +0 -220
  77. data/vendor/git/plexus/vendor/priority-queue/CHANGELOG +0 -33
  78. data/vendor/git/plexus/vendor/priority-queue/Makefile +0 -140
  79. data/vendor/git/plexus/vendor/priority-queue/README +0 -133
  80. data/vendor/git/plexus/vendor/priority-queue/benchmark/dijkstra.rb +0 -171
  81. data/vendor/git/plexus/vendor/priority-queue/compare_comments.rb +0 -49
  82. data/vendor/git/plexus/vendor/priority-queue/doc/c-vs-rb.png +0 -0
  83. data/vendor/git/plexus/vendor/priority-queue/doc/compare_big.gp +0 -14
  84. data/vendor/git/plexus/vendor/priority-queue/doc/compare_big.png +0 -0
  85. data/vendor/git/plexus/vendor/priority-queue/doc/compare_small.gp +0 -15
  86. data/vendor/git/plexus/vendor/priority-queue/doc/compare_small.png +0 -0
  87. data/vendor/git/plexus/vendor/priority-queue/doc/results.csv +0 -37
  88. data/vendor/git/plexus/vendor/priority-queue/ext/priority_queue/CPriorityQueue/extconf.rb +0 -2
  89. data/vendor/git/plexus/vendor/priority-queue/ext/priority_queue/CPriorityQueue/priority_queue.c +0 -947
  90. data/vendor/git/plexus/vendor/priority-queue/lib/priority_queue.rb +0 -14
  91. data/vendor/git/plexus/vendor/priority-queue/lib/priority_queue/c_priority_queue.rb +0 -1
  92. data/vendor/git/plexus/vendor/priority-queue/lib/priority_queue/poor_priority_queue.rb +0 -46
  93. data/vendor/git/plexus/vendor/priority-queue/lib/priority_queue/ruby_priority_queue.rb +0 -526
  94. data/vendor/git/plexus/vendor/priority-queue/priority_queue.so +0 -0
  95. data/vendor/git/plexus/vendor/priority-queue/setup.rb +0 -1551
  96. data/vendor/git/plexus/vendor/priority-queue/test/priority_queue_test.rb +0 -371
  97. data/vendor/git/plexus/vendor/rdot.rb +0 -360
@@ -1,79 +0,0 @@
1
- class Object
2
- # Get the singleton class of the object.
3
- # Depending on the object which requested its singleton class,
4
- # a `module_eval` or a `class_eval` will be performed.
5
- # Object of special constant type (`Fixnum`, `NilClass`, `TrueClass`,
6
- # `FalseClass` and `Symbol`) return `nil` as they do not have a
7
- # singleton class.
8
- #
9
- # @return the singleton class
10
- def singleton_class
11
- if self.respond_to? :module_eval
12
- self.module_eval("class << self; self; end")
13
- elsif self.respond_to? :instance_eval
14
- begin
15
- self.instance_eval("class << self; self; end")
16
- rescue TypeError
17
- nil
18
- end
19
- end
20
- end
21
-
22
- # Check wether the object is of the specified kind.
23
- # If the receiver has a singleton class, will also perform
24
- # the check on its singleton class' ancestors, so as to catch
25
- # any included modules for object instances.
26
- #
27
- # Example:
28
- #
29
- # class A; include Digraph; end
30
- # a.singleton_class.ancestors
31
- # # => [Plexus::GraphAPI, Plexus::DirectedGraph::Algorithms, ...
32
- # Plexus::Labels, Enumerable, Object, Plexus, Kernel, BasicObject]
33
- # a.is_a? Plexus::Graph
34
- # # => true
35
- #
36
- # @param [Class] klass
37
- # @return [Boolean]
38
- def is_a? klass
39
- sc = self.singleton_class
40
- if not sc.nil?
41
- self.singleton_class.ancestors.include?(klass) || super
42
- else
43
- super
44
- end
45
- end
46
- end
47
-
48
- class Module
49
- # Helper which purpose is, given a class including a module,
50
- # to make each methods defined within a module's submodule `ClassMethods`
51
- # available as class methods to the receiving class.
52
- #
53
- # Example:
54
- #
55
- # module A
56
- # extends_host
57
- # module ClassMethods
58
- # def selfy; puts "class method for #{self}"; end
59
- # end
60
- # end
61
- #
62
- # class B; include A; end
63
- #
64
- # B.selfy
65
- # # => class method for B
66
- #
67
- # @option *params [Symbol] :with (:ClassMethods) the name of the
68
- # module to extend the receiver with
69
- def extends_host(*params)
70
- args = (params.pop if params.last.is_a? Hash) || {}
71
- @_extension_module = args[:with] || :ClassMethods
72
-
73
- def included(base)
74
- unless @_extension_module.nil?
75
- base.extend(self.const_get(@_extension_module))
76
- end
77
- end
78
- end
79
- end
@@ -1,626 +0,0 @@
1
- module Plexus
2
- # Using the methods required by the {GraphAPI}, it implements all the
3
- # *basic* functions of a {Graph} using *only* functions
4
- # requested in {GraphAPI}. The process is under the control of the pattern
5
- # {AdjacencyGraphBuilder}, unless a specific implementation is specified
6
- # during initialization.
7
- #
8
- # An actual, complete implementation still needs to be done using this cheap result,
9
- # hence {Digraph}, {UndirectedGraph} and their roomates.
10
- module GraphBuilder
11
- include Enumerable
12
- include Labels
13
- include Dot
14
-
15
- #def self.[](*a)
16
- #puts self
17
- #self.new.from_array(*a)
18
- #end
19
- # after the class->module transition, has been moved at implementation level,
20
- # using a helper (extends_host)
21
- extends_host
22
- module ClassMethods
23
- def [](*a)
24
- self.new.from_array(*a)
25
- end
26
- end
27
-
28
- # Creates a generic graph.
29
- #
30
- # @param [Hash(Plexus::Graph, Array)] *params initialization parameters.
31
- # See {AdjacencyGraphBuilder#implementation_initialize} for more details.
32
- # @return [Graph]
33
- def initialize(*params)
34
- raise ArgumentError if params.any? do |p|
35
- # FIXME: checking wether it's a GraphBuilder (module) is not sufficient
36
- # and the is_a? redefinition trick (instance_evaling) should be
37
- # completed by a clever way to check the actual class of p.
38
- # Maybe using ObjectSpace to get the available Graph classes?
39
- !(p.is_a? Plexus::GraphBuilder or p.is_a? Array or p.is_a? Hash)
40
- end
41
-
42
- args = params.last || {}
43
-
44
- class << self
45
- self
46
- end.module_eval do
47
- # These inclusions trigger some validations checks by the way.
48
- include(args[:implementation] ? args[:implementation] : Plexus::AdjacencyGraphBuilder)
49
- include(args[:algorithmic_category] ? args[:algorithmic_category] : Plexus::DigraphBuilder )
50
- include Plexus::GraphAPI
51
- end
52
-
53
- implementation_initialize(*params)
54
- end
55
-
56
- # Shortcut for creating a Graph.
57
- #
58
- # Using an arry of implicit {Arc}, specifying the vertices:
59
- #
60
- # Plexus::Graph[1,2, 2,3, 2,4, 4,5].edges.to_a.to_s
61
- # # => "(1-2)(2-3)(2-4)(4-5)"
62
- #
63
- # Using a Hash for specifying labels along the way:
64
- #
65
- # Plexus::Graph[ [:a,:b] => 3, [:b,:c] => 4 ] (note: do not use for Multi or Pseudo graphs)
66
- #
67
- # @param [Array, Hash] *a
68
- # @return [Graph]
69
- def from_array(*a)
70
- if a.size == 1 and a[0].is_a? Hash
71
- # Convert to edge class
72
- a[0].each do |k,v|
73
- #FIXME, edge class shouldn't be assume here!!!
74
- if edge_class.include? Plexus::ArcNumber
75
- add_edge!(edge_class[k[0],k[1],nil,v])
76
- else
77
- add_edge!(edge_class[k[0],k[1],v])
78
- end
79
- end
80
- #FIXME, edge class shouldn't be assume here!!!
81
- elsif a[0].is_a? Plexus::Arc
82
- a.each{ |e| add_edge!(e); self[e] = e.label}
83
- elsif a.size % 2 == 0
84
- 0.step(a.size-1, 2) {|i| add_edge!(a[i], a[i+1])}
85
- else
86
- raise ArgumentError
87
- end
88
- self
89
- end
90
-
91
- # Non destructive version of {AdjacencyGraphBuilder#add_vertex!} (works on a copy of the graph).
92
- #
93
- # @param [vertex] v
94
- # @param [Label] l
95
- # @return [Graph] a new graph with the supplementary vertex
96
- def add_vertex(v, l = nil)
97
- x = self.class.new(self)
98
- x.add_vertex!(v, l)
99
- end
100
-
101
- # Non destructive version {AdjacencyGraphBuilder#add_edge!} (works on a copy of the graph).
102
- #
103
- # @param [vertex] u
104
- # @param [vertex] v
105
- # @param [Label] l
106
- # @return [Graph] a new graph with the supplementary edge
107
- def add_edge(u, v = nil, l = nil)
108
- x = self.class.new(self)
109
- x.add_edge!(u, v, l)
110
- end
111
- alias add_arc add_edge
112
-
113
- # Non destructive version of {AdjacencyGraphBuilder#remove_vertex!} (works on a copy of the graph).
114
- #
115
- # @param [vertex] v
116
- # @return [Graph] a new graph without the specified vertex
117
- def remove_vertex(v)
118
- x = self.class.new(self)
119
- x.remove_vertex!(v)
120
- end
121
-
122
- # Non destructive version {AdjacencyGraphBuilder#remove_edge!} (works on a copy of the graph).
123
- #
124
- # @param [vertex] u
125
- # @param [vertex] v
126
- # @return [Graph] a new graph without the specified edge
127
- def remove_edge(u, v = nil)
128
- x = self.class.new(self)
129
- x.remove_edge!(u, v)
130
- end
131
- alias remove_arc remove_edge
132
-
133
- # Computes the adjacent portions of the Graph.
134
- #
135
- # The options specify the parameters about the adjacency search.
136
- # Note: it is probably more efficently done in the implementation class.
137
- #
138
- # @param [vertex, Edge] x can either be a vertex an edge
139
- # @option options [Symbol] :type (:vertices) can be either `:edges` or `:vertices`
140
- # @option options [Symbol] :direction (:all) can be `:in`, `:out` or `:all`
141
- # @return [Array] an array of the adjacent portions
142
- # @fixme
143
- def adjacent(x, options = {})
144
- d = directed? ? (options[:direction] || :out) : :all
145
-
146
- # Discharge the easy ones first.
147
- return [x.source] if x.is_a? Arc and options[:type] == :vertices and d == :in
148
- return [x.target] if x.is_a? Arc and options[:type] == :vertices and d == :out
149
- return [x.source, x.target] if x.is_a? Arc and options[:type] != :edges and d == :all
150
-
151
- (options[:type] == :edges ? edges : to_a).select { |u| adjacent?(x,u,d) }
152
- end
153
- #FIXME: This is a hack around a serious problem
154
- alias graph_adjacent adjacent
155
-
156
- # Adds all specified vertices to the vertex set.
157
- #
158
- # @param [#each] *a an Enumerable vertices set
159
- # @return [Graph] `self`
160
- def add_vertices!(*a)
161
- a.each { |v| add_vertex! v }
162
- self
163
- end
164
-
165
- # Same as {GraphBuilder#add_vertices! add_vertices!} but works on copy of the receiver.
166
- #
167
- # @param [#each] *a
168
- # @return [Graph] a modified copy of `self`
169
- def add_vertices(*a)
170
- x = self.class.new(self)
171
- x.add_vertices!(*a)
172
- self
173
- end
174
-
175
- # Adds all edges mentionned in the specified Enumerable to the edge set.
176
- #
177
- # Elements of the Enumerable can be either two-element arrays or instances of
178
- # {Edge} or {Arc}.
179
- #
180
- # @param [#each] *a an Enumerable edges set
181
- # @return [Graph] `self`
182
- def add_edges!(*a)
183
- a.each { |edge| add_edge!(edge) }
184
- self
185
- end
186
- alias add_arcs! add_edges!
187
-
188
- # Same as {GraphBuilder#add_egdes! add_edges!} but works on a copy of the receiver.
189
- #
190
- # @param [#each] *a an Enumerable edges set
191
- # @return [Graph] a modified copy of `self`
192
- def add_edges(*a)
193
- x = self.class.new(self)
194
- x.add_edges!(*a)
195
- self
196
- end
197
- alias add_arcs add_edges
198
-
199
- # Removes all vertices mentionned in the specified Enumerable from the graph.
200
- #
201
- # The process relies on {GraphBuilder#remove_vertex! remove_vertex!}.
202
- #
203
- # @param [#each] *a an Enumerable vertices set
204
- # @return [Graph] `self`
205
- def remove_vertices!(*a)
206
- a.each { |v| remove_vertex! v }
207
- end
208
- alias delete_vertices! remove_vertices!
209
-
210
- # Same as {GraphBuilder#remove_vertices! remove_vertices!} but works on a copy of the receiver.
211
- #
212
- # @param [#each] *a a vertex Enumerable set
213
- # @return [Graph] a modified copy of `self`
214
- def remove_vertices(*a)
215
- x = self.class.new(self)
216
- x.remove_vertices(*a)
217
- end
218
- alias delete_vertices remove_vertices
219
-
220
- # Removes all edges mentionned in the specified Enumerable from the graph.
221
- #
222
- # The process relies on {GraphBuilder#remove_edges! remove_edges!}.
223
- #
224
- # @param [#each] *a an Enumerable edges set
225
- # @return [Graph] `self`
226
- def remove_edges!(*a)
227
- a.each { |e| remove_edge! e }
228
- end
229
- alias remove_arcs! remove_edges!
230
- alias delete_edges! remove_edges!
231
- alias delete_arcs! remove_edges!
232
-
233
- # Same as {GraphBuilder#remove_edges! remove_edges!} but works on a copy of the receiver.
234
- #
235
- # @param [#each] *a an Enumerable edges set
236
- # @return [Graph] a modified copy of `self`
237
- def remove_edges(*a)
238
- x = self.class.new(self)
239
- x.remove_edges!(*a)
240
- end
241
- alias remove_arcs remove_edges
242
- alias delete_edges remove_edges
243
- alias delete_arcs remove_edges
244
-
245
- # Executes the given block for each vertex. It allows for mixing Enumerable in.
246
- def each(&block)
247
- vertices.each(&block)
248
- end
249
-
250
- # Returns true if the specified vertex belongs to the graph.
251
- #
252
- # This is a default implementation that is of O(n) average complexity.
253
- # If a subclass uses a hash to store vertices, then this can be
254
- # made into an O(1) average complexity operation.
255
- #
256
- # @param [vertex] v
257
- # @return [Boolean]
258
- def vertex?(v)
259
- vertices.include?(v)
260
- end
261
- alias has_vertex? vertex?
262
- # TODO: (has_)vertices?
263
-
264
- # Returns true if u or (u,v) is an {Edge edge} of the graph.
265
- #
266
- # @overload edge?(a)
267
- # @param [Arc, Edge] a
268
- # @overload edge?(u, v)
269
- # @param [vertex] u
270
- # @param [vertex] v
271
- # @return [Boolean]
272
- def edge?(*args)
273
- edges.include?(edge_convert(*args))
274
- end
275
- alias arc? edge?
276
- alias has_edge? edge?
277
- alias has_arc? edge?
278
-
279
- # Tests two objects to see if they are adjacent.
280
- #
281
- # Note that in this method, one is primarily concerned with finding
282
- # all adjacent objects in a graph to a given object. The concern is primarily on seeing
283
- # if two objects touch. For two vertexes, any edge between the two will usually do, but
284
- # the direction can be specified if needed.
285
- #
286
- # @param [vertex] source
287
- # @param [vertex] target
288
- # @param [Symbol] direction (:all) constraint on the direction of adjacency; may be either `:in`, `:out` or `:all`
289
- def adjacent?(source, target, direction = :all)
290
- if source.is_a? Plexus::Arc
291
- raise NoArcError unless edge? source
292
- if target.is_a? Plexus::Arc
293
- raise NoArcError unless edge? target
294
- (direction != :out and source.source == target.target) or (direction != :in and source.target == target.source)
295
- else
296
- raise NoVertexError unless vertex? target
297
- (direction != :out and source.source == target) or (direction != :in and source.target == target)
298
- end
299
- else
300
- raise NoVertexError unless vertex? source
301
- if target.is_a? Plexus::Arc
302
- raise NoArcError unless edge? target
303
- (direction != :out and source == target.target) or (direction != :in and source == target.source)
304
- else
305
- raise NoVertexError unless vertex? target
306
- (direction != :out and edge?(target,source)) or (direction != :in and edge?(source,target))
307
- end
308
- end
309
- end
310
-
311
- # Is the graph connected?
312
- #
313
- # A graph is called connected if every pair of distinct vertices in the graph
314
- # can be connected through some path. The exact definition depends on whether
315
- # the graph is directed or not, hence this method should overriden in specific
316
- # implementations.
317
- #
318
- # This methods implements a lazy routine using the internal vertices hash.
319
- # If you ever want to check connectivity state using a bfs/dfs algorithm, use
320
- # the `:algo => :bfs` or `:dfs` option.
321
- #
322
- # @return [Boolean] `true` if the graph is connected, `false` otherwise
323
- def connected?(options = {})
324
- options = options.reverse_merge! :algo => :bfs
325
- if options[:algo] == (:bfs || :dfs)
326
- num_nodes = 0
327
- send(options[:algo]) { |n| num_nodes += 1 }
328
- return num_nodes == @vertex_dict.size
329
- else
330
- !@vertex_dict.collect { |v| degree(v) > 0 }.any? { |check| check == false }
331
- end
332
- end
333
- # TODO: do it!
334
- # TODO: for directed graphs, add weakly_connected? and strongly_connected? (aliased as strong?)
335
- # TODO: in the context of vertices/Arc, add connected_vertices? and disconnected_vertices?
336
- # TODO: maybe implement some routine which would compute cuts and connectivity? tricky though,
337
- # but would be useful (k_connected?(k))
338
-
339
- # Returns true if the graph has no vertex.
340
- #
341
- # @return [Boolean]
342
- def empty?
343
- vertices.size.zero?
344
- end
345
-
346
- # Returns true if the given object is a vertex or an {Arc arc} of the graph.
347
- #
348
- # @param [vertex, Arc] x
349
- def include?(x)
350
- x.is_a?(Plexus::Arc) ? edge?(x) : vertex?(x)
351
- end
352
- alias has? include?
353
-
354
- # Returns the neighborhood of the given vertex or {Arc arc}.
355
- #
356
- # This is equivalent to {GraphBuilder#adjacent adjacent}, but the type is based on the
357
- # type of the specified object.
358
- #
359
- # @param [vertex, Arc] x
360
- # @param [Symbol] direction (:all) can be either `:all`, `:in` or `:out`
361
- def neighborhood(x, direction = :all)
362
- adjacent(x, :direction => direction, :type => ((x.is_a? Plexus::Arc) ? :edges : :vertices ))
363
- end
364
-
365
- # Union of all neighborhoods of vertices (or edges) in the Enumerable x minus the contents of x.
366
- #
367
- # Definition taken from: Jorgen Bang-Jensen, Gregory Gutin, *Digraphs: Theory, Algorithms and Applications*, pg. 4
368
- #
369
- # @param [vertex] x
370
- # @param [Symbol] direction can be either `:all`, `:in` or `:out`
371
- def set_neighborhood(x, direction = :all)
372
- x.inject(Set.new) { |a,v| a.merge(neighborhood(v, direction))}.reject { |v2| x.include?(v2) }
373
- end
374
-
375
- # Union of all {GraphBuilder#set_neighborhood set_neighborhoods} reachable
376
- # among the specified edges.
377
- #
378
- # Definition taken from Jorgen Bang-Jensen, Gregory Gutin, *Digraphs:
379
- # Theory, Algorithms and Applications*, pg. 46
380
- #
381
- # @param [vertex] w
382
- # @param [Edges] p
383
- # @param [Symbol] direction can be `:all`, `:in`, or `:out`
384
- def closed_pth_neighborhood(w, p, direction = :all)
385
- if p <= 0
386
- w
387
- elsif p == 1
388
- (w + set_neighborhood(w, direction)).uniq
389
- else
390
- n = set_neighborhood(w, direction)
391
- (w + n + closed_pth_neighborhood(n, p-1, direction)).uniq
392
- end
393
- end
394
-
395
- # Returns the neighboorhoods reachable in a certain amount of steps from
396
- # every vertex (or edge) in the specified Enumerable.
397
- #
398
- # Definition taken from Jorgen Bang-Jensen, Gregory Gutin, _Digraphs:
399
- # Theory, Algorithms and Applications_, pg. 46
400
- #
401
- # @param [Enumerable] x
402
- # @param [Integer] p number of steps to perform
403
- # @param [Symbol] direction can be `:all`, `:in`, or `:out`
404
- def open_pth_neighborhood(x, p, direction = :all)
405
- if p <= 0
406
- x
407
- elsif p == 1
408
- set_neighborhood(x,direction)
409
- else
410
- set_neighborhood(open_pth_neighborhood(x, p-1, direction), direction) -
411
- closed_pth_neighborhood(x, p-1, direction)
412
- end
413
- end
414
-
415
- # Returns the number of out-edges (for directed graphs) or the number of
416
- # incident edges (for undirected graphs) of the specified vertex.
417
- #
418
- # @param [vertex] v
419
- # @return [Integer] number of matching edges
420
- def out_degree(v)
421
- adjacent(v, :direction => :out).size
422
- end
423
-
424
- # Returns the number of in-edges (for directed graphs) or the number of
425
- # incident edges (for undirected graphs) of the specified vertex
426
- #
427
- # @param [vertex] v
428
- # @return [Integer] number of matching edges
429
- def in_degree(v)
430
- adjacent(v, :direction => :in).size
431
- end
432
-
433
- # Returns the sum of the number in and out edges for the specified vertex.
434
- #
435
- # @param [vertex] v
436
- # @return [Integer] degree
437
- def degree(v)
438
- in_degree(v) + out_degree(v)
439
- end
440
-
441
- # Minimum in-degree of the graph.
442
- #
443
- # @return [Integer, nil] returns `nil` if the graph is empty
444
- def min_in_degree
445
- return nil if to_a.empty?
446
- to_a.map { |v| in_degree(v) }.min
447
- end
448
-
449
- # Minimum out-degree of the graph.
450
- #
451
- # @return [Integer, nil] returns `nil` if the graph is empty
452
- def min_out_degree
453
- return nil if to_a.empty?
454
- to_a.map {|v| out_degree(v)}.min
455
- end
456
-
457
- # Minimum degree of all vertexes of the graph.
458
- #
459
- # @return [Integer] `min` between {GraphBuilder#min_in_degree min_in_degree}
460
- # and {GraphBuilder#min_out_degree max_out_degree}
461
- def min_degree
462
- [min_in_degree, min_out_degree].min
463
- end
464
-
465
- # Maximum in-degree of the graph.
466
- #
467
- # @return [Integer, nil] returns `nil` if the graph is empty
468
- def max_in_degree
469
- return nil if to_a.empty?
470
- vertices.map { |v| in_degree(v)}.max
471
- end
472
-
473
- # Maximum out-degree of the graph.
474
- #
475
- # @return [Integer, nil] returns nil if the graph is empty
476
- def max_out_degree
477
- return nil if to_a.empty?
478
- vertices.map { |v| out_degree(v)}.max
479
- end
480
-
481
- # Maximum degree of all vertexes of the graph.
482
- #
483
- # @return [Integer] `max` between {GraphBuilder#max_in_degree max_in_degree}
484
- # and {GraphBuilder#max_out_degree max_out_degree}
485
- def max_degree
486
- [max_in_degree, max_out_degree].max
487
- end
488
-
489
- # Is the graph regular, that is are its min degree and max degree equal?
490
- #
491
- # @return [Boolean]
492
- def regular?
493
- min_degree == max_degree
494
- end
495
-
496
- # Number of vertices.
497
- #
498
- # @return [Integer]
499
- def size
500
- vertices.size
501
- end
502
- alias num_vertices size
503
- alias number_of_vertices size
504
-
505
- # Number of vertices.
506
- #
507
- # @return [Integer]
508
- def num_vertices
509
- vertices.size
510
- end
511
- alias number_of_vertices num_vertices
512
-
513
- # Number of edges.
514
- #
515
- # @return [Integer]
516
- def num_edges
517
- edges.size
518
- end
519
- alias number_of_edges num_edges
520
-
521
- # Utility method to show a string representation of the edges of the graph.
522
- #def to_s
523
- #edges.to_s
524
- #end
525
-
526
- # Equality is defined to be same set of edges and directed?
527
- def eql?(g)
528
- return false unless g.is_a? Plexus::Graph
529
-
530
- (directed? == g.directed?) and
531
- (vertices.sort == g.vertices.sort) and
532
- (edges.sort == g.edges.sort)
533
- end
534
- alias == eql?
535
-
536
- # Merges another graph into the receiver.
537
- #
538
- # @param [Graph] other the graph to merge in
539
- # @return [Graph] `self`
540
- def merge(other)
541
- other.vertices.each { |v| add_vertex!(v) }
542
- other.edges.each { |e| add_edge!(e) }
543
- other.edges.each { |e| add_edge!(e.reverse) } if directed? and !other.directed?
544
- self
545
- end
546
-
547
- # A synonym for {GraphBuilder#merge merge}, but doesn't modify the current graph.
548
- #
549
- # @param [Graph, Arc] other
550
- # @return [Graph] a new graph
551
- def +(other)
552
- result = self.class.new(self)
553
- case other
554
- when Plexus::Graph
555
- result.merge(other)
556
- when Plexus::Arc
557
- result.add_edge!(other)
558
- else
559
- result.add_vertex!(other)
560
- end
561
- end
562
-
563
- # Removes all vertices in the specified graph.
564
- #
565
- # @param [Graph, Arc] other
566
- # @return [Graph]
567
- def -(other)
568
- case other
569
- when Plexus::Graph
570
- induced_subgraph(vertices - other.vertices)
571
- when Plexus::Arc
572
- self.class.new(self).remove_edge!(other)
573
- else
574
- self.class.new(self).remove_vertex!(other)
575
- end
576
- end
577
-
578
- # A synonym for {AdjacencyGraphBuilder#add_edge! add_edge!}.
579
- def <<(edge)
580
- add_edge!(edge)
581
- end
582
-
583
- # Computes the complement of the current graph.
584
- #
585
- # @return [Graph]
586
- def complement
587
- vertices.inject(self.class.new) do |a,v|
588
- a.add_vertex!(v)
589
- vertices.each { |v2| a.add_edge!(v, v2) unless edge?(v, v2) }; a
590
- end
591
- end
592
-
593
- # Given an array of vertices, computes the induced subgraph.
594
- #
595
- # @param [Array(vertex)] v
596
- # @return [Graph]
597
- def induced_subgraph(v)
598
- edges.inject(self.class.new) do |a,e|
599
- (v.include?(e.source) and v.include?(e.target)) ? (a << e) : a
600
- end
601
- end
602
-
603
- def inspect
604
- ## FIXME: broken, it's not updated. The issue's not with inspect, but it's worth mentionning here.
605
- ## Example:
606
- ## dg = Digraph[1,2, 2,3, 2,4, 4,5, 6,4, 1,6]
607
- ## dg.add_vertices! 1, 5, "yosh"
608
- ## # => Plexus::Digraph[Plexus::Arc[1,2,nil], Plexus::Arc[1,6,nil], Plexus::Arc[2,3,nil], Plexus::Arc[2,4,nil], Plexus::Arc[4,5,nil], Plexus::Arc[6,4,nil]]
609
- ## dg.vertex?("yosh")
610
- ## # => true
611
- ## dg
612
- ## # =>Plexus::Digraph[Plexus::Arc[1,2,nil], Plexus::Arc[1,6,nil], Plexus::Arc[2,3,nil], Plexus::Arc[2,4,nil], Plexus::Arc[4,5,nil], Plexus::Arc[6,4,nil]]
613
- ## the new vertex doesn't show up.
614
- ## Actually this version of inspect is far too verbose IMO :)
615
- l = vertices.select { |v| self[v]}.map { |u| "vertex_label_set(#{u.inspect}, #{self[u].inspect})"}.join('.')
616
- self.class.to_s + '[' + edges.map {|e| e.inspect}.join(', ') + ']' + (l && l != '' ? '.'+l : '')
617
- end
618
-
619
- private
620
-
621
- # ?
622
- def edge_convert(*args)
623
- args[0].is_a?(Plexus::Arc) ? args[0] : edge_class[*args]
624
- end
625
- end
626
- end