gratr19 0.4.4

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 (54) hide show
  1. data/README +335 -0
  2. data/examples/graph_self.rb +54 -0
  3. data/examples/module_graph.jpg +0 -0
  4. data/examples/module_graph.rb +12 -0
  5. data/examples/self_graph.jpg +0 -0
  6. data/examples/visualize.jpg +0 -0
  7. data/examples/visualize.rb +8 -0
  8. data/install.rb +49 -0
  9. data/lib/gratr.rb +42 -0
  10. data/lib/gratr/adjacency_graph.rb +230 -0
  11. data/lib/gratr/base.rb +34 -0
  12. data/lib/gratr/biconnected.rb +116 -0
  13. data/lib/gratr/chinese_postman.rb +123 -0
  14. data/lib/gratr/common.rb +74 -0
  15. data/lib/gratr/comparability.rb +92 -0
  16. data/lib/gratr/digraph.rb +115 -0
  17. data/lib/gratr/digraph_distance.rb +185 -0
  18. data/lib/gratr/dot.rb +90 -0
  19. data/lib/gratr/edge.rb +145 -0
  20. data/lib/gratr/graph.rb +314 -0
  21. data/lib/gratr/graph_api.rb +82 -0
  22. data/lib/gratr/import.rb +44 -0
  23. data/lib/gratr/labels.rb +103 -0
  24. data/lib/gratr/maximum_flow.rb +107 -0
  25. data/lib/gratr/rdot.rb +332 -0
  26. data/lib/gratr/search.rb +422 -0
  27. data/lib/gratr/strong_components.rb +127 -0
  28. data/lib/gratr/undirected_graph.rb +153 -0
  29. data/lib/gratr/version.rb +6 -0
  30. data/lib/priority-queue/benchmark/dijkstra.rb +171 -0
  31. data/lib/priority-queue/compare_comments.rb +49 -0
  32. data/lib/priority-queue/ext/priority_queue/CPriorityQueue/extconf.rb +2 -0
  33. data/lib/priority-queue/lib/priority_queue.rb +14 -0
  34. data/lib/priority-queue/lib/priority_queue/c_priority_queue.rb +1 -0
  35. data/lib/priority-queue/lib/priority_queue/poor_priority_queue.rb +46 -0
  36. data/lib/priority-queue/lib/priority_queue/ruby_priority_queue.rb +525 -0
  37. data/lib/priority-queue/setup.rb +1551 -0
  38. data/lib/priority-queue/test/priority_queue_test.rb +371 -0
  39. data/tests/TestBiconnected.rb +53 -0
  40. data/tests/TestChinesePostman.rb +53 -0
  41. data/tests/TestComplement.rb +54 -0
  42. data/tests/TestDigraph.rb +333 -0
  43. data/tests/TestDigraphDistance.rb +138 -0
  44. data/tests/TestDot.rb +75 -0
  45. data/tests/TestEdge.rb +171 -0
  46. data/tests/TestInspection.rb +57 -0
  47. data/tests/TestMultiEdge.rb +57 -0
  48. data/tests/TestNeighborhood.rb +64 -0
  49. data/tests/TestProperties.rb +160 -0
  50. data/tests/TestSearch.rb +277 -0
  51. data/tests/TestStrongComponents.rb +85 -0
  52. data/tests/TestTriagulated.rb +137 -0
  53. data/tests/TestUndirectedGraph.rb +219 -0
  54. 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
@@ -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,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