rgl 0.4.0 → 0.5.0

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 (72) hide show
  1. data/ChangeLog +19 -10
  2. data/Gemfile +3 -0
  3. data/{README → README.rdoc} +70 -98
  4. data/Rakefile +44 -150
  5. data/examples/canvas.rb +63 -64
  6. data/examples/examples.rb +42 -42
  7. data/examples/graph.dot +46 -0
  8. data/examples/images/example.jpg +0 -0
  9. data/examples/images/module_graph.jpg +0 -0
  10. data/examples/images/rgl_modules.png +0 -0
  11. data/examples/{insel-der-tausend-gefahren.rb → insel_der_tausend_gefahren.rb} +18 -19
  12. data/examples/north.rb +2 -2
  13. data/examples/north2.rb +11 -11
  14. data/examples/rdep-rgl.rb +218 -222
  15. data/lib/rgl/adjacency.rb +78 -74
  16. data/lib/rgl/base.rb +160 -78
  17. data/lib/rgl/bellman_ford.rb +115 -0
  18. data/lib/rgl/bidirectional.rb +17 -10
  19. data/lib/rgl/bipartite.rb +87 -0
  20. data/lib/rgl/condensation.rb +13 -4
  21. data/lib/rgl/connected_components.rb +38 -30
  22. data/lib/rgl/dijkstra.rb +158 -0
  23. data/lib/rgl/dijkstra_visitor.rb +42 -0
  24. data/lib/rgl/dot.rb +40 -32
  25. data/lib/rgl/edge_properties_map.rb +55 -0
  26. data/lib/rgl/edmonds_karp.rb +136 -0
  27. data/lib/rgl/enumerable_ext.rb +4 -1
  28. data/lib/rgl/graph_iterator.rb +15 -0
  29. data/lib/rgl/graph_visitor.rb +138 -0
  30. data/lib/rgl/graph_wrapper.rb +15 -0
  31. data/lib/rgl/graphxml.rb +20 -10
  32. data/lib/rgl/implicit.rb +68 -66
  33. data/lib/rgl/mutable.rb +37 -31
  34. data/lib/rgl/path_builder.rb +40 -0
  35. data/lib/rgl/prim.rb +52 -0
  36. data/lib/rgl/rdot.rb +411 -374
  37. data/lib/rgl/topsort.rb +23 -16
  38. data/lib/rgl/transitivity.rb +29 -27
  39. data/lib/rgl/traversal.rb +67 -205
  40. data/rakelib/dep_graph.rake +4 -3
  41. data/test/bellman_ford_test.rb +187 -0
  42. data/test/bipartite_test.rb +47 -0
  43. data/test/components_test.rb +80 -0
  44. data/test/cycles_test.rb +60 -0
  45. data/test/dijkstra_test.rb +148 -0
  46. data/test/directed_graph_test.rb +118 -0
  47. data/test/dot_test.rb +26 -0
  48. data/test/edge_properties_map_test.rb +63 -0
  49. data/test/edge_test.rb +35 -0
  50. data/test/edmonds_karp_test.rb +105 -0
  51. data/{tests/TestGraph.rb → test/graph_test.rb} +6 -6
  52. data/test/graph_xml_test.rb +57 -0
  53. data/test/implicit_test.rb +53 -0
  54. data/test/prim_test.rb +98 -0
  55. data/{tests/TestRdot.rb → test/rdot_test.rb} +309 -308
  56. data/{tests → test}/test_helper.rb +4 -1
  57. data/{tests/TestTransitivity.rb → test/transitivity_test.rb} +43 -43
  58. data/test/traversal_test.rb +221 -0
  59. data/test/undirected_graph_test.rb +103 -0
  60. metadata +226 -145
  61. data/examples/example.jpg +0 -0
  62. data/examples/module_graph.jpg +0 -0
  63. data/install.rb +0 -49
  64. data/tests/TestComponents.rb +0 -65
  65. data/tests/TestCycles.rb +0 -61
  66. data/tests/TestDirectedGraph.rb +0 -125
  67. data/tests/TestDot.rb +0 -18
  68. data/tests/TestEdge.rb +0 -34
  69. data/tests/TestGraphXML.rb +0 -57
  70. data/tests/TestImplicit.rb +0 -52
  71. data/tests/TestTraversal.rb +0 -220
  72. data/tests/TestUnDirectedGraph.rb +0 -102
