gratr 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
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,160 @@
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 'test/unit'
30
+ require 'gratr/import'
31
+ require 'gratr/dot'
32
+
33
+ # This test runs the classes from Appendix F in
34
+ # _Algorithmic_Graph_Theory_and_Perfect_Graphs,
35
+ # by Martin Charles Golumbic
36
+ class TestProperties < Test::Unit::TestCase # :nodoc:
37
+
38
+ def test_g1
39
+ g1 = UndirectedGraph[ :a,:b, :a,:d, :a,:e, :a,:i, :a,:g, :a,:h,
40
+ :b,:c, :b,:f,
41
+ :c,:d, :c,:h,
42
+ :d,:h, :d,:e,
43
+ :e,:f,
44
+ :f,:g, :f,:h, :f,:i,
45
+ :h,:i ]
46
+
47
+ assert !g1.triangulated?
48
+ assert !g1.complement.triangulated? # Disagrees with Golumbic!
49
+ assert !g1.comparability?
50
+ assert !g1.complement.comparability?
51
+ assert !g1.interval?
52
+ assert !g1.complement.interval?
53
+ assert !g1.permutation?
54
+ assert !g1.split?
55
+
56
+ # g1.write_to_graphic_file('jpg','g1')
57
+ # g1.complement.write_to_graphic_file('jpg','g1_complement')
58
+ end
59
+
60
+ def test_g2
61
+ g2 = UndirectedGraph[ :a,:b, :a,:e,
62
+ :b,:c, :b,:e, :b,:f,
63
+ :c,:d, :c,:f, :c,:g,
64
+ :d,:g,
65
+ :e,:f,
66
+ :f,:g]
67
+
68
+ assert g2.triangulated?
69
+ assert !g2.complement.triangulated?
70
+ assert !g2.comparability?
71
+ assert g2.complement.comparability?
72
+ assert g2.interval?
73
+ assert !g2.complement.interval?
74
+ assert !g2.permutation?
75
+ assert !g2.split?
76
+ end
77
+
78
+ def test_g3
79
+ g3 = UndirectedGraph[ :a,:c,
80
+ :b,:e,
81
+ :c,:d, :c,:f,
82
+ :d,:f, :d,:g, :d,:e,
83
+ :e,:g,
84
+ :f,:g ]
85
+ assert g3.triangulated?
86
+ assert !g3.complement.triangulated?
87
+ assert !g3.comparability?
88
+ assert g3.complement.comparability?
89
+ assert g3.interval?
90
+ assert !g3.complement.interval?
91
+ assert !g3.permutation?
92
+ assert !g3.split?
93
+ end
94
+
95
+ def test_g4
96
+ g4 = UndirectedGraph[ :a,:b,
97
+ :b,:c,
98
+ :c,:d, :c,:e,
99
+ :d,:f,
100
+ :e,:g]
101
+ assert g4.triangulated?
102
+ assert !g4.complement.triangulated?
103
+ assert g4.comparability?
104
+ assert !g4.complement.comparability?
105
+ assert !g4.interval?
106
+ assert !g4.complement.interval?
107
+ assert !g4.permutation?
108
+ assert !g4.split?
109
+ end
110
+
111
+ def test_g5
112
+ g5 = UndirectedGraph[ :a,:b, :a,:c,
113
+ :b,:c, :b,:d, :b,:f, :b,:g,
114
+ :c,:e, :c,:f, :c,:g,
115
+ :d,:f,
116
+ :e,:g,
117
+ :f,:g]
118
+ assert g5.triangulated?
119
+ assert g5.complement.triangulated?
120
+ assert g5.comparability?
121
+ assert !g5.complement.comparability?
122
+ assert !g5.interval?
123
+ assert g5.complement.interval?
124
+ assert !g5.permutation?
125
+ assert g5.split?
126
+ end
127
+
128
+ def test_g6
129
+ g6 = UndirectedGraph[ :a,:c, :a,:d,
130
+ :b,:c,
131
+ :c,:f,
132
+ :d,:e, :d,:f]
133
+ assert !g6.triangulated?
134
+ assert !g6.complement.triangulated?
135
+ assert g6.comparability?
136
+ assert g6.complement.comparability?
137
+ assert !g6.interval?
138
+ assert !g6.complement.interval?
139
+ assert g6.permutation?
140
+ assert !g6.split?
141
+ end
142
+
143
+ def test_g7
144
+ g7 = UndirectedGraph[ :a,:b, :a,:c,
145
+ :b,:c, :b,:d, :b,:e,
146
+ :c,:e, :c,:f,
147
+ :d,:e,
148
+ :e,:f]
149
+ assert g7.triangulated?
150
+ assert g7.complement.triangulated?
151
+ assert !g7.comparability?
152
+ assert !g7.complement.comparability?
153
+ assert !g7.interval?
154
+ assert !g7.complement.interval?
155
+ assert !g7.permutation?
156
+ assert g7.split?
157
+
158
+ end
159
+
160
+ end
@@ -0,0 +1,257 @@
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
+
30
+ require 'test/unit'
31
+ require 'gratr/import'
32
+
33
+ class TestSearch < Test::Unit::TestCase # :nodoc:
34
+
35
+ def setup
36
+ @directed = Digraph[1,2, 2,3, 2,4, 4,5, 1,6, 6,4]
37
+ @undirected = UndirectedGraph[1,2, 2,3, 2,4, 4,5, 1,6]
38
+ @tree = Digraph[ 1,2, 1,3, 1,4, 2,5, 2,4, 2,6, 6,7, 23,24 ]
39
+ end
40
+
41
+ # "Algorithmic Graph Theory and Perfect Graphs", Martin Charles
42
+ # Golumbic, 1980, Academic Press, page 38, Figure 2.6
43
+ def assign_dfsnumber_ancestry(graph, dfsnumber, father, start)
44
+ i = 0
45
+ dfsnumber.clear
46
+ father.clear
47
+ ev = Proc.new {|v| dfsnumber[v] = (i+=1) }
48
+ te = Proc.new {|e| father[e.target] = e.source }
49
+ graph.dfs({:enter_vertex => ev, :tree_edge => te, :start => start})
50
+ end
51
+
52
+ # Is v an ancestor of u
53
+ def ancestor?(father, u, v)
54
+ i = 1
55
+ while v
56
+ return i if father[v] == u
57
+ v = father[v]
58
+ i += 1
59
+ end; nil
60
+ end
61
+
62
+ # Is there any relationship?
63
+ def related?(father,u,v) ancestor?(father,u,v) or ancestor?(father,v,u); end
64
+
65
+ # "Algorithmic Graph Theory and Perfect Graphs", Martin Charles
66
+ # Golumbic, 1980, Academic Press, page 39, Propery (D1) and (D2) of
67
+ # depth first search
68
+ def test_dfs_properties
69
+ dfs = {}
70
+ father = {}
71
+ @directed.each do |vertex|
72
+ assign_dfsnumber_ancestry(@directed, dfs, father, vertex)
73
+ # Property (D1)
74
+ father.keys.each {|v| assert(dfs[father[v]] < dfs[v])}
75
+ # Property (D2)
76
+ # FIXME: Huh? Doesn't work
77
+ #@directed.edges.each {|e| assert(related?(father, e.source, e.target))}
78
+ #@directed.edges.each {|e| assert(dfs[e.source] < dfs[e.target])}
79
+ end
80
+ assert_equal 6, @directed.dfs.size
81
+ assert_equal @directed.vertices.sort, @directed.dfs.sort
82
+ end
83
+
84
+ # "Algorithmic Graph Theory and Perfect Graphs", Martin Charles
85
+ # Golumbic, 1980, Academic Press, page 40, Figure 2.7
86
+ def assign_bfsnumber_ancestry(graph, bfsnum, level, father, start)
87
+ i = 0
88
+ bfsnum.clear
89
+ level.clear
90
+ father.clear
91
+ rt = Proc.new {|v| level[v] = 0 }
92
+ ev = Proc.new {|v| bfsnum[v]=(i+=1);level[v]=(level[father[v]]+1) if father[v]}
93
+ te = Proc.new {|e| father[e.target] = e.source }
94
+ graph.dfs({:enter_vertex => ev, :tree_edge => te,
95
+ :root_vertex => rt, :start => start})
96
+ end
97
+
98
+ # "Algorithmic Graph Theory and Perfect Graphs", Martin Charles
99
+ # Golumbic, 1980, Academic Press, page 40, Propery (B1), (B2) and (B3) of
100
+ # breadth first search
101
+ def test_bfs_properties
102
+ level = {}
103
+ father = {}
104
+ bfs = {}
105
+ @directed.each do |vertex|
106
+ assign_bfsnumber_ancestry(@directed, bfs, level, father, vertex)
107
+ # Property (B1)
108
+ father.keys.each {|v| assert(bfs[father[v]] < bfs[v])}
109
+ # Property (B2)
110
+ @directed.edges.each {|e| assert((level[e.source]-level[e.target]).abs<2)}
111
+ # Property (B3)
112
+ # FIXME: How can one test this?
113
+ #@directed.vertex.each {|v| assert((level[e.source]-level[e.target]).abs<2)}
114
+ end
115
+ assert_equal 6, @directed.dfs.size
116
+ assert_equal @directed.vertices.sort, @directed.dfs.sort
117
+ end
118
+
119
+ def test_cyclic
120
+ assert @directed.acyclic?
121
+ assert @undirected.acyclic?
122
+ assert !@directed.cyclic?
123
+ assert !@undirected.cyclic?
124
+ @undirected.add_edge!(4,6)
125
+ @directed.add_edge!(3,1)
126
+ assert !@directed.acyclic?
127
+ assert !@undirected.acyclic?
128
+ assert @directed.cyclic?
129
+ assert @undirected.cyclic?
130
+
131
+ # Test empty graph
132
+ x = Digraph.new
133
+ assert !x.cyclic?
134
+ assert x.acyclic?
135
+ end
136
+
137
+ def test_astar
138
+ # Graph from "Artificial Intelligence: A Modern Approach" by Stuart
139
+ # Russell ande Peter Norvig, Prentice-Hall 2nd Edition, pg 63
140
+ romania = UndirectedGraph.new.
141
+ add_edge!('Oradea', 'Zerind', 71).
142
+ add_edge!('Oradea', 'Sibiu', 151).
143
+ add_edge!('Zerind', 'Arad', 75).
144
+ add_edge!('Arad', 'Sibiu', 99).
145
+ add_edge!('Arad', 'Timisoara', 138).
146
+ add_edge!('Timisoara', 'Lugoj', 111).
147
+ add_edge!('Lugoj', 'Mehadia', 70).
148
+ add_edge!('Mehadia', 'Dobreta', 75).
149
+ add_edge!('Dobreta', 'Craiova', 120).
150
+ add_edge!('Sibiu', 'Fagaras', 99).
151
+ add_edge!('Fagaras', 'Bucharest', 211).
152
+ add_edge!('Sibiu', 'Rimnicu Vilcea', 80).
153
+ add_edge!('Rimnicu Vilcea', 'Craiova', 146).
154
+ add_edge!('Rimnicu Vilcea', 'Pitesti', 97).
155
+ add_edge!('Craiova', 'Pitesti', 138).
156
+ add_edge!('Pitesti', 'Bucharest', 101).
157
+ add_edge!('Bucharest', 'Giurgin', 90).
158
+ add_edge!('Bucharest', 'Urzieni', 85).
159
+ add_edge!('Urzieni', 'Hirsova', 98).
160
+ add_edge!('Urzieni', 'Vaslui', 142).
161
+ add_edge!('Hirsova', 'Eforie', 86).
162
+ add_edge!('Vaslui', 'Iasi', 92).
163
+ add_edge!('Iasi', 'Neamt', 87)
164
+
165
+ # Heuristic from "Artificial Intelligence: A Modern Approach" by Stuart
166
+ # Russell ande Peter Norvig, Prentice-Hall 2nd Edition, pg 95
167
+ straight_line_to_Bucharest =
168
+ {
169
+ 'Arad' => 366,
170
+ 'Bucharest' => 0,
171
+ 'Craiova' => 160,
172
+ 'Dobreta' => 242,
173
+ 'Eforie' => 161,
174
+ 'Fagaras' => 176,
175
+ 'Giurgiu' => 77,
176
+ 'Hirsova' => 151,
177
+ 'Iasi' => 226,
178
+ 'Lugoj' => 244,
179
+ 'Mehadia' => 241,
180
+ 'Neamt' => 234,
181
+ 'Oradea' => 380,
182
+ 'Pitesti' => 100,
183
+ 'Rimnicu Vilcea' => 193,
184
+ 'Sibiu' => 253,
185
+ 'Timisoara' => 329,
186
+ 'Urziceni' => 80,
187
+ 'Vaslui' => 199,
188
+ 'Zerind' => 374
189
+ }
190
+
191
+ # Heuristic is distance as crow flies, always under estimates costs.
192
+ h = Proc.new {|v| straight_line_to_Bucharest[v]}
193
+
194
+ list = []
195
+
196
+ dv = Proc.new {|v| list << "dv #{v}" }
197
+ ev = Proc.new {|v| list << "ev #{v}" }
198
+ bt = Proc.new {|v| list << "bt #{v}" }
199
+ fv = Proc.new {|v| list << "fv #{v}" }
200
+ er = Proc.new {|e| list << "er #{e}" }
201
+ enr = Proc.new {|e| list << "enr #{e}" }
202
+
203
+ options = { :discover_vertex => dv,
204
+ :examine_vertex => ev,
205
+ :black_target => bt,
206
+ :finish_vertex => fv,
207
+ :edge_relaxed => er,
208
+ :edge_not_relaxed => enr }
209
+
210
+ result = romania.astar('Arad', 'Bucharest', h, options)
211
+
212
+ assert_equal ["Arad", "Sibiu", "Rimnicu Vilcea", "Bucharest"], result
213
+ # This isn't the greatest test since the exact ordering is not
214
+ # not specified by the algorithm. If someone has a better idea, please fix
215
+ assert_equal ["ev Arad", "er (Arad=Sibiu)", "dv Sibiu",
216
+ "er (Arad=Timisoara)", "dv Timisoara", "er (Arad=Zerind)", "dv Zerind",
217
+ "fv Arad", "ev Zerind", "er (Oradea=Zerind)", "dv Oradea",
218
+ "enr (Arad=Zerind)", "fv Zerind", "ev Oradea", "enr (Oradea=Sibiu)",
219
+ "enr (Oradea=Zerind)", "fv Oradea", "ev Timisoara",
220
+ "er (Lugoj=Timisoara)", "dv Lugoj", "enr (Arad=Timisoara)",
221
+ "fv Timisoara", "ev Lugoj", "enr (Lugoj=Timisoara)",
222
+ "er (Lugoj=Mehadia)", "dv Mehadia", "fv Lugoj", "ev Mehadia",
223
+ "er (Dobreta=Mehadia)", "dv Dobreta", "enr (Lugoj=Mehadia)",
224
+ "fv Mehadia", "ev Dobreta", "enr (Dobreta=Mehadia)",
225
+ "er (Craiova=Dobreta)", "dv Craiova", "fv Dobreta", "ev Craiova",
226
+ "enr (Craiova=Dobreta)", "er (Craiova=Rimnicu Vilcea)",
227
+ "dv Rimnicu Vilcea", "er (Craiova=Pitesti)", "dv Pitesti",
228
+ "fv Craiova", "ev Pitesti", "enr (Pitesti=Rimnicu Vilcea)",
229
+ "er (Bucharest=Pitesti)", "dv Bucharest"], list
230
+ end
231
+
232
+ def test_bfs_spanning_forest
233
+ predecessor, roots = @tree.bfs_spanning_forest(1)
234
+ assert_equal({2=>1, 3=>1, 4=>1, 5=>2, 6=>2, 7=>6, 24=>23}, predecessor)
235
+ assert_equal [1,23], roots.sort
236
+ predecessor, roots = @tree.bfs_spanning_forest(3)
237
+ assert_equal({7=>6, 24=>23, 2=>1, 4=>1}, predecessor)
238
+ assert_equal [1,3,5,6,23], roots.sort
239
+ end
240
+
241
+ def test_dfs_spanning_forest
242
+ predecessor, roots = @tree.dfs_spanning_forest(1)
243
+ assert_equal({5=>2, 6=>2, 7=>6, 24=>23, 2=>1, 3=>1, 4=>2}, predecessor)
244
+ assert_equal [1,23], roots.sort
245
+ predecessor, roots = @tree.dfs_spanning_forest(3)
246
+ assert_equal({7=>6, 24=>23, 2=>1, 4=>2}, predecessor)
247
+ assert_equal [1,3,5,6,23], roots.sort
248
+ end
249
+
250
+ def test_tree_from_vertex
251
+ assert_equal({5=>2, 6=>2, 7=>6, 2=>1, 3=>1, 4=>1}, @tree.bfs_tree_from_vertex(1))
252
+ assert_equal({}, @tree.bfs_tree_from_vertex(3))
253
+ assert_equal({5=>2, 6=>2, 7=>6, 2=>1, 3=>1, 4=>2}, @tree.dfs_tree_from_vertex(1))
254
+ assert_equal({}, @tree.dfs_tree_from_vertex(3))
255
+ end
256
+
257
+ end
@@ -0,0 +1,85 @@
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 'test/unit'
30
+ require 'gratr/import'
31
+
32
+ class TestStrongComponents < Test::Unit::TestCase # :nodoc:
33
+ # Test from boost strong_components.cpp
34
+ # Original Copyright 1997-2001, University of Notre Dame.
35
+ # Original Authors: Andrew Lumsdaine, Lie-Quan Lee, Jermey G. Siek
36
+ def test_boost
37
+ g = Digraph[ 'a', 'b', 'a', 'f', 'a', 'h',
38
+ 'b', 'c', 'b', 'a',
39
+ 'c', 'd', 'c', 'b',
40
+ 'd', 'e',
41
+ 'e', 'd',
42
+ 'f', 'g',
43
+ 'g', 'f', 'g', 'd',
44
+ 'h', 'i',
45
+ 'i', 'h', 'i', 'j', 'i', 'e', 'i', 'c']
46
+
47
+ c = g.strong_components.map {|x| x.sort}
48
+ assert_equal 10, g.vertices.size
49
+ assert_equal 4, c.size
50
+ assert c.include?(['d','e'])
51
+ assert c.include?(['f','g'])
52
+ assert c.include?(['j'])
53
+ assert c.include?(['a','b','c','h','i'])
54
+
55
+ cg = g.condensation
56
+ cg_vertices = cg.map {|v| v.sort}
57
+ assert_equal 4, cg_vertices.size
58
+ assert cg_vertices.include?(['j'])
59
+ assert cg_vertices.include?(['d','e'])
60
+ assert cg_vertices.include?(['f', 'g'])
61
+ assert cg_vertices.include?(['a', 'b', 'c', 'h', 'i'])
62
+ assert cg.edges.map {|e| [e.source.sort.join, e.target.sort.join] }.sort ==
63
+ [['abchi','de'], ['abchi', 'fg'], ['abchi', 'j'], ['fg', 'de']]
64
+ end
65
+
66
+
67
+ # Figure #3, from 'Depth-First Search and Linear Graph Algorithms'
68
+ # by Robert Tarjan, SIAM J. Comput. Vol 1, No.2, June 1972
69
+ def test_tarjan_fig_3
70
+ g = Digraph[ 1,2,
71
+ 2,3, 2,8,
72
+ 3,4, 3,7,
73
+ 4,5,
74
+ 5,3, 5,6,
75
+ 7,4, 7,6,
76
+ 8,1, 8,7 ]
77
+
78
+ c = g.strong_components.map {|x| x.sort}
79
+ assert_equal 8, g.vertices.size
80
+ assert_equal 3, c.size
81
+ assert c.include?([6])
82
+ assert c.include?([1,2,8])
83
+ assert c.include?([3,4,5,7])
84
+ end
85
+ end