gratr 0.4.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 (45) hide show
  1. data/Grater.xcf +0 -0
  2. data/README +328 -0
  3. data/Rakefile +220 -0
  4. data/examples/graph_self.rb +54 -0
  5. data/examples/module_graph.jpg +0 -0
  6. data/examples/module_graph.rb +12 -0
  7. data/examples/self_graph.jpg +0 -0
  8. data/examples/visualize.jpg +0 -0
  9. data/examples/visualize.rb +8 -0
  10. data/install.rb +49 -0
  11. data/lib/gratr.rb +33 -0
  12. data/lib/gratr/adjacency_graph.rb +230 -0
  13. data/lib/gratr/base.rb +34 -0
  14. data/lib/gratr/biconnected.rb +116 -0
  15. data/lib/gratr/chinese_postman.rb +123 -0
  16. data/lib/gratr/common.rb +73 -0
  17. data/lib/gratr/comparability.rb +92 -0
  18. data/lib/gratr/digraph.rb +113 -0
  19. data/lib/gratr/digraph_distance.rb +185 -0
  20. data/lib/gratr/dot.rb +90 -0
  21. data/lib/gratr/edge.rb +145 -0
  22. data/lib/gratr/graph.rb +315 -0
  23. data/lib/gratr/graph_api.rb +82 -0
  24. data/lib/gratr/import.rb +44 -0
  25. data/lib/gratr/labels.rb +103 -0
  26. data/lib/gratr/maximum_flow.rb +107 -0
  27. data/lib/gratr/rdot.rb +326 -0
  28. data/lib/gratr/search.rb +409 -0
  29. data/lib/gratr/strong_components.rb +127 -0
  30. data/lib/gratr/undirected_graph.rb +153 -0
  31. data/tests/TestBiconnected.rb +53 -0
  32. data/tests/TestChinesePostman.rb +53 -0
  33. data/tests/TestComplement.rb +54 -0
  34. data/tests/TestDigraph.rb +333 -0
  35. data/tests/TestDigraphDistance.rb +138 -0
  36. data/tests/TestEdge.rb +171 -0
  37. data/tests/TestInspection.rb +57 -0
  38. data/tests/TestMultiEdge.rb +57 -0
  39. data/tests/TestNeighborhood.rb +64 -0
  40. data/tests/TestProperties.rb +160 -0
  41. data/tests/TestSearch.rb +257 -0
  42. data/tests/TestStrongComponents.rb +85 -0
  43. data/tests/TestTriagulated.rb +137 -0
  44. data/tests/TestUndirectedGraph.rb +219 -0
  45. metadata +92 -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