@@ -0,0 +1,118 @@
1
+ require 'test_helper'
2
+
3
+ require 'rgl/adjacency'
4
+
5
+ include RGL
6
+ include RGL::Edge
7
+
8
+ class TestDirectedGraph < Test::Unit::TestCase
9
+ def setup
10
+ @dg = DirectedAdjacencyGraph.new
11
+ [[1, 2], [2, 3], [3, 2], [2, 4]].each do |(src, target)|
12
+ @dg.add_edge(src, target)
13
+ end
14
+ end
15
+
16
+ def test_empty_graph
17
+ dg = DirectedAdjacencyGraph.new
18
+ assert dg.empty?
19
+ assert dg.directed?
20
+ assert(!dg.has_edge?(2, 1))
21
+ assert(!dg.has_vertex?(3))
22
+ # Non existent vertex result in a Name Error because each_key is
23
+ # called for nil
24
+ assert_raises(NoVertexError) { dg.out_degree(3) }
25
+ assert_equal([], dg.vertices)
26
+ assert_equal(0, dg.size)
27
+ assert_equal(0, dg.num_vertices)
28
+ assert_equal(0, dg.num_edges)
29
+ assert_equal(DirectedEdge, dg.edge_class)
30
+ assert([].eql?(dg.edges))
31
+ end
32
+
33
+ def test_add
34
+ dg = DirectedAdjacencyGraph.new
35
+ dg.add_edge(1, 2)
36
+ assert(!dg.empty?)
37
+ assert(dg.has_edge?(1, 2))
38
+ assert(!dg.has_edge?(2, 1))
39
+ assert(dg.has_vertex?(1) && dg.has_vertex?(2))
40
+ assert(!dg.has_vertex?(3))
41
+
42
+ assert_equal([1, 2], dg.vertices.sort)
43
+ assert([DirectedEdge.new(1, 2)].eql?(dg.edges))
44
+ assert_equal("(1-2)", dg.edges.join)
45
+
46
+ assert_equal([2], dg.adjacent_vertices(1))
47
+ assert_equal([], dg.adjacent_vertices(2))
48
+
49
+ assert_equal(1, dg.out_degree(1))
50
+ assert_equal(0, dg.out_degree(2))
51
+ end
52
+
53
+ def test_edges
54
+ assert_equal(4, @dg.edges.length)
55
+ assert_equal([1, 2, 2, 3], @dg.edges.map { |l| l.source }.sort)
56
+ assert_equal([2, 2, 3, 4], @dg.edges.map { |l| l.target }.sort)
57
+ assert_equal("(1-2)(2-3)(2-4)(3-2)", @dg.edges.map { |l| l.to_s }.sort.join)
58
+ # assert_equal([0,1,2,3], @dg.edges.map {|l| l.info}.sort)
59
+ end
60
+
61
+ def test_vertices
62
+ assert_equal([1, 2, 3, 4], @dg.vertices.sort)
63
+ end
64
+
65
+ def test_edges_from_to?
66
+ assert @dg.has_edge?(1, 2)
67
+ assert @dg.has_edge?(2, 3)
68
+ assert @dg.has_edge?(3, 2)
69
+ assert @dg.has_edge?(2, 4)
70
+ assert !@dg.has_edge?(2, 1)
71
+ assert !@dg.has_edge?(3, 1)
72
+ assert !@dg.has_edge?(4, 1)
73
+ assert !@dg.has_edge?(4, 2)
74
+ end
75
+
76
+ def test_remove_edges
77
+ @dg.remove_edge 1, 2
78
+ assert !@dg.has_edge?(1, 2)
79
+ @dg.remove_edge 1, 2
80
+ assert !@dg.has_edge?(1, 2)
81
+ @dg.remove_vertex 3
82
+ assert !@dg.has_vertex?(3)
83
+ assert !@dg.has_edge?(2, 3)
84
+ assert_equal('(2-4)', @dg.edges.join)
85
+ end
86
+
87
+ def test_add_vertices
88
+ dg = DirectedAdjacencyGraph.new
89
+ dg.add_vertices 1, 3, 2, 4
90
+ assert_equal dg.vertices.sort, [1, 2, 3, 4]
91
+
92
+ dg.remove_vertices 1, 3
93
+ assert_equal dg.vertices.sort, [2, 4]
94
+ end
95
+
96
+ def test_creating_from_array
97
+ dg = DirectedAdjacencyGraph[1, 2, 3, 4]
98
+ assert_equal([1, 2, 3, 4], dg.vertices.sort)
99
+ assert_equal('(1-2)(3-4)', dg.edges.join)
100
+ end
101
+
102
+ def test_reverse
103
+ # Add isolated vertex
104
+ @dg.add_vertex(42)
105
+ reverted = @dg.reverse
106
+
107
+ @dg.each_edge do |u, v|
108
+ assert(reverted.has_edge?(v, u))
109
+ end
110
+
111
+ assert(reverted.has_vertex?(42), 'Reverted graph should contain isolated Vertex 42')
112
+ end
113
+
114
+ def test_to_undirected
115
+ undirected = @dg.to_undirected
116
+ assert_equal '(1=2)(2=3)(2=4)', undirected.edges.sort.join
117
+ end
118
+ end
@@ -0,0 +1,26 @@
1
+ require 'test_helper'
2
+
3
+ require 'rgl/dot'
4
+ require 'rgl/adjacency'
5
+
6
+ class TestDot < Test::Unit::TestCase
7
+
8
+ def assert_match(dot, pattern)
9
+ assert(!(dot =~ pattern).nil?, "#{dot} doesn't match #{pattern}")
10
+ end
11
+
12
+ def test_to_dot_digraph
13
+ graph = RGL::DirectedAdjacencyGraph[1, 2]
14
+ dot = graph.to_dot_graph.to_s
15
+
16
+ assert_match(dot, /\{[^}]*\}/) # {...}
17
+ assert_match(dot, /1\s*\[/) # node 1
18
+ assert_match(dot, /2\s*\[/) # node 2
19
+ assert_match(dot, /1\s*->\s*2/) # edge
20
+ end
21
+
22
+ def test_to_dot_graph
23
+ graph = RGL::AdjacencyGraph[1, 2]
24
+ dot = graph.write_to_graphic_file
25
+ end
26
+ end
@@ -0,0 +1,63 @@
1
+ require 'test_helper'
2
+
3
+ require 'rgl/edge_properties_map'
4
+
5
+ include RGL
6
+
7
+ class TestEdgePropertiesMap < Test::Unit::TestCase
8
+
9
+ def setup
10
+ @edge_properties = {
11
+ [1, 2] => 1,
12
+ [2, 3] => 3,
13
+ [1, 3] => 7
14
+ }
15
+
16
+ @edge_properties_lambda = lambda { |edge| @edge_properties[edge] }
17
+ end
18
+
19
+ def test_directed_graph
20
+ properties_map = EdgePropertiesMap.new(@edge_properties, true)
21
+
22
+ assert_equal(1, properties_map.edge_property(1, 2))
23
+ assert_equal(3, properties_map.edge_property(2, 3))
24
+ assert_equal(7, properties_map.edge_property(1, 3))
25
+
26
+ assert_raise ArgumentError do
27
+ properties_map.edge_property(2, 1)
28
+ end
29
+ end
30
+
31
+ def test_undirected_graph
32
+ properties_map = EdgePropertiesMap.new(@edge_properties, false)
33
+
34
+ assert_equal(1, properties_map.edge_property(1, 2))
35
+ assert_equal(3, properties_map.edge_property(2, 3))
36
+ assert_equal(7, properties_map.edge_property(1, 3))
37
+
38
+ assert_equal(1, properties_map.edge_property(2, 1))
39
+ assert_equal(3, properties_map.edge_property(3, 2))
40
+ assert_equal(7, properties_map.edge_property(3, 1))
41
+ end
42
+
43
+ def test_nil_properties
44
+ assert_raise ArgumentError do
45
+ EdgePropertiesMap.new(@edge_properties.merge([1, 4] => nil), false)
46
+ end
47
+ end
48
+
49
+ def test_non_negative_properties_map
50
+ assert_raise ArgumentError do
51
+ NonNegativeEdgePropertiesMap.new(@edge_properties.merge([1, 4] => -2), false)
52
+ end
53
+ end
54
+
55
+ def test_with_lambda
56
+ properties_map = EdgePropertiesMap.new(@edge_properties_lambda, true)
57
+
58
+ assert_equal(1, properties_map.edge_property(1, 2))
59
+ assert_equal(3, properties_map.edge_property(2, 3))
60
+ assert_equal(7, properties_map.edge_property(1, 3))
61
+ end
62
+
63
+ end
@@ -0,0 +1,35 @@
1
+ require 'test_helper'
2
+
3
+ require 'rgl/base'
4
+
5
+ include RGL::Edge
6
+
7
+ class TestEdge < Test::Unit::TestCase
8
+
9
+ def test_directed_edge
10
+ assert_raises(ArgumentError) { DirectedEdge.new }
11
+ e = DirectedEdge.new 1, 2
12
+ assert_equal(1, e.source)
13
+ assert_equal(2, e.target)
14
+ assert_equal([1, 2], e.to_a)
15
+ assert_equal("(1-2)", e.to_s)
16
+ assert_equal("(2-1)", e.reverse.to_s)
17
+ assert_equal([1, 2], [e[0], e[1]])
18
+ assert(DirectedEdge[1, 2].eql?(DirectedEdge.new(1, 2)))
19
+ assert(!DirectedEdge[1, 2].eql?(DirectedEdge.new(1, 3)))
20
+ assert(!DirectedEdge[2, 1].eql?(DirectedEdge.new(1, 2)))
21
+ end
22
+
23
+ def test_undirected_edge
24
+ assert_raises(ArgumentError) { UnDirectedEdge.new }
25
+ e = UnDirectedEdge.new 1, 2
26
+ assert_equal(1, e.source)
27
+ assert_equal(2, e.target)
28
+ assert_equal([1, 2], e.to_a)
29
+ assert_equal("(1=2)", e.to_s)
30
+ assert(UnDirectedEdge.new(1, 2).eql?(UnDirectedEdge.new(2, 1)))
31
+ assert(!UnDirectedEdge.new(1, 3).eql?(UnDirectedEdge.new(2, 1)))
32
+ assert_equal(UnDirectedEdge.new(1, 2).hash, UnDirectedEdge.new(1, 2).hash)
33
+ end
34
+
35
+ end
@@ -0,0 +1,105 @@
1
+ require 'test_helper'
2
+
3
+ require 'rgl/edmonds_karp'
4
+ require 'rgl/adjacency'
5
+
6
+ include RGL
7
+
8
+ class TestEdmondsKarp < Test::Unit::TestCase
9
+
10
+ def setup
11
+ @capacities_map = {
12
+ [1, 2] => 3,
13
+ [1, 4] => 3,
14
+ [2, 3] => 4,
15
+ [3, 1] => 3,
16
+ [3, 4] => 1,
17
+ [3, 5] => 2,
18
+ [4, 5] => 2,
19
+ [4, 6] => 6,
20
+ [5, 2] => 1,
21
+ [5, 7] => 1,
22
+ [6, 7] => 9
23
+ }
24
+
25
+ @graph = DirectedAdjacencyGraph[*@capacities_map.keys.flatten]
26
+
27
+ add_reverse_edges(@graph, @capacities_map)
28
+
29
+ @expected_flow = {
30
+ [1, 2] => 2, [2, 1] => -2,
31
+ [1, 4] => 3, [4, 1] => -3,
32
+ [2, 3] => 2, [3, 2] => -2,
33
+ [3, 4] => 1, [4, 3] => -1,
34
+ [3, 5] => 1, [5, 3] => -1,
35
+ [4, 5] => 0, [5, 4] => 0,
36
+ [4, 6] => 4, [6, 4] => -4,
37
+ [5, 7] => 1, [7, 5] => -1,
38
+ [6, 7] => 4, [7, 6] => -4,
39
+ }
40
+ end
41
+
42
+ def test_max_flow
43
+ assert_equal(@expected_flow, maximum_flow(1, 7))
44
+ end
45
+
46
+ def test_max_flow_with_lambda_capacities_map
47
+ capacities_lambda = lambda { |edge| @capacities_map[edge] }
48
+ assert_equal(@expected_flow, maximum_flow(1, 7), capacities_lambda)
49
+ end
50
+
51
+ def test_reverse_edges_validation
52
+ @graph.remove_edge(2, 1)
53
+ assert_raises(ArgumentError, 'reverse edge for (2, 1) is missing') { maximum_flow(1, 7) }
54
+ end
55
+
56
+ def test_missing_capacities_validation
57
+ @capacities_map.delete([3, 5])
58
+ assert_raises(ArgumentError, 'capacity for edge (3, 5) is missing') { maximum_flow(1, 7) }
59
+ end
60
+
61
+ def test_negative_capacities_validation
62
+ @capacities_map[[5, 2]] = -2
63
+ assert_raises(ArgumentError, 'capacity of edge (5, 2) is negative') { maximum_flow(1, 7) }
64
+ end
65
+
66
+ def test_zero_reverse_capacities_validation
67
+ @capacities_map[[7, 5]] = 1
68
+ assert_raises(ArgumentError, 'either (7, 5) or (5, 7) should have 0 capacity') { maximum_flow(1, 7) }
69
+ end
70
+
71
+ def test_zero_capacities
72
+ @capacities_map[[1, 5]] = 0
73
+ @capacities_map[[5, 1]] = 0
74
+ assert_equal(@expected_flow, maximum_flow(1, 7))
75
+ end
76
+
77
+ def test_equal_source_and_sink
78
+ assert_raises(ArgumentError, "source and sink can't be equal") { maximum_flow(1, 1) }
79
+ end
80
+
81
+ def test_directed_graph_validation
82
+ graph = AdjacencyGraph.new
83
+ graph.add_vertex(1)
84
+
85
+ assert_raises(NotDirectedError, 'Edmonds-Karp algorithm can only be applied to a directed graph') { graph.maximum_flow({}, 1, 2) }
86
+ end
87
+
88
+ def test_unreachable_sink
89
+ assert_equal({}, maximum_flow(1, 8))
90
+ end
91
+
92
+ private
93
+
94
+ def maximum_flow(source, sink, capacities_map = @capacities_map)
95
+ @graph.maximum_flow(capacities_map, source, sink)
96
+ end
97
+
98
+ def add_reverse_edges(graph, capacities_map)
99
+ capacities_map.keys.each do |(u, v)|
100
+ graph.add_edge(v, u)
101
+ capacities_map[[v, u]] = 0
102
+ end
103
+ end
104
+
105
+ end
@@ -1,7 +1,7 @@
1
- require 'test/unit'
2
- require 'rgl/adjacency'
3
1
  require 'test_helper'
4
2
 
3
+ require 'rgl/adjacency'
4
+
5
5
  include RGL
6
6
 
7
7
  class TestGraph < Test::Unit::TestCase
@@ -11,9 +11,9 @@ class TestGraph < Test::Unit::TestCase
11
11
  end
12
12
 
13
13
  def setup
14
- @dg1 = DirectedAdjacencyGraph.new
15
- @edges = [[1,2],[2,3],[2,4],[4,5],[1,6],[6,4]]
16
- @edges.each do |(src,target)|
14
+ @dg1 = DirectedAdjacencyGraph.new
15
+ @edges = [[1, 2], [2, 3], [2, 4], [4, 5], [1, 6], [6, 4]]
16
+ @edges.each do |(src, target)|
17
17
  @dg1.add_edge(src, target)
18
18
  end
19
19
  @loan_vertices = [7, 8, 9]
@@ -58,7 +58,7 @@ class TestGraph < Test::Unit::TestCase
58
58
  end
59
59
 
60
60
  def test_set_edgelist_class
61
- edges = @dg1.edges
61
+ edges = @dg1.edges
62
62
  @dg1.edgelist_class=Array
63
63
  assert_equal edges, @dg1.edges
64
64
  end
@@ -0,0 +1,57 @@
1
+ require 'test_helper'
2
+
3
+ require 'rgl/graphxml'
4
+ require 'rgl/adjacency'
5
+ require 'rgl/topsort'
6
+ require 'rgl/connected_components'
7
+
8
+ include RGL
9
+
10
+ class TestGraphXML < Test::Unit::TestCase
11
+ NORTH_DIR = './examples/north/'
12
+
13
+ def setup
14
+ @stream = File.new(NORTH_DIR + "g.10.0.graphml")
15
+ end
16
+
17
+ def tear_down
18
+ @stream.close
19
+ end
20
+
21
+ def test_graphxml
22
+ @dg = DirectedAdjacencyGraph.new.from_graphxml(@stream).edges.sort.join
23
+ assert_equal("(n0-n1)(n0-n2)(n0-n9)(n3-n4)(n4-n5)(n5-n7)(n8-n0)(n8-n3)(n8-n4)(n8-n5)(n8-n6)", @dg)
24
+ end
25
+
26
+ def test_north_graphs
27
+ name, nnodes, nedges = '', 0, 0
28
+ IO.foreach(NORTH_DIR + '/Graph.log') {
29
+ |line|
30
+ if /name:\s*(.*)\sformat: graphml\s+nodes: (\d+)\s+edges: (\d+)/ =~ line
31
+ name, nnodes, nedges = $1, $2.to_i, $3.to_i
32
+ end
33
+ if name && /directed: (\w+).*acyclic: (\w+).*connected: (\w+).*biconnected: (\w+)\s+/ =~ line
34
+ directed, acyclic, connected, biconnected = $1, $2, $3, $4
35
+ File.open(NORTH_DIR + name + '.graphml') {
36
+ |file|
37
+ print '.'; $stdout.flush
38
+ graph = (directed == 'true' ? DirectedAdjacencyGraph : AdjacencyGraph).new.from_graphxml(file)
39
+ #graph.write_to_graphic_file
40
+ assert_equal(nnodes, graph.num_vertices)
41
+ assert_equal(nedges, graph.num_edges)
42
+ assert_equal(acyclic, graph.acyclic?.to_s)
43
+
44
+ num_comp = 0
45
+ graph.to_undirected.each_connected_component { |x| num_comp += 1 }
46
+ assert_equal(connected, (num_comp == 1).to_s)
47
+
48
+ # if graph.directed?
49
+ # num_comp = graph.strongly_connected_components.num_comp
50
+ # #puts num_comp
51
+ # assert_equal(biconnected, (num_comp == 1).to_s)
52
+ # end
53
+ }
54
+ end
55
+ }
56
+ end
57
+ end
@@ -0,0 +1,53 @@
1
+ require 'test_helper'
2
+
3
+ require 'rgl/implicit'
4
+ require 'rgl/adjacency'
5
+
6
+ include RGL
7
+
8
+ class TestImplicit < Test::Unit::TestCase
9
+ def setup
10
+ @dg = DirectedAdjacencyGraph.new
11
+ [[1, 2], [2, 3], [2, 4], [4, 5], [1, 6], [6, 4]].each do |(src, target)|
12
+ @dg.add_edge(src, target)
13
+ end
14
+
15
+ @cycle = ImplicitGraph.new { |g|
16
+ g.vertex_iterator { |b| 0.upto(4, &b) }
17
+ g.adjacent_iterator { |x, b| b.call((x+1)%5) }
18
+ g.directed = true
19
+ }
20
+ end
21
+
22
+ def test_empty
23
+ empty = ImplicitGraph.new
24
+ assert(empty.empty?)
25
+ assert_equal([], empty.edges)
26
+ assert_equal([], empty.vertices)
27
+ end
28
+
29
+ def test_cycle
30
+ assert(!@cycle.empty?)
31
+ assert_equal([0, 1, 2, 3, 4], @cycle.vertices.sort)
32
+ assert_equal("(0-1)(1-2)(2-3)(3-4)(4-0)", @cycle.edges.sort.join)
33
+ end
34
+
35
+ def test_vertex_filtered_graph
36
+ fg = @cycle.vertices_filtered_by { |v| v%2 == 0 }
37
+ assert_equal([0, 2, 4], fg.vertices.sort)
38
+ assert_equal("(4-0)", fg.edges.sort.join)
39
+ assert(fg.directed?)
40
+
41
+ fg = @dg.vertices_filtered_by { |v| v%2 == 0 }
42
+ assert_equal([2, 4, 6], fg.vertices.sort)
43
+ assert_equal("(2-4)(6-4)", fg.edges.sort.join)
44
+ assert(fg.directed?)
45
+ end
46
+
47
+ def test_edge_filtered_graph
48
+ fg = @cycle.edges_filtered_by { |u, v| u+v > 3 }
49
+ assert_equal(@cycle.vertices.sort, fg.vertices.sort)
50
+ assert_equal("(2-3)(3-4)(4-0)", fg.edges.sort.join)
51
+ assert(fg.directed?)
52
+ end
53
+ end