gratr19 0.4.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README +335 -0
- data/examples/graph_self.rb +54 -0
- data/examples/module_graph.jpg +0 -0
- data/examples/module_graph.rb +12 -0
- data/examples/self_graph.jpg +0 -0
- data/examples/visualize.jpg +0 -0
- data/examples/visualize.rb +8 -0
- data/install.rb +49 -0
- data/lib/gratr.rb +42 -0
- data/lib/gratr/adjacency_graph.rb +230 -0
- data/lib/gratr/base.rb +34 -0
- data/lib/gratr/biconnected.rb +116 -0
- data/lib/gratr/chinese_postman.rb +123 -0
- data/lib/gratr/common.rb +74 -0
- data/lib/gratr/comparability.rb +92 -0
- data/lib/gratr/digraph.rb +115 -0
- data/lib/gratr/digraph_distance.rb +185 -0
- data/lib/gratr/dot.rb +90 -0
- data/lib/gratr/edge.rb +145 -0
- data/lib/gratr/graph.rb +314 -0
- data/lib/gratr/graph_api.rb +82 -0
- data/lib/gratr/import.rb +44 -0
- data/lib/gratr/labels.rb +103 -0
- data/lib/gratr/maximum_flow.rb +107 -0
- data/lib/gratr/rdot.rb +332 -0
- data/lib/gratr/search.rb +422 -0
- data/lib/gratr/strong_components.rb +127 -0
- data/lib/gratr/undirected_graph.rb +153 -0
- data/lib/gratr/version.rb +6 -0
- data/lib/priority-queue/benchmark/dijkstra.rb +171 -0
- data/lib/priority-queue/compare_comments.rb +49 -0
- data/lib/priority-queue/ext/priority_queue/CPriorityQueue/extconf.rb +2 -0
- data/lib/priority-queue/lib/priority_queue.rb +14 -0
- data/lib/priority-queue/lib/priority_queue/c_priority_queue.rb +1 -0
- data/lib/priority-queue/lib/priority_queue/poor_priority_queue.rb +46 -0
- data/lib/priority-queue/lib/priority_queue/ruby_priority_queue.rb +525 -0
- data/lib/priority-queue/setup.rb +1551 -0
- data/lib/priority-queue/test/priority_queue_test.rb +371 -0
- data/tests/TestBiconnected.rb +53 -0
- data/tests/TestChinesePostman.rb +53 -0
- data/tests/TestComplement.rb +54 -0
- data/tests/TestDigraph.rb +333 -0
- data/tests/TestDigraphDistance.rb +138 -0
- data/tests/TestDot.rb +75 -0
- data/tests/TestEdge.rb +171 -0
- data/tests/TestInspection.rb +57 -0
- data/tests/TestMultiEdge.rb +57 -0
- data/tests/TestNeighborhood.rb +64 -0
- data/tests/TestProperties.rb +160 -0
- data/tests/TestSearch.rb +277 -0
- data/tests/TestStrongComponents.rb +85 -0
- data/tests/TestTriagulated.rb +137 -0
- data/tests/TestUndirectedGraph.rb +219 -0
- metadata +152 -0
@@ -0,0 +1,82 @@
|
|
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 AREf
|
20
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
21
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
22
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
23
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
24
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
25
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
26
|
+
#++
|
27
|
+
|
28
|
+
|
29
|
+
module GRATR
|
30
|
+
|
31
|
+
# This defines the minimum set of functions required to make a graph class that can
|
32
|
+
# use the algorithms defined by this library
|
33
|
+
module GraphAPI
|
34
|
+
|
35
|
+
# Is the graph directed?
|
36
|
+
#
|
37
|
+
# This method must be implemented by the specific graph class
|
38
|
+
def directed?() raise NotImplementedError; end
|
39
|
+
|
40
|
+
# Add a vertex to the Graph and return the Graph
|
41
|
+
# An additional label l can be specified as well
|
42
|
+
#
|
43
|
+
# This method must be implemented by the specific graph class
|
44
|
+
def add_vertex!(v,l=nil) raise NotImplementedError; end
|
45
|
+
|
46
|
+
# Add an edge to the Graph and return the Graph
|
47
|
+
# u can be an object of type GRATR::Arc or u,v specifies
|
48
|
+
# a source, target pair. The last parameter is an optional label
|
49
|
+
#
|
50
|
+
# This method must be implemented by the specific graph class
|
51
|
+
def add_edge!(u,v=nil,l=nil) raise NotImplementedError; end
|
52
|
+
|
53
|
+
# Remove a vertex to the Graph and return the Graph
|
54
|
+
#
|
55
|
+
# This method must be implemented by the specific graph class
|
56
|
+
def remove_vertex!(v) raise NotImplementedError; end
|
57
|
+
|
58
|
+
# Remove an edge from the Graph and return the Graph
|
59
|
+
#
|
60
|
+
# Can be a type of GRATR::Arc or a source and target
|
61
|
+
# This method must be implemented by the specific graph class
|
62
|
+
def remove_edge!(u,v=nil) raise NotImplementedError; end
|
63
|
+
|
64
|
+
# Return the array of vertices.
|
65
|
+
#
|
66
|
+
# This method must be implemented by the specific graph class
|
67
|
+
def vertices() raise NotImplementedError; end
|
68
|
+
|
69
|
+
# Return the array of edges.
|
70
|
+
#
|
71
|
+
# This method must be implemented by the specific graph class
|
72
|
+
def edges() raise NotImplementedError; end
|
73
|
+
|
74
|
+
# Returns the edge class
|
75
|
+
def edge_class() raise NotImplementedError; end
|
76
|
+
|
77
|
+
# Return the chromatic number for this graph
|
78
|
+
# This is currently incomplete and in some cases will be NP-complete
|
79
|
+
# FIXME: Should this even be here? My gut feeling is no...
|
80
|
+
def chromatic_number() raise NotImplementedError; end
|
81
|
+
end
|
82
|
+
end
|
data/lib/gratr/import.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2006 Shawn Patrick Garbett
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
# are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# * Redistributions of source code must retain the above copyright notice(s),
|
8
|
+
# this list of conditions and the following disclaimer.
|
9
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer in the documentation
|
11
|
+
# and/or other materials provided with the distribution.
|
12
|
+
# * Neither the name of the Shawn Garbett nor the names of its contributors
|
13
|
+
# may be used to endorse or promote products derived from this software
|
14
|
+
# without specific prior written permission.
|
15
|
+
#
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
17
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
18
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
19
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
20
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
21
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
22
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
23
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
24
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
25
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
26
|
+
#++
|
27
|
+
|
28
|
+
|
29
|
+
require 'gratr'
|
30
|
+
|
31
|
+
# Pull all GRATR classes up into the current namespace
|
32
|
+
Arc = GRATR::Arc
|
33
|
+
Edge = GRATR::Edge
|
34
|
+
MultiArc = GRATR::MultiArc
|
35
|
+
MultiEdge = GRATR::MultiEdge
|
36
|
+
Graph = GRATR::Graph
|
37
|
+
Digraph = GRATR::Digraph
|
38
|
+
DirectedGraph = GRATR::DirectedGraph
|
39
|
+
DirectedPseudoGraph = GRATR::DirectedPseudoGraph
|
40
|
+
DirectedMultiGraph = GRATR::DirectedMultiGraph
|
41
|
+
UndirectedGraph = GRATR::UndirectedGraph
|
42
|
+
UndirectedPseudoGraph = GRATR::UndirectedPseudoGraph
|
43
|
+
UndirectedMultiGraph = GRATR::UndirectedMultiGraph
|
44
|
+
Complete = GRATR::Complete
|
data/lib/gratr/labels.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2006 Shawn Patrick Garbett
|
3
|
+
#
|
4
|
+
# Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
# are permitted provided that the following conditions are met:
|
6
|
+
#
|
7
|
+
# * Redistributions of source code must retain the above copyright notice(s),
|
8
|
+
# this list of conditions and the following disclaimer.
|
9
|
+
# * Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer in the documentation
|
11
|
+
# and/or other materials provided with the distribution.
|
12
|
+
# * Neither the name of the Shawn Garbett nor the names of its contributors
|
13
|
+
# may be used to endorse or promote products derived from this software
|
14
|
+
# without specific prior written permission.
|
15
|
+
#
|
16
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
17
|
+
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
18
|
+
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
19
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
20
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
21
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
22
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
23
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
24
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
25
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
26
|
+
#++
|
27
|
+
|
28
|
+
module GRATR
|
29
|
+
module Labels
|
30
|
+
# Return a label for an edge or vertex
|
31
|
+
def [](u) (u.kind_of? GRATR::Arc) ? edge_label(u) : vertex_label(u); end
|
32
|
+
|
33
|
+
# Set a label for an edge or vertex
|
34
|
+
def []= (u, value) (u.kind_of? GRATR::Arc) ? edge_label_set(u,value) : vertex_label_set(u, value); end
|
35
|
+
|
36
|
+
# Delete a label entirely
|
37
|
+
def delete_label(u) (u.kind_of? GRATR::Arc) ? edge_label_delete(u) : vertex_label_delete(u); end
|
38
|
+
|
39
|
+
# Get the label for an edge
|
40
|
+
def vertex_label(v) vertex_label_dict[v]; end
|
41
|
+
|
42
|
+
# Set the label for an edge
|
43
|
+
def vertex_label_set(v, l) vertex_label_dict[v] = l; self; end
|
44
|
+
|
45
|
+
# Get the label for an edge
|
46
|
+
def edge_label(u,v=nil,n=nil)
|
47
|
+
u = edge_convert(u,v,n)
|
48
|
+
edge_label_dict[u]
|
49
|
+
end
|
50
|
+
|
51
|
+
# Set the label for an edge
|
52
|
+
def edge_label_set(u, v=nil, l=nil, n=nil)
|
53
|
+
u.kind_of?(GRATR::Arc) ? l = v : u = edge_convert(u,v,n)
|
54
|
+
edge_label_dict[u] = l; self
|
55
|
+
end
|
56
|
+
|
57
|
+
# Delete all graph labels
|
58
|
+
def clear_all_labels() @vertex_labels = {}; @edge_labels = {}; end
|
59
|
+
|
60
|
+
# Delete an edge label
|
61
|
+
def edge_label_delete(u, v=nil, n=nil)
|
62
|
+
u = edge_convert(u,v,n)
|
63
|
+
edge_label_dict.delete(u)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Delete a vertex label
|
67
|
+
def vertex_label_delete(v) vertex_label_dict.delete(v); end
|
68
|
+
|
69
|
+
protected
|
70
|
+
|
71
|
+
def vertex_label_dict() @vertex_labels ||= {}; end
|
72
|
+
def edge_label_dict() @edge_labels ||= {}; end
|
73
|
+
|
74
|
+
# A generic cost function. It either calls the weight function with and edge
|
75
|
+
# constructed from the two nodes, or calls the [] operator of the label
|
76
|
+
# when given a value. If no weight value is specified, the label itself is
|
77
|
+
# treated as the cost value.
|
78
|
+
#
|
79
|
+
# Note: This function will not work for Pseudo or Multi graphs at present.
|
80
|
+
# FIXME: Remove u,v interface to fix Pseudo Multi graph problems.
|
81
|
+
def cost(u,v=nil,weight=nil)
|
82
|
+
u.kind_of?(Arc) ? weight = v : u = edge_class[u,v]
|
83
|
+
case weight
|
84
|
+
when Proc : weight.call(u)
|
85
|
+
when nil : self[u]
|
86
|
+
else self[u][weight]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# An alias of cost for property retrieval in general
|
91
|
+
alias property cost
|
92
|
+
|
93
|
+
# A function to set properties specified by the user.
|
94
|
+
def property_set(u,name,value)
|
95
|
+
case name
|
96
|
+
when Proc : name.call(value)
|
97
|
+
when nil : self[u] = value
|
98
|
+
else self[u][name] = value
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,107 @@
|
|
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 'gratr/digraph'
|
30
|
+
|
31
|
+
module GRATR
|
32
|
+
|
33
|
+
class Network < Digraph
|
34
|
+
attr_accessor :lower, :upper, :cost, :flow
|
35
|
+
|
36
|
+
def residual(residual_capacity, cost_property, zero = 0)
|
37
|
+
r = Digraph.new
|
38
|
+
edges.each do |e1|
|
39
|
+
[e1,e1.reverse].each do |e|
|
40
|
+
rij = property(e,self.upper) - property(e,self.flow) if edge? e
|
41
|
+
rij += property(e.reverse,self.flow) - property(e.reverse,self.lower) if edge? e.reverse
|
42
|
+
r.add_edge!(e) if rij > zero
|
43
|
+
r.property_set(e,residual_capacity, rij)
|
44
|
+
r.property_set(e,cost, cost(e,cost_property))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
r
|
48
|
+
end
|
49
|
+
|
50
|
+
def maximum_flow() eliminate_lower_bounds.maximum_flow_prime.restore_lower_bounds(self); end
|
51
|
+
|
52
|
+
private:
|
53
|
+
|
54
|
+
def eliminate_lower_bounds
|
55
|
+
no_lower_bounds = Digraph.new(self)
|
56
|
+
if self.upper.kind_of? Proc then
|
57
|
+
no_lower_bounds.upper = Proc.new {|e| self.upper.call(e) - property(e,self.lower) }
|
58
|
+
else
|
59
|
+
no_lower_bounds.edges.each {|e| no_lower_bounds[e][self.upper] -= property(e,self.lower)}
|
60
|
+
end
|
61
|
+
no_lower_bounds
|
62
|
+
end
|
63
|
+
|
64
|
+
def restore_lower_bounds(src)
|
65
|
+
src.edges.each do {|e| (src.flow ? src[e][src.flow] : src[e]) = property(e,self.flow) + src.property(e,self.lower) }
|
66
|
+
src
|
67
|
+
end
|
68
|
+
|
69
|
+
def maximum_flow_prime
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
module Graph
|
75
|
+
|
76
|
+
module MaximumFlow
|
77
|
+
|
78
|
+
# Maximum flow, it returns an array with the maximum flow and a hash of flow per edge
|
79
|
+
# Currently a highly inefficient implementation, FIXME, This should use Goldberg and Tarjan's method.
|
80
|
+
def maximum_flow(s, t, capacity = nil, zero = 0)
|
81
|
+
flow = Hash.new(zero)
|
82
|
+
available = Hash.new(zero)
|
83
|
+
total = zero
|
84
|
+
edges.each {|e| available[e] = cost(e,capacity)}
|
85
|
+
adj_positive = Proc.new do |u|
|
86
|
+
adjacent(u).select {|r| available[edge_class[u,r]] > zero}
|
87
|
+
end
|
88
|
+
while (tree = bfs_tree_from_vertex(start))[t]
|
89
|
+
route = [t]
|
90
|
+
while route[-1] != s
|
91
|
+
route << tree[route[route[-1]]]
|
92
|
+
raise ArgumentError, "No route from #{s} to #{t} possible"
|
93
|
+
end; route.reverse
|
94
|
+
amt = route.map {|e| available[e]}.min
|
95
|
+
route.each do |e|
|
96
|
+
flow[e] += amt
|
97
|
+
available[e] -= amt
|
98
|
+
end
|
99
|
+
total += amt
|
100
|
+
end
|
101
|
+
|
102
|
+
[total, flow]
|
103
|
+
end
|
104
|
+
|
105
|
+
end # MaximumFlow
|
106
|
+
end # Graph
|
107
|
+
end # GRATR
|
data/lib/gratr/rdot.rb
ADDED
@@ -0,0 +1,332 @@
|
|
1
|
+
# rdot.rb
|
2
|
+
#
|
3
|
+
# $Id: rdot.rb,v 1.4 2005/03/26 15:06:36 wsdng Exp $
|
4
|
+
#
|
5
|
+
# This is a modified version of dot.rb from Dave Thomas's rdoc project. I [Horst Duchene]
|
6
|
+
# renamed it to rdot.rb to avoid collision with an installed rdoc/dot.
|
7
|
+
#
|
8
|
+
# It also supports undirected edges.
|
9
|
+
|
10
|
+
class Hash
|
11
|
+
def stringify_keys
|
12
|
+
inject({}) {|options, (key, value)| options[key.to_s] = value; options}
|
13
|
+
end
|
14
|
+
end unless Hash.respond_to? :stringify_keys
|
15
|
+
|
16
|
+
module DOT
|
17
|
+
|
18
|
+
# These glogal vars are used to make nice graph source.
|
19
|
+
|
20
|
+
$tab = ' '
|
21
|
+
$tab2 = $tab * 2
|
22
|
+
|
23
|
+
# if we don't like 4 spaces, we can change it any time
|
24
|
+
|
25
|
+
def change_tab (t)
|
26
|
+
$tab = t
|
27
|
+
$tab2 = t * 2
|
28
|
+
end
|
29
|
+
|
30
|
+
# options for node declaration
|
31
|
+
|
32
|
+
NODE_OPTS = [
|
33
|
+
# attributes due to
|
34
|
+
# http://www.graphviz.org/Documentation/dotguide.pdf
|
35
|
+
# March, 26, 2005
|
36
|
+
'bottomlabel', # auxiliary label for nodes of shape M*
|
37
|
+
'color', # default: black; node shape color
|
38
|
+
'comment', # any string (format-dependent)
|
39
|
+
'distortion', # default: 0.0; node distortion for shape=polygon
|
40
|
+
'fillcolor', # default: lightgrey/black; node fill color
|
41
|
+
'fixedsize', # default: false; label text has no affect on node size
|
42
|
+
'fontcolor', # default: black; type face color
|
43
|
+
'fontname', # default: Times-Roman; font family
|
44
|
+
'fontsize', #default: 14; point size of label
|
45
|
+
'group', # name of node�s group
|
46
|
+
'height', # default: .5; height in inches
|
47
|
+
'label', # default: node name; any string
|
48
|
+
'layer', # default: overlay range; all, id or id:id
|
49
|
+
'orientation', # dafault: 0.0; node rotation angle
|
50
|
+
'peripheries', # shape-dependent number of node boundaries
|
51
|
+
'regular', # default: false; force polygon to be regular
|
52
|
+
'shape', # default: ellipse; node shape; see Section 2.1 and Appendix E
|
53
|
+
'shapefile', # external EPSF or SVG custom shape file
|
54
|
+
'sides', # default: 4; number of sides for shape=polygon
|
55
|
+
'skew' , # default: 0.0; skewing of node for shape=polygon
|
56
|
+
'style', # graphics options, e.g. bold, dotted, filled; cf. Section 2.3
|
57
|
+
'toplabel', # auxiliary label for nodes of shape M*
|
58
|
+
'URL', # URL associated with node (format-dependent)
|
59
|
+
'width', # default: .75; width in inches
|
60
|
+
'z', #default: 0.0; z coordinate for VRML output
|
61
|
+
|
62
|
+
# maintained for backward compatibility or rdot internal
|
63
|
+
'bgcolor',
|
64
|
+
'rank'
|
65
|
+
]
|
66
|
+
|
67
|
+
# options for edge declaration
|
68
|
+
|
69
|
+
EDGE_OPTS = [
|
70
|
+
'arrowhead', # default: normal; style of arrowhead at head end
|
71
|
+
'arrowsize', # default: 1.0; scaling factor for arrowheads
|
72
|
+
'arrowtail', # default: normal; style of arrowhead at tail end
|
73
|
+
'color', # default: black; edge stroke color
|
74
|
+
'comment', # any string (format-dependent)
|
75
|
+
'constraint', # default: true use edge to affect node ranking
|
76
|
+
'decorate', # if set, draws a line connecting labels with their edges
|
77
|
+
'dir', # default: forward; forward, back, both, or none
|
78
|
+
'fontcolor', # default: black type face color
|
79
|
+
'fontname', # default: Times-Roman; font family
|
80
|
+
'fontsize', # default: 14; point size of label
|
81
|
+
'headlabel', # label placed near head of edge
|
82
|
+
'headport', # n,ne,e,se,s,sw,w,nw
|
83
|
+
'headURL', # URL attached to head label if output format is ismap
|
84
|
+
'label', # edge label
|
85
|
+
'labelangle', # default: -25.0; angle in degrees which head or tail label is rotated off edge
|
86
|
+
'labeldistance', # default: 1.0; scaling factor for distance of head or tail label from node
|
87
|
+
'labelfloat', # default: false; lessen constraints on edge label placement
|
88
|
+
'labelfontcolor', # default: black; type face color for head and tail labels
|
89
|
+
'labelfontname', # default: Times-Roman; font family for head and tail labels
|
90
|
+
'labelfontsize', # default: 14 point size for head and tail labels
|
91
|
+
'layer', # default: overlay range; all, id or id:id
|
92
|
+
'lhead', # name of cluster to use as head of edge
|
93
|
+
'ltail', # name of cluster to use as tail of edge
|
94
|
+
'minlen', # default: 1 minimum rank distance between head and tail
|
95
|
+
'samehead', # tag for head node; edge heads with the same tag are merged onto the same port
|
96
|
+
'sametail', # tag for tail node; edge tails with the same tag are merged onto the same port
|
97
|
+
'style', # graphics options, e.g. bold, dotted, filled; cf. Section 2.3
|
98
|
+
'taillabel', # label placed near tail of edge
|
99
|
+
'tailport', # n,ne,e,se,s,sw,w,nw
|
100
|
+
'tailURL', # URL attached to tail label if output format is ismap
|
101
|
+
'weight', # default: 1; integer cost of stretching an edge
|
102
|
+
|
103
|
+
# maintained for backward compatibility or rdot internal
|
104
|
+
'id'
|
105
|
+
]
|
106
|
+
|
107
|
+
# options for graph declaration
|
108
|
+
|
109
|
+
GRAPH_OPTS = [
|
110
|
+
'bgcolor',
|
111
|
+
'center', 'clusterrank', 'color', 'concentrate',
|
112
|
+
'fontcolor', 'fontname', 'fontsize',
|
113
|
+
'label', 'layerseq',
|
114
|
+
'margin', 'mclimit',
|
115
|
+
'nodesep', 'nslimit',
|
116
|
+
'ordering', 'orientation',
|
117
|
+
'page',
|
118
|
+
'rank', 'rankdir', 'ranksep', 'ratio',
|
119
|
+
'size'
|
120
|
+
]
|
121
|
+
|
122
|
+
# a root class for any element in dot notation
|
123
|
+
|
124
|
+
class DOTSimpleElement
|
125
|
+
|
126
|
+
attr_accessor :name
|
127
|
+
|
128
|
+
def initialize (params = {})
|
129
|
+
@label = params['name'] ? params['name'] : ''
|
130
|
+
end
|
131
|
+
|
132
|
+
def to_s
|
133
|
+
@name
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# an element that has options ( node, edge, or graph )
|
138
|
+
|
139
|
+
class DOTElement < DOTSimpleElement
|
140
|
+
|
141
|
+
# attr_reader :parent
|
142
|
+
attr_accessor :name, :options
|
143
|
+
|
144
|
+
def initialize (params = {}, option_list = [])
|
145
|
+
super(params)
|
146
|
+
@name = params['name'] ? params['name'] : nil
|
147
|
+
@parent = params['parent'] ? params['parent'] : nil
|
148
|
+
@options = {}
|
149
|
+
p = params.stringify_keys
|
150
|
+
option_list.each {|i| @options[i] = p[i] if p[i] }
|
151
|
+
@options['label'] ||= @name if @name != 'node'
|
152
|
+
end
|
153
|
+
|
154
|
+
def each_option
|
155
|
+
@options.each{ |i| yield i }
|
156
|
+
end
|
157
|
+
|
158
|
+
def each_option_pair
|
159
|
+
@options.each_pair{ |key, val| yield key, val }
|
160
|
+
end
|
161
|
+
|
162
|
+
#def parent=( thing )
|
163
|
+
# @parent.delete( self ) if defined?( @parent ) and @parent
|
164
|
+
# @parent = thing
|
165
|
+
#end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
# This is used when we build nodes that have shape=record
|
171
|
+
# ports don't have options :)
|
172
|
+
|
173
|
+
class DOTPort < DOTSimpleElement
|
174
|
+
|
175
|
+
attr_accessor :label
|
176
|
+
|
177
|
+
def initialize (params = {})
|
178
|
+
super(params)
|
179
|
+
@name = params['label'] ? params['label'] : ''
|
180
|
+
end
|
181
|
+
|
182
|
+
def to_s
|
183
|
+
( @name && @name != "" ? "<#{@name}>" : "" ) + "#{@label}"
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# node element
|
188
|
+
|
189
|
+
class DOTNode < DOTElement
|
190
|
+
|
191
|
+
@ports
|
192
|
+
|
193
|
+
def initialize (params = {}, option_list = NODE_OPTS)
|
194
|
+
super(params, option_list)
|
195
|
+
@ports = params['ports'] ? params['ports'] : []
|
196
|
+
end
|
197
|
+
|
198
|
+
def each_port
|
199
|
+
@ports.each { |i| yield i }
|
200
|
+
end
|
201
|
+
|
202
|
+
def << (thing)
|
203
|
+
@ports << thing
|
204
|
+
end
|
205
|
+
|
206
|
+
def push (thing)
|
207
|
+
@ports.push(thing)
|
208
|
+
end
|
209
|
+
|
210
|
+
def pop
|
211
|
+
@ports.pop
|
212
|
+
end
|
213
|
+
|
214
|
+
def to_s (t = '')
|
215
|
+
|
216
|
+
# This code is totally incomprehensible; it needs to be replaced!
|
217
|
+
|
218
|
+
label = @options['shape'] != 'record' && @ports.length == 0 ?
|
219
|
+
@options['label'] ?
|
220
|
+
t + $tab + "label = \"#{@options['label']}\"\n" :
|
221
|
+
'' :
|
222
|
+
t + $tab + 'label = "' + " \\\n" +
|
223
|
+
t + $tab2 + "#{@options['label']}| \\\n" +
|
224
|
+
@ports.collect{ |i|
|
225
|
+
t + $tab2 + i.to_s
|
226
|
+
}.join( "| \\\n" ) + " \\\n" +
|
227
|
+
t + $tab + '"' + "\n"
|
228
|
+
|
229
|
+
t + "#{@name} [\n" +
|
230
|
+
@options.to_a.collect{ |i|
|
231
|
+
i[1] && i[0] != 'label' ?
|
232
|
+
t + $tab + "#{i[0]} = #{i[1]}" : nil
|
233
|
+
}.compact.join( ",\n" ) + ( label != '' ? ",\n" : "\n" ) +
|
234
|
+
label +
|
235
|
+
t + "]\n"
|
236
|
+
end
|
237
|
+
|
238
|
+
end # class DOTNode
|
239
|
+
|
240
|
+
# A subgraph element is the same to graph, but has another header in dot
|
241
|
+
# notation.
|
242
|
+
|
243
|
+
class DOTSubgraph < DOTElement
|
244
|
+
|
245
|
+
@nodes
|
246
|
+
@dot_string
|
247
|
+
|
248
|
+
def initialize (params = {}, option_list = GRAPH_OPTS)
|
249
|
+
super(params, option_list)
|
250
|
+
@nodes = params['nodes'] ? params['nodes'] : []
|
251
|
+
@dot_string = 'graph'
|
252
|
+
end
|
253
|
+
|
254
|
+
def each_node
|
255
|
+
@nodes.each{ |i| yield i }
|
256
|
+
end
|
257
|
+
|
258
|
+
def << (thing)
|
259
|
+
@nodes << thing
|
260
|
+
end
|
261
|
+
|
262
|
+
def push (thing)
|
263
|
+
@nodes.push( thing )
|
264
|
+
end
|
265
|
+
|
266
|
+
def pop
|
267
|
+
@nodes.pop
|
268
|
+
end
|
269
|
+
|
270
|
+
def to_s (t = '')
|
271
|
+
hdr = t + "#{@dot_string} #{@name} {\n"
|
272
|
+
|
273
|
+
options = @options.to_a.collect{ |name, val|
|
274
|
+
val && name != 'label' ?
|
275
|
+
t + $tab + "#{name} = #{val}" :
|
276
|
+
name ? t + $tab + "#{name} = \"#{val}\"" : nil
|
277
|
+
}.compact.join( "\n" ) + "\n"
|
278
|
+
|
279
|
+
nodes = @nodes.collect{ |i|
|
280
|
+
i.to_s( t + $tab )
|
281
|
+
}.join( "\n" ) + "\n"
|
282
|
+
hdr + options + nodes + t + "}\n"
|
283
|
+
end
|
284
|
+
|
285
|
+
end # class DOTSubgraph
|
286
|
+
|
287
|
+
# This is a graph.
|
288
|
+
|
289
|
+
class DOTDigraph < DOTSubgraph
|
290
|
+
|
291
|
+
def initialize (params = {}, option_list = GRAPH_OPTS)
|
292
|
+
super(params, option_list)
|
293
|
+
@dot_string = 'digraph'
|
294
|
+
end
|
295
|
+
|
296
|
+
end # class DOTDigraph
|
297
|
+
|
298
|
+
# This is an edge.
|
299
|
+
|
300
|
+
class DOTArc < DOTElement
|
301
|
+
|
302
|
+
attr_accessor :from, :to
|
303
|
+
|
304
|
+
def initialize (params = {}, option_list = EDGE_OPTS)
|
305
|
+
super(params, option_list)
|
306
|
+
@from = params['from'] ? params['from'] : nil
|
307
|
+
@to = params['to'] ? params['to'] : nil
|
308
|
+
end
|
309
|
+
|
310
|
+
def edge_link
|
311
|
+
'--'
|
312
|
+
end
|
313
|
+
|
314
|
+
def to_s (t = '')
|
315
|
+
t + "#{@from} #{edge_link} #{to} [\n" +
|
316
|
+
@options.to_a.collect{ |i|
|
317
|
+
i[1] && i[0] != 'label' ?
|
318
|
+
t + $tab + "#{i[0]} = #{i[1]}" :
|
319
|
+
i[1] ? t + $tab + "#{i[0]} = \"#{i[1]}\"" : nil
|
320
|
+
}.compact.join( "\n" ) + "\n" + t + "]\n"
|
321
|
+
end
|
322
|
+
|
323
|
+
end # class DOTArc
|
324
|
+
|
325
|
+
class DOTDirectedArc < DOTArc
|
326
|
+
|
327
|
+
def edge_link
|
328
|
+
'->'
|
329
|
+
end
|
330
|
+
|
331
|
+
end # class DOTDirectedArc
|
332
|
+
end # module DOT
|