@@ -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
@@ -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
@@ -0,0 +1,326 @@
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
+ module DOT
11
+
12
+ # These glogal vars are used to make nice graph source.
13
+
14
+ $tab = ' '
15
+ $tab2 = $tab * 2
16
+
17
+ # if we don't like 4 spaces, we can change it any time
18
+
19
+ def change_tab (t)
20
+ $tab = t
21
+ $tab2 = t * 2
22
+ end
23
+
24
+ # options for node declaration
25
+
26
+ NODE_OPTS = [
27
+ # attributes due to
28
+ # http://www.graphviz.org/Documentation/dotguide.pdf
29
+ # March, 26, 2005
30
+ 'bottomlabel', # auxiliary label for nodes of shape M*
31
+ 'color', # default: black; node shape color
32
+ 'comment', # any string (format-dependent)
33
+ 'distortion', # default: 0.0; node distortion for shape=polygon
34
+ 'fillcolor', # default: lightgrey/black; node fill color
35
+ 'fixedsize', # default: false; label text has no affect on node size
36
+ 'fontcolor', # default: black; type face color
37
+ 'fontname', # default: Times-Roman; font family
38
+ 'fontsize', #default: 14; point size of label
39
+ 'group', # name of node�s group
40
+ 'height', # default: .5; height in inches
41
+ 'label', # default: node name; any string
42
+ 'layer', # default: overlay range; all, id or id:id
43
+ 'orientation', # dafault: 0.0; node rotation angle
44
+ 'peripheries', # shape-dependent number of node boundaries
45
+ 'regular', # default: false; force polygon to be regular
46
+ 'shape', # default: ellipse; node shape; see Section 2.1 and Appendix E
47
+ 'shapefile', # external EPSF or SVG custom shape file
48
+ 'sides', # default: 4; number of sides for shape=polygon
49
+ 'skew' , # default: 0.0; skewing of node for shape=polygon
50
+ 'style', # graphics options, e.g. bold, dotted, filled; cf. Section 2.3
51
+ 'toplabel', # auxiliary label for nodes of shape M*
52
+ 'URL', # URL associated with node (format-dependent)
53
+ 'width', # default: .75; width in inches
54
+ 'z', #default: 0.0; z coordinate for VRML output
55
+
56
+ # maintained for backward compatibility or rdot internal
57
+ 'bgcolor',
58
+ 'rank'
59
+ ]
60
+
61
+ # options for edge declaration
62
+
63
+ EDGE_OPTS = [
64
+ 'arrowhead', # default: normal; style of arrowhead at head end
65
+ 'arrowsize', # default: 1.0; scaling factor for arrowheads
66
+ 'arrowtail', # default: normal; style of arrowhead at tail end
67
+ 'color', # default: black; edge stroke color
68
+ 'comment', # any string (format-dependent)
69
+ 'constraint', # default: true use edge to affect node ranking
70
+ 'decorate', # if set, draws a line connecting labels with their edges
71
+ 'dir', # default: forward; forward, back, both, or none
72
+ 'fontcolor', # default: black type face color
73
+ 'fontname', # default: Times-Roman; font family
74
+ 'fontsize', # default: 14; point size of label
75
+ 'headlabel', # label placed near head of edge
76
+ 'headport', # n,ne,e,se,s,sw,w,nw
77
+ 'headURL', # URL attached to head label if output format is ismap
78
+ 'label', # edge label
79
+ 'labelangle', # default: -25.0; angle in degrees which head or tail label is rotated off edge
80
+ 'labeldistance', # default: 1.0; scaling factor for distance of head or tail label from node
81
+ 'labelfloat', # default: false; lessen constraints on edge label placement
82
+ 'labelfontcolor', # default: black; type face color for head and tail labels
83
+ 'labelfontname', # default: Times-Roman; font family for head and tail labels
84
+ 'labelfontsize', # default: 14 point size for head and tail labels
85
+ 'layer', # default: overlay range; all, id or id:id
86
+ 'lhead', # name of cluster to use as head of edge
87
+ 'ltail', # name of cluster to use as tail of edge
88
+ 'minlen', # default: 1 minimum rank distance between head and tail
89
+ 'samehead', # tag for head node; edge heads with the same tag are merged onto the same port
90
+ 'sametail', # tag for tail node; edge tails with the same tag are merged onto the same port
91
+ 'style', # graphics options, e.g. bold, dotted, filled; cf. Section 2.3
92
+ 'taillabel', # label placed near tail of edge
93
+ 'tailport', # n,ne,e,se,s,sw,w,nw
94
+ 'tailURL', # URL attached to tail label if output format is ismap
95
+ 'weight', # default: 1; integer cost of stretching an edge
96
+
97
+ # maintained for backward compatibility or rdot internal
98
+ 'id'
99
+ ]
100
+
101
+ # options for graph declaration
102
+
103
+ GRAPH_OPTS = [
104
+ 'bgcolor',
105
+ 'center', 'clusterrank', 'color', 'concentrate',
106
+ 'fontcolor', 'fontname', 'fontsize',
107
+ 'label', 'layerseq',
108
+ 'margin', 'mclimit',
109
+ 'nodesep', 'nslimit',
110
+ 'ordering', 'orientation',
111
+ 'page',
112
+ 'rank', 'rankdir', 'ranksep', 'ratio',
113
+ 'size'
114
+ ]
115
+
116
+ # a root class for any element in dot notation
117
+
118
+ class DOTSimpleElement
119
+
120
+ attr_accessor :name
121
+
122
+ def initialize (params = {})
123
+ @label = params['name'] ? params['name'] : ''
124
+ end
125
+
126
+ def to_s
127
+ @name
128
+ end
129
+ end
130
+
131
+ # an element that has options ( node, edge, or graph )
132
+
133
+ class DOTElement < DOTSimpleElement
134
+
135
+ # attr_reader :parent
136
+ attr_accessor :name, :options
137
+
138
+ def initialize (params = {}, option_list = [])
139
+ super(params)
140
+ @name = params['name'] ? params['name'] : nil
141
+ @parent = params['parent'] ? params['parent'] : nil
142
+ @options = {}
143
+ p = params.stringify_keys
144
+ option_list.each {|i| @options[i] = p[i] if p[i] }
145
+ @options['label'] ||= @name if @name != 'node'
146
+ end
147
+
148
+ def each_option
149
+ @options.each{ |i| yield i }
150
+ end
151
+
152
+ def each_option_pair
153
+ @options.each_pair{ |key, val| yield key, val }
154
+ end
155
+
156
+ #def parent=( thing )
157
+ # @parent.delete( self ) if defined?( @parent ) and @parent
158
+ # @parent = thing
159
+ #end
160
+
161
+ end
162
+
163
+
164
+ # This is used when we build nodes that have shape=record
165
+ # ports don't have options :)
166
+
167
+ class DOTPort < DOTSimpleElement
168
+
169
+ attr_accessor :label
170
+
171
+ def initialize (params = {})
172
+ super(params)
173
+ @name = params['label'] ? params['label'] : ''
174
+ end
175
+
176
+ def to_s
177
+ ( @name && @name != "" ? "<#{@name}>" : "" ) + "#{@label}"
178
+ end
179
+ end
180
+
181
+ # node element
182
+
183
+ class DOTNode < DOTElement
184
+
185
+ @ports
186
+
187
+ def initialize (params = {}, option_list = NODE_OPTS)
188
+ super(params, option_list)
189
+ @ports = params['ports'] ? params['ports'] : []
190
+ end
191
+
192
+ def each_port
193
+ @ports.each { |i| yield i }
194
+ end
195
+
196
+ def << (thing)
197
+ @ports << thing
198
+ end
199
+
200
+ def push (thing)
201
+ @ports.push(thing)
202
+ end
203
+
204
+ def pop
205
+ @ports.pop
206
+ end
207
+
208
+ def to_s (t = '')
209
+
210
+ # This code is totally incomprehensible; it needs to be replaced!
211
+
212
+ label = @options['shape'] != 'record' && @ports.length == 0 ?
213
+ @options['label'] ?
214
+ t + $tab + "label = \"#{@options['label']}\"\n" :
215
+ '' :
216
+ t + $tab + 'label = "' + " \\\n" +
217
+ t + $tab2 + "#{@options['label']}| \\\n" +
218
+ @ports.collect{ |i|
219
+ t + $tab2 + i.to_s
220
+ }.join( "| \\\n" ) + " \\\n" +
221
+ t + $tab + '"' + "\n"
222
+
223
+ t + "#{@name} [\n" +
224
+ @options.to_a.collect{ |i|
225
+ i[1] && i[0] != 'label' ?
226
+ t + $tab + "#{i[0]} = #{i[1]}" : nil
227
+ }.compact.join( ",\n" ) + ( label != '' ? ",\n" : "\n" ) +
228
+ label +
229
+ t + "]\n"
230
+ end
231
+
232
+ end # class DOTNode
233
+
234
+ # A subgraph element is the same to graph, but has another header in dot
235
+ # notation.
236
+
237
+ class DOTSubgraph < DOTElement
238
+
239
+ @nodes
240
+ @dot_string
241
+
242
+ def initialize (params = {}, option_list = GRAPH_OPTS)
243
+ super(params, option_list)
244
+ @nodes = params['nodes'] ? params['nodes'] : []
245
+ @dot_string = 'graph'
246
+ end
247
+
248
+ def each_node
249
+ @nodes.each{ |i| yield i }
250
+ end
251
+
252
+ def << (thing)
253
+ @nodes << thing
254
+ end
255
+
256
+ def push (thing)
257
+ @nodes.push( thing )
258
+ end
259
+
260
+ def pop
261
+ @nodes.pop
262
+ end
263
+
264
+ def to_s (t = '')
265
+ hdr = t + "#{@dot_string} #{@name} {\n"
266
+
267
+ options = @options.to_a.collect{ |name, val|
268
+ val && name != 'label' ?
269
+ t + $tab + "#{name} = #{val}" :
270
+ name ? t + $tab + "#{name} = \"#{val}\"" : nil
271
+ }.compact.join( "\n" ) + "\n"
272
+
273
+ nodes = @nodes.collect{ |i|
274
+ i.to_s( t + $tab )
275
+ }.join( "\n" ) + "\n"
276
+ hdr + options + nodes + t + "}\n"
277
+ end
278
+
279
+ end # class DOTSubgraph
280
+
281
+ # This is a graph.
282
+
283
+ class DOTDigraph < DOTSubgraph
284
+
285
+ def initialize (params = {}, option_list = GRAPH_OPTS)
286
+ super(params, option_list)
287
+ @dot_string = 'digraph'
288
+ end
289
+
290
+ end # class DOTDigraph
291
+
292
+ # This is an edge.
293
+
294
+ class DOTArc < DOTElement
295
+
296
+ attr_accessor :from, :to
297
+
298
+ def initialize (params = {}, option_list = EDGE_OPTS)
299
+ super(params, option_list)
300
+ @from = params['from'] ? params['from'] : nil
301
+ @to = params['to'] ? params['to'] : nil
302
+ end
303
+
304
+ def edge_link
305
+ '--'
306
+ end
307
+
308
+ def to_s (t = '')
309
+ t + "#{@from} #{edge_link} #{to} [\n" +
310
+ @options.to_a.collect{ |i|
311
+ i[1] && i[0] != 'label' ?
312
+ t + $tab + "#{i[0]} = #{i[1]}" :
313
+ i[1] ? t + $tab + "#{i[0]} = \"#{i[1]}\"" : nil
314
+ }.compact.join( "\n" ) + "\n" + t + "]\n"
315
+ end
316
+
317
+ end # class DOTArc
318
+
319
+ class DOTDirectedArc < DOTArc
320
+
321
+ def edge_link
322
+ '->'
323
+ end
324
+
325
+ end # class DOTDirectedArc
326
+ end # module DOT