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,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