jumoku 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -0
- data/lib/jumoku.rb +4 -2
- data/lib/jumoku/version.rb +1 -1
- data/spec/raw_tree_spec.rb +353 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/tree_spec.rb +553 -0
- data/vendor/git/graphy/CREDITS.md +31 -0
- data/vendor/git/graphy/LICENSE +35 -0
- data/vendor/git/graphy/README.md +186 -0
- data/vendor/git/graphy/Rakefile +61 -0
- data/vendor/git/graphy/TODO.md +20 -0
- data/vendor/git/graphy/VERSION +1 -0
- data/vendor/git/graphy/examples/graph_self.rb +56 -0
- data/vendor/git/graphy/examples/module_graph.jpg +0 -0
- data/vendor/git/graphy/examples/module_graph.rb +14 -0
- data/vendor/git/graphy/examples/self_graph.jpg +0 -0
- data/vendor/git/graphy/examples/visualize.jpg +0 -0
- data/vendor/git/graphy/examples/visualize.rb +10 -0
- data/vendor/git/graphy/graphy.gemspec +149 -0
- data/vendor/git/graphy/lib/graphy.rb +90 -0
- data/vendor/git/graphy/lib/graphy/adjacency_graph.rb +224 -0
- data/vendor/git/graphy/lib/graphy/arc.rb +65 -0
- data/vendor/git/graphy/lib/graphy/arc_number.rb +52 -0
- data/vendor/git/graphy/lib/graphy/biconnected.rb +84 -0
- data/vendor/git/graphy/lib/graphy/chinese_postman.rb +91 -0
- data/vendor/git/graphy/lib/graphy/classes/graph_classes.rb +28 -0
- data/vendor/git/graphy/lib/graphy/common.rb +63 -0
- data/vendor/git/graphy/lib/graphy/comparability.rb +63 -0
- data/vendor/git/graphy/lib/graphy/directed_graph.rb +76 -0
- data/vendor/git/graphy/lib/graphy/directed_graph/algorithms.rb +92 -0
- data/vendor/git/graphy/lib/graphy/directed_graph/distance.rb +167 -0
- data/vendor/git/graphy/lib/graphy/dot.rb +94 -0
- data/vendor/git/graphy/lib/graphy/edge.rb +37 -0
- data/vendor/git/graphy/lib/graphy/ext.rb +79 -0
- data/vendor/git/graphy/lib/graphy/graph.rb +631 -0
- data/vendor/git/graphy/lib/graphy/graph_api.rb +35 -0
- data/vendor/git/graphy/lib/graphy/labels.rb +113 -0
- data/vendor/git/graphy/lib/graphy/maximum_flow.rb +77 -0
- data/vendor/git/graphy/lib/graphy/ruby_compatibility.rb +17 -0
- data/vendor/git/graphy/lib/graphy/search.rb +511 -0
- data/vendor/git/graphy/lib/graphy/strong_components.rb +93 -0
- data/vendor/git/graphy/lib/graphy/support/support.rb +9 -0
- data/vendor/git/graphy/lib/graphy/undirected_graph.rb +57 -0
- data/vendor/git/graphy/lib/graphy/undirected_graph/algorithms.rb +90 -0
- data/vendor/git/graphy/spec/biconnected_spec.rb +27 -0
- data/vendor/git/graphy/spec/chinese_postman_spec.rb +27 -0
- data/vendor/git/graphy/spec/community_spec.rb +44 -0
- data/vendor/git/graphy/spec/complement_spec.rb +27 -0
- data/vendor/git/graphy/spec/digraph_distance_spec.rb +121 -0
- data/vendor/git/graphy/spec/digraph_spec.rb +339 -0
- data/vendor/git/graphy/spec/dot_spec.rb +48 -0
- data/vendor/git/graphy/spec/edge_spec.rb +159 -0
- data/vendor/git/graphy/spec/inspection_spec.rb +40 -0
- data/vendor/git/graphy/spec/multi_edge_spec.rb +32 -0
- data/vendor/git/graphy/spec/neighborhood_spec.rb +38 -0
- data/vendor/git/graphy/spec/properties_spec.rb +146 -0
- data/vendor/git/graphy/spec/search_spec.rb +227 -0
- data/vendor/git/graphy/spec/spec.opts +4 -0
- data/vendor/git/graphy/spec/spec_helper.rb +56 -0
- data/vendor/git/graphy/spec/strong_components_spec.rb +61 -0
- data/vendor/git/graphy/spec/triangulated_spec.rb +125 -0
- data/vendor/git/graphy/spec/undirected_graph_spec.rb +220 -0
- data/vendor/git/graphy/vendor/priority-queue/CHANGELOG +33 -0
- data/vendor/git/graphy/vendor/priority-queue/Makefile +140 -0
- data/vendor/git/graphy/vendor/priority-queue/README +133 -0
- data/vendor/git/graphy/vendor/priority-queue/benchmark/dijkstra.rb +171 -0
- data/vendor/git/graphy/vendor/priority-queue/compare_comments.rb +49 -0
- data/vendor/git/graphy/vendor/priority-queue/doc/c-vs-rb.png +0 -0
- data/vendor/git/graphy/vendor/priority-queue/doc/compare_big.gp +14 -0
- data/vendor/git/graphy/vendor/priority-queue/doc/compare_big.png +0 -0
- data/vendor/git/graphy/vendor/priority-queue/doc/compare_small.gp +15 -0
- data/vendor/git/graphy/vendor/priority-queue/doc/compare_small.png +0 -0
- data/vendor/git/graphy/vendor/priority-queue/doc/results.csv +37 -0
- data/vendor/git/graphy/vendor/priority-queue/ext/priority_queue/CPriorityQueue/extconf.rb +2 -0
- data/vendor/git/graphy/vendor/priority-queue/ext/priority_queue/CPriorityQueue/priority_queue.c +947 -0
- data/vendor/git/graphy/vendor/priority-queue/lib/priority_queue.rb +14 -0
- data/vendor/git/graphy/vendor/priority-queue/lib/priority_queue/c_priority_queue.rb +1 -0
- data/vendor/git/graphy/vendor/priority-queue/lib/priority_queue/poor_priority_queue.rb +46 -0
- data/vendor/git/graphy/vendor/priority-queue/lib/priority_queue/ruby_priority_queue.rb +526 -0
- data/vendor/git/graphy/vendor/priority-queue/priority_queue.so +0 -0
- data/vendor/git/graphy/vendor/priority-queue/setup.rb +1551 -0
- data/vendor/git/graphy/vendor/priority-queue/test/priority_queue_test.rb +371 -0
- data/vendor/git/graphy/vendor/rdot.rb +360 -0
- metadata +83 -1
@@ -0,0 +1,90 @@
|
|
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
|
+
require 'set'
|
30
|
+
|
31
|
+
module Graphy
|
32
|
+
# Graphy internals: graph builders and additionnal behaviors
|
33
|
+
autoload :GraphAPI, 'graphy/graph_api'
|
34
|
+
|
35
|
+
autoload :GraphBuilder, 'graphy/graph'
|
36
|
+
autoload :AdjacencyGraphBuilder, 'graphy/adjacency_graph'
|
37
|
+
|
38
|
+
autoload :DirectedGraphBuilder, 'graphy/directed_graph'
|
39
|
+
autoload :DigraphBuilder, 'graphy/directed_graph'
|
40
|
+
autoload :DirectedPseudoGraphBuilder, 'graphy/directed_graph'
|
41
|
+
autoload :DirectedMultiGraphBuilder, 'graphy/directed_graph'
|
42
|
+
|
43
|
+
autoload :UndirectedGraphBuilder, 'graphy/undirected_graph'
|
44
|
+
autoload :UndirectedPseudoGraphBuilder, 'graphy/undirected_graph'
|
45
|
+
autoload :UndirectedMultiGraphBuilder, 'graphy/undirected_graph'
|
46
|
+
|
47
|
+
autoload :Arc, 'graphy/arc'
|
48
|
+
autoload :ArcNumber, 'graphy/arc_number'
|
49
|
+
autoload :Biconnected, 'graphy/biconnected'
|
50
|
+
autoload :ChinesePostman, 'graphy/chinese_postman'
|
51
|
+
autoload :Common, 'graphy/common'
|
52
|
+
autoload :Comparability, 'graphy/comparability'
|
53
|
+
|
54
|
+
autoload :Dot, 'graphy/dot'
|
55
|
+
autoload :Edge, 'graphy/edge'
|
56
|
+
autoload :Labels, 'graphy/labels'
|
57
|
+
autoload :MaximumFlow, 'graphy/maximum_flow'
|
58
|
+
#autoload :Rdot, 'graphy/dot'
|
59
|
+
autoload :Search, 'graphy/search'
|
60
|
+
autoload :StrongComponents, 'graphy/strong_components'
|
61
|
+
|
62
|
+
# Graphy classes
|
63
|
+
autoload :AdjacencyGraph, 'graphy/classes/graph_classes'
|
64
|
+
autoload :DirectedGraph, 'graphy/classes/graph_classes'
|
65
|
+
autoload :Digraph, 'graphy/classes/graph_classes'
|
66
|
+
autoload :DirectedPseudoGraph, 'graphy/classes/graph_classes'
|
67
|
+
autoload :DirectedMultiGraph, 'graphy/classes/graph_classes'
|
68
|
+
autoload :UndirectedGraph, 'graphy/classes/graph_classes'
|
69
|
+
autoload :UndirectedPseudoGraph, 'graphy/classes/graph_classes'
|
70
|
+
autoload :UndirectedMultiGraph, 'graphy/classes/graph_classes'
|
71
|
+
|
72
|
+
# ruby stdlib extensions
|
73
|
+
require 'graphy/ext'
|
74
|
+
# ruby 1.8.x/1.9.x compatibility
|
75
|
+
require 'graphy/ruby_compatibility'
|
76
|
+
end
|
77
|
+
|
78
|
+
# Vendored libraries
|
79
|
+
|
80
|
+
require 'pathname'
|
81
|
+
path = Pathname.new(__FILE__)
|
82
|
+
$LOAD_PATH.unshift(path + '../../vendor')
|
83
|
+
$LOAD_PATH.unshift(path + '../../vendor/priority-queue/lib')
|
84
|
+
|
85
|
+
require 'rdot'
|
86
|
+
require 'facets/hash'
|
87
|
+
|
88
|
+
require 'priority_queue/ruby_priority_queue'
|
89
|
+
PriorityQueue = RubyPriorityQueue
|
90
|
+
|
@@ -0,0 +1,224 @@
|
|
1
|
+
module Graphy
|
2
|
+
|
3
|
+
# This module provides the basic routines needed to implement the specialized builders:
|
4
|
+
# {DigraphBuilder}, {UndirectedGraphBuilder}, {DirectedPseudoGraphBuilder},
|
5
|
+
# {UndirectedPseudoGraphBuilder}, {DirectedMultiGraphBuilder} and {UndirectedMultiGraphBuilder}
|
6
|
+
# modules, each of them streamlining {AdjacencyGraphBuilder}'s behavior. Those
|
7
|
+
# implementations rely on the {GraphBuilder}, under the control of the {GraphAPI}.
|
8
|
+
module AdjacencyGraphBuilder
|
9
|
+
|
10
|
+
# Defines a useful `push` -> `add` alias for arrays.
|
11
|
+
class ArrayWithAdd < Array
|
12
|
+
alias add push
|
13
|
+
end
|
14
|
+
|
15
|
+
# This method is called by the specialized implementations
|
16
|
+
# upon graph creation.
|
17
|
+
#
|
18
|
+
# Initialization parameters can include:
|
19
|
+
#
|
20
|
+
# * an array of edges to add
|
21
|
+
# * one or several graphs to copy (will be merged if multiple)
|
22
|
+
# * `:parallel_edges` denotes that duplicate edges are allowed
|
23
|
+
# * `:loops denotes` that loops are allowed
|
24
|
+
#
|
25
|
+
# @param *params [Hash] the initialization parameters
|
26
|
+
def implementation_initialize(*params)
|
27
|
+
@vertex_dict = Hash.new
|
28
|
+
clear_all_labels
|
29
|
+
|
30
|
+
# FIXME: could definitely make use of the activesupport helper
|
31
|
+
# extract_options! and facets' reverse_merge! technique
|
32
|
+
# to handle parameters
|
33
|
+
args = (params.pop if params.last.is_a? Hash) || {}
|
34
|
+
|
35
|
+
# Basic configuration of adjacency.
|
36
|
+
@allow_loops = args[:loops] || false
|
37
|
+
@parallel_edges = args[:parallel_edges] || false
|
38
|
+
@edgelist_class = @parallel_edges ? ArrayWithAdd : Set
|
39
|
+
if @parallel_edges
|
40
|
+
@edge_number = Hash.new
|
41
|
+
@next_edge_number = 0
|
42
|
+
end
|
43
|
+
|
44
|
+
# Copy any given graph into this graph.
|
45
|
+
params.select { |p| p.is_a? Graphy::GraphBuilder }.each do |g|
|
46
|
+
g.edges.each do |e|
|
47
|
+
add_edge!(e)
|
48
|
+
edge_label_set(e, edge_label(e)) if edge_label(e)
|
49
|
+
end
|
50
|
+
g.vertices.each do |v|
|
51
|
+
add_vertex!(v)
|
52
|
+
vertex_label_set(v, vertex_label(v)) if vertex_label(v)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Add all array edges specified.
|
57
|
+
params.select { |p| p.is_a? Array }.each do |a|
|
58
|
+
0.step(a.size-1, 2) { |i| add_edge!(a[i], a[i+1]) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns true if v is a vertex of this Graph
|
63
|
+
# (an "O(1)" implementation of `vertex?`).
|
64
|
+
#
|
65
|
+
# @param [vertex] v
|
66
|
+
# @return [Boolean]
|
67
|
+
def vertex?(v)
|
68
|
+
@vertex_dict.has_key?(v)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns true if [u,v] or u is an {Arc}
|
72
|
+
# (an "O(1)" implementation of `edge?`).
|
73
|
+
#
|
74
|
+
# @param [vertex] u
|
75
|
+
# @param [vertex] v (nil)
|
76
|
+
# @return [Boolean]
|
77
|
+
def edge?(u, v = nil)
|
78
|
+
u, v = u.source, u.target if u.is_a? Graphy::Arc
|
79
|
+
vertex?(u) and @vertex_dict[u].include?(v)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Adds a vertex to the graph with an optional label.
|
83
|
+
#
|
84
|
+
# @param [vertex(Object)] vertex any kind of Object can act as a vertex
|
85
|
+
# @param [#to_s] label (nil)
|
86
|
+
def add_vertex!(vertex, label = nil)
|
87
|
+
@vertex_dict[vertex] ||= @edgelist_class.new
|
88
|
+
self[vertex] = label if label
|
89
|
+
self
|
90
|
+
end
|
91
|
+
|
92
|
+
# Adds an edge to the graph.
|
93
|
+
#
|
94
|
+
# Can be called in two basic ways, label is optional:
|
95
|
+
# @overload add_edge!(arc)
|
96
|
+
# Using an explicit {Arc}
|
97
|
+
# @param [Arc] arc an {Arc}[source, target, label = nil] object
|
98
|
+
# @return [AdjacencyGraph] `self`
|
99
|
+
# @overload add_edge!(source, target, label = nil)
|
100
|
+
# Using vertices to define an arc implicitly
|
101
|
+
# @param [vertex] u
|
102
|
+
# @param [vertex] v (nil)
|
103
|
+
# @param [Label] l (nil)
|
104
|
+
# @param [Integer] n (nil) {Arc arc} number of `(u, v)` (if `nil` and if `u`
|
105
|
+
# has an {ArcNumber}, then it will be used)
|
106
|
+
# @return [AdjacencyGraph] `self`
|
107
|
+
def add_edge!(u, v = nil, l = nil, n = nil)
|
108
|
+
n = u.number if u.class.include? ArcNumber and n.nil?
|
109
|
+
u, v, l = u.source, u.target, u.label if u.is_a? Graphy::Arc
|
110
|
+
|
111
|
+
return self if not @allow_loops and u == v
|
112
|
+
|
113
|
+
n = (@next_edge_number += 1) unless n if @parallel_edges
|
114
|
+
add_vertex!(u)
|
115
|
+
add_vertex!(v)
|
116
|
+
@vertex_dict[u].add(v)
|
117
|
+
(@edge_number[u] ||= @edgelist_class.new).add(n) if @parallel_edges
|
118
|
+
|
119
|
+
unless directed?
|
120
|
+
@vertex_dict[v].add(u)
|
121
|
+
(@edge_number[v] ||= @edgelist_class.new).add(n) if @parallel_edges
|
122
|
+
end
|
123
|
+
|
124
|
+
self[n ? edge_class[u,v,n] : edge_class[u,v]] = l if l
|
125
|
+
self
|
126
|
+
end
|
127
|
+
|
128
|
+
# Removes a given vertex from the graph.
|
129
|
+
#
|
130
|
+
# @param [vertex] v
|
131
|
+
# @return [AdjacencyGraph] `self`
|
132
|
+
def remove_vertex!(v)
|
133
|
+
# FIXME This is broken for multi graphs
|
134
|
+
@vertex_dict.delete(v)
|
135
|
+
@vertex_dict.each_value { |adjList| adjList.delete(v) }
|
136
|
+
@vertex_dict.keys.each do |u|
|
137
|
+
delete_label(edge_class[u,v])
|
138
|
+
delete_label(edge_class[v,u])
|
139
|
+
end
|
140
|
+
delete_label(v)
|
141
|
+
self
|
142
|
+
end
|
143
|
+
|
144
|
+
# Removes an edge from the graph.
|
145
|
+
#
|
146
|
+
# Can be called with both source and target as vertex,
|
147
|
+
# or with source and object of {Graphy::Arc} derivation.
|
148
|
+
#
|
149
|
+
# @overload remove_edge!(a)
|
150
|
+
# @param [Graphy::Arc] a
|
151
|
+
# @return [AdjacencyGraph] `self`
|
152
|
+
# @raise [ArgumentError] if parallel edges are enabled
|
153
|
+
# @overload remove_edge!(u, v)
|
154
|
+
# @param [vertex] u
|
155
|
+
# @param [vertex] v
|
156
|
+
# @return [AdjacencyGraph] `self`
|
157
|
+
# @raise [ArgumentError] if parallel edges are enabled and the {ArcNumber} of `u` is zero
|
158
|
+
def remove_edge!(u, v = nil)
|
159
|
+
unless u.is_a? Graphy::Arc
|
160
|
+
raise ArgumentError if @parallel_edges
|
161
|
+
u = edge_class[u,v]
|
162
|
+
end
|
163
|
+
raise ArgumentError if @parallel_edges and (u.number || 0) == 0
|
164
|
+
return self unless @vertex_dict[u.source] # It doesn't exist
|
165
|
+
delete_label(u) # Get rid of label
|
166
|
+
if @parallel_edges
|
167
|
+
index = @edge_number[u.source].index(u.number)
|
168
|
+
raise NoArcError unless index
|
169
|
+
@vertex_dict[u.source].delete_at(index)
|
170
|
+
@edge_number[u.source].delete_at(index)
|
171
|
+
else
|
172
|
+
@vertex_dict[u.source].delete(u.target)
|
173
|
+
end
|
174
|
+
self
|
175
|
+
end
|
176
|
+
|
177
|
+
# Returns an array of vertices that the graph has.
|
178
|
+
#
|
179
|
+
# @return [Array] graph's vertices
|
180
|
+
def vertices
|
181
|
+
@vertex_dict.keys
|
182
|
+
end
|
183
|
+
|
184
|
+
# Returns an array of edges, most likely of class {Arc} or {Edge} depending
|
185
|
+
# upon the type of graph.
|
186
|
+
#
|
187
|
+
# @return [Array]
|
188
|
+
def edges
|
189
|
+
@vertex_dict.keys.inject(Set.new) do |a,v|
|
190
|
+
if @parallel_edges and @edge_number[v]
|
191
|
+
@vertex_dict[v].zip(@edge_number[v]).each do |w|
|
192
|
+
s, t, n = v, w[0], w[1]
|
193
|
+
a.add(edge_class[s, t, n, edge_label(s, t, n)])
|
194
|
+
end
|
195
|
+
else
|
196
|
+
@vertex_dict[v].each do |w|
|
197
|
+
a.add(edge_class[v, w, edge_label(v, w)])
|
198
|
+
end
|
199
|
+
end
|
200
|
+
a
|
201
|
+
end.to_a
|
202
|
+
end
|
203
|
+
|
204
|
+
# FIXME, EFFED UP (but why?)
|
205
|
+
#
|
206
|
+
# @fixme
|
207
|
+
def adjacent(x, options = {})
|
208
|
+
options[:direction] ||= :out
|
209
|
+
if !x.is_a?(Graphy::Arc) and (options[:direction] == :out || !directed?)
|
210
|
+
if options[:type] == :edges
|
211
|
+
i = -1
|
212
|
+
@parallel_edges ?
|
213
|
+
@vertex_dict[x].map { |v| e = edge_class[x, v, @edge_number[x][i+=1]]; e.label = self[e]; e } :
|
214
|
+
@vertex_dict[x].map { |v| e = edge_class[x, v]; e.label = self[e]; e }
|
215
|
+
else
|
216
|
+
@vertex_dict[x].to_a
|
217
|
+
end
|
218
|
+
else
|
219
|
+
graph_adjacent(x,options)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
end # Adjacency Graph
|
224
|
+
end # Graphy
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Graphy
|
2
|
+
|
3
|
+
# Arc includes classes for representing egdes of directed and
|
4
|
+
# undirected graphs. There is no need for a Vertex class, because any ruby
|
5
|
+
# object can be a vertex of a graph.
|
6
|
+
#
|
7
|
+
# Arc's base is a Struct with a :source, a :target and a :label
|
8
|
+
Struct.new("ArcBase", :source, :target, :label)
|
9
|
+
|
10
|
+
class Arc < Struct::ArcBase
|
11
|
+
|
12
|
+
def initialize(p_source, p_target, p_label = nil)
|
13
|
+
super(p_source, p_target, p_label)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Ignore labels for equality.
|
17
|
+
def eql?(other)
|
18
|
+
self.class == other.class and target == other.target and source == other.source
|
19
|
+
end
|
20
|
+
alias == eql?
|
21
|
+
|
22
|
+
# Returns (v,u) if self == (u,v).
|
23
|
+
def reverse() self.class.new(target, source, label); end
|
24
|
+
|
25
|
+
# Sort support.
|
26
|
+
def <=>(rhs)
|
27
|
+
[source, target] <=> [rhs.source, rhs.target]
|
28
|
+
end
|
29
|
+
|
30
|
+
# Arc.new[1,2].to_s => "(1-2 'label')"
|
31
|
+
def to_s
|
32
|
+
l = label ? " '#{label.to_s}'" : ''
|
33
|
+
"(#{source}-#{target}#{l})"
|
34
|
+
end
|
35
|
+
|
36
|
+
# Hash is defined in such a way that label is not
|
37
|
+
# part of the hash value
|
38
|
+
# FIXME: I had to get rid of that in order to make to_dot_graph
|
39
|
+
# work, but I can't figure it out (doesn't show up in the stack!)
|
40
|
+
#def hash
|
41
|
+
#puts "--- #{caller}"
|
42
|
+
##puts source.inspect
|
43
|
+
##puts target.inspect
|
44
|
+
#source.hash ^ (target.hash + 1)
|
45
|
+
#puts "---"
|
46
|
+
#end
|
47
|
+
|
48
|
+
# Shortcut constructor.
|
49
|
+
#
|
50
|
+
# Instead of Arc.new(1,2) one can use Arc[1,2].
|
51
|
+
def self.[](p_source, p_target, p_label = nil)
|
52
|
+
new(p_source, p_target, p_label)
|
53
|
+
end
|
54
|
+
|
55
|
+
#def inspect
|
56
|
+
#"#{self.class.to_s}[#{source.inspect},#{target.inspect},#{label.inspect}]"
|
57
|
+
#end
|
58
|
+
|
59
|
+
end # Arc
|
60
|
+
|
61
|
+
class MultiArc < Arc
|
62
|
+
include ArcNumber
|
63
|
+
end
|
64
|
+
|
65
|
+
end # Graphy
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Graphy
|
2
|
+
# This module handles internal numbering of edges in order to differente between mutliple edges.
|
3
|
+
module ArcNumber
|
4
|
+
|
5
|
+
# Used to differentiate between mutli-edges
|
6
|
+
attr_accessor :number
|
7
|
+
|
8
|
+
def initialize(p_source, p_target, p_number, p_label = nil)
|
9
|
+
self.number = p_number
|
10
|
+
super(p_source, p_target, p_label)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns (v,u) if self == (u,v).
|
14
|
+
def reverse
|
15
|
+
self.class.new(target, source, number, label)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Allow for hashing of self loops.
|
19
|
+
def hash
|
20
|
+
super ^ number.hash
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_s
|
24
|
+
super + "[#{number}]"
|
25
|
+
end
|
26
|
+
|
27
|
+
def <=>(rhs)
|
28
|
+
(result = super(rhs)) == 0 ? number <=> rhs.number : result
|
29
|
+
end
|
30
|
+
|
31
|
+
def inspect
|
32
|
+
"#{self.class.to_s}[#{source.inspect},#{target.inspect},#{number.inspect},#{label.inspect}]"
|
33
|
+
end
|
34
|
+
|
35
|
+
def eql?(rhs)
|
36
|
+
super(rhs) and (rhs.number.nil? or number.nil? or number == rhs.number)
|
37
|
+
end
|
38
|
+
|
39
|
+
def ==(rhs)
|
40
|
+
eql?(rhs)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Shortcut constructor. Instead of Arc.new(1,2) one can use Arc[1,2]
|
44
|
+
def self.included(cl)
|
45
|
+
# FIXME: lacks a cl.class_eval, no?
|
46
|
+
def cl.[](p_source, p_target, p_number = nil, p_label = nil)
|
47
|
+
new(p_source, p_target, p_number, p_label)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end # ArcNumber
|
52
|
+
end # Graphy
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Graphy
|
2
|
+
|
3
|
+
# Biconnected is a module for adding the biconnected algorithm to
|
4
|
+
# UndirectedGraphs
|
5
|
+
module Biconnected
|
6
|
+
|
7
|
+
# biconnected computes the biconnected subgraphs
|
8
|
+
# of a graph using Tarjan's algorithm based on DFS. See: Robert E. Tarjan
|
9
|
+
# _Depth_First_Search_and_Linear_Graph_Algorithms_. SIAM Journal on
|
10
|
+
# Computing, 1(2):146-160, 1972
|
11
|
+
#
|
12
|
+
# The output of the algorithm is a pair, the first value is an
|
13
|
+
# array of biconnected subgraphs. The second is the set of
|
14
|
+
# articulation vertices.
|
15
|
+
#
|
16
|
+
# A connected graph is biconnected if the removal of any single vertex
|
17
|
+
# (and all edges incident on that vertex) cannot disconnect the graph.
|
18
|
+
# More generally, the biconnected components of a graph are the maximal
|
19
|
+
# subsets of vertices such that the removal of a vertex from a particular
|
20
|
+
# component will not disconnect the component. Unlike connected components,
|
21
|
+
# vertices may belong to multiple biconnected components: those vertices
|
22
|
+
# that belong to more than one biconnected component are called articulation
|
23
|
+
# points or, equivalently, cut vertices. Articulation points are vertices
|
24
|
+
# whose removal would increase the number of connected components in the graph.
|
25
|
+
# Thus, a graph without articulation points is biconnected.
|
26
|
+
def biconnected
|
27
|
+
dfs_num = 0
|
28
|
+
number = {}; predecessor = {}; low_point = {}
|
29
|
+
stack = []; result = []; articulation= []
|
30
|
+
|
31
|
+
root_vertex = Proc.new {|v| predecessor[v]=v }
|
32
|
+
enter_vertex = Proc.new {|u| number[u]=low_point[u]=(dfs_num+=1) }
|
33
|
+
tree_edge = Proc.new do |e|
|
34
|
+
stack.push(e)
|
35
|
+
predecessor[e.target] = e.source
|
36
|
+
end
|
37
|
+
back_edge = Proc.new do |e|
|
38
|
+
if e.target != predecessor[e.source]
|
39
|
+
stack.push(e)
|
40
|
+
low_point[e.source] = [low_point[e.source], number[e.target]].min
|
41
|
+
end
|
42
|
+
end
|
43
|
+
exit_vertex = Proc.new do |u|
|
44
|
+
parent = predecessor[u]
|
45
|
+
is_articulation_point = false
|
46
|
+
if number[parent] > number[u]
|
47
|
+
parent = predecessor[parent]
|
48
|
+
is_articulation_point = true
|
49
|
+
end
|
50
|
+
if parent == u
|
51
|
+
is_articulation_point = false if (number[u] + 1) == number[predecessor[u]]
|
52
|
+
else
|
53
|
+
low_point[parent] = [low_point[parent], low_point[u]].min
|
54
|
+
if low_point[u] >= number[parent]
|
55
|
+
if number[parent] > number[predecessor[parent]]
|
56
|
+
predecessor[u] = predecessor[parent]
|
57
|
+
predecessor[parent] = u
|
58
|
+
end
|
59
|
+
result << (component = self.class.new)
|
60
|
+
while number[stack[-1].source] >= number[u]
|
61
|
+
component.add_edge!(stack.pop)
|
62
|
+
end
|
63
|
+
component.add_edge!(stack.pop)
|
64
|
+
if stack.empty?
|
65
|
+
predecessor[u] = parent
|
66
|
+
predecessor[parent] = u
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
articulation << u if is_articulation_point
|
71
|
+
end
|
72
|
+
|
73
|
+
# Execute depth first search
|
74
|
+
dfs({:root_vertex => root_vertex,
|
75
|
+
:enter_vertex => enter_vertex,
|
76
|
+
:tree_edge => tree_edge,
|
77
|
+
:back_edge => back_edge,
|
78
|
+
:exit_vertex => exit_vertex})
|
79
|
+
|
80
|
+
[result, articulation]
|
81
|
+
end # biconnected
|
82
|
+
|
83
|
+
end # Biconnected
|
84
|
+
end # Graphy
|