yargi 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +54 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +23 -0
- data/LICENCE.md +22 -0
- data/Manifest.txt +15 -0
- data/README.md +128 -0
- data/Rakefile +23 -0
- data/examples/fs2dot.rb +1 -1
- data/examples/random.rb +20 -0
- data/lib/yargi.rb +8 -10
- data/lib/yargi/decorate.rb +55 -0
- data/lib/yargi/digraph.rb +71 -46
- data/lib/yargi/digraph_edge.rb +23 -23
- data/lib/yargi/digraph_vertex.rb +27 -27
- data/lib/yargi/edge_set.rb +12 -12
- data/lib/yargi/element_set.rb +54 -54
- data/lib/yargi/loader.rb +1 -0
- data/lib/yargi/markable.rb +12 -12
- data/lib/yargi/predicate.rb +62 -43
- data/lib/yargi/random.rb +98 -0
- data/lib/yargi/version.rb +14 -0
- data/lib/yargi/vertex_set.rb +12 -12
- data/spec/spec_helper.rb +2 -0
- data/spec/test_decorate.rb +28 -0
- data/spec/test_digraph.rb +42 -0
- data/spec/test_random.rb +45 -0
- data/spec/test_yargi.rb +8 -0
- data/tasks/debug_mail.rake +75 -0
- data/tasks/debug_mail.txt +13 -0
- data/tasks/gem.rake +68 -0
- data/tasks/spec_test.rake +71 -0
- data/tasks/unit_test.rake +76 -0
- data/tasks/yard.rake +51 -0
- data/test/test_all.rb +1 -1
- data/test/yargi/README-example.gif +0 -0
- data/test/yargi/digraph_set_features_test.rb +14 -16
- data/test/yargi/digraph_test.rb +33 -33
- data/test/yargi/digraph_vertex_test.rb +9 -9
- data/test/yargi/documentation_test.rb +7 -7
- data/test/yargi/edge_set_test.rb +3 -3
- data/test/yargi/element_set_test.rb +2 -2
- data/test/yargi/hypotheses_test.rb +4 -4
- data/test/yargi/markable_test.rb +11 -11
- data/test/yargi/predicate_test.rb +14 -14
- data/test/yargi/source-sink.gif +0 -0
- data/test/yargi/vertex_set_test.rb +7 -7
- data/yargi.gemspec +187 -0
- data/yargi.noespec +23 -0
- metadata +110 -38
- data/CONTRIBUTE +0 -11
- data/LICENCE +0 -25
- data/README +0 -79
- data/examples/fs2dot.dot +0 -78
- data/examples/fs2dot.gif +0 -0
data/CHANGELOG.md
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
# 0.2.0 / 2012-02-17
|
2
|
+
|
3
|
+
* Enhancements
|
4
|
+
|
5
|
+
* Digraph.new now yields self if a block is given. This allows building a
|
6
|
+
graph more smoothly:
|
7
|
+
|
8
|
+
graph = Digraph.new{|d|
|
9
|
+
d.add_n_vertices(5)
|
10
|
+
...
|
11
|
+
}
|
12
|
+
|
13
|
+
* An integer is now automatically recognized as a selection predicate. This
|
14
|
+
means that the following will also work:
|
15
|
+
|
16
|
+
# connect 1-th and 2-th vertices
|
17
|
+
graph.connect(1, 2)
|
18
|
+
|
19
|
+
* Added Digraph#ith_vertex and Digraph#ith_edge
|
20
|
+
* Added Digraph#vertex_count and Digraph#edge_count
|
21
|
+
* Added Digraph.random to generate graph for tests and benchmarks
|
22
|
+
|
23
|
+
* Bug fixes
|
24
|
+
|
25
|
+
* Misused of $STDERR has been replaced by $stderr
|
26
|
+
|
27
|
+
* Internals & Devel
|
28
|
+
|
29
|
+
* The project structure is now handled by Noe.
|
30
|
+
|
31
|
+
# 0.1.2
|
32
|
+
|
33
|
+
* Enhancements
|
34
|
+
|
35
|
+
* Markable.add_marks also accepts a block to mimic add_n_vertices and
|
36
|
+
ElementSet.add_marks
|
37
|
+
* EdgeSet forwards source= and target= to its elements, allowing bulk
|
38
|
+
reconnection
|
39
|
+
* Digraph.to_dot_attributes made much more smart on typical array values
|
40
|
+
|
41
|
+
* Bug fixes
|
42
|
+
|
43
|
+
* Markable.has_key? is used to avoid predicate side-effects on v[:mark] when
|
44
|
+
the mark is false
|
45
|
+
|
46
|
+
# 0.1.1
|
47
|
+
|
48
|
+
* Bug fix in dot generation
|
49
|
+
* Examples started and documentation improved
|
50
|
+
* Web site index generation using wlang
|
51
|
+
|
52
|
+
# 0.1.0
|
53
|
+
|
54
|
+
* Birthday!
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.3)
|
5
|
+
rake (0.9.2.2)
|
6
|
+
rspec (2.8.0)
|
7
|
+
rspec-core (~> 2.8.0)
|
8
|
+
rspec-expectations (~> 2.8.0)
|
9
|
+
rspec-mocks (~> 2.8.0)
|
10
|
+
rspec-core (2.8.0)
|
11
|
+
rspec-expectations (2.8.0)
|
12
|
+
diff-lcs (~> 1.1.2)
|
13
|
+
rspec-mocks (2.8.0)
|
14
|
+
wlang (0.10.2)
|
15
|
+
|
16
|
+
PLATFORMS
|
17
|
+
java
|
18
|
+
ruby
|
19
|
+
|
20
|
+
DEPENDENCIES
|
21
|
+
rake (~> 0.9.2)
|
22
|
+
rspec (~> 2.8.0)
|
23
|
+
wlang (~> 0.10.2)
|
data/LICENCE.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# The MIT Licence
|
2
|
+
|
3
|
+
Copyright (c) 2009-2011 - Bernard Lambeau
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Manifest.txt
ADDED
data/README.md
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
# Yargi
|
2
|
+
|
3
|
+
Yet Another Ruby Graph Library
|
4
|
+
|
5
|
+
## Links
|
6
|
+
|
7
|
+
* http://rubydoc.info/github/blambeau/yargi/master/frames
|
8
|
+
* http://github.com/blambeau/yargi
|
9
|
+
* http://rubygems.org/gems/yargi
|
10
|
+
|
11
|
+
## Description
|
12
|
+
|
13
|
+
Yargi provides a powerful **mutable** digraph implementation. It has been
|
14
|
+
designed to allow full mutability the easy way. A quick tour below.
|
15
|
+
|
16
|
+
## Quick tour
|
17
|
+
|
18
|
+
### Digraph, Vertex and Edge
|
19
|
+
|
20
|
+
Unlike {http://rubyforge.org/projects/rgl/ RGL}, Yargi implements graph
|
21
|
+
components through concrete classes, not modules (that is, in a
|
22
|
+
standard-but-closed way). See Digraph, Digraph::Vertex and Digraph::Edge
|
23
|
+
respectively.
|
24
|
+
|
25
|
+
### Markable pattern
|
26
|
+
|
27
|
+
Graphs, vertices and edges are markable through a Hash-like API: users can
|
28
|
+
install their own key/value pairs on each graph element. When keys are Symbol
|
29
|
+
objects, accessors are automatically generated to provide a friendly
|
30
|
+
object-oriented API (this feature must be used with care, for obvious reasons).
|
31
|
+
|
32
|
+
graph = Yargi::Digraph.new
|
33
|
+
v1 = graph.add_vertex(:kind => "simple vertex")
|
34
|
+
puts v1.kind
|
35
|
+
# => "simple vertex"
|
36
|
+
|
37
|
+
### Typed elements
|
38
|
+
|
39
|
+
Graph elements (vertices and edges) can be tagged with your own modules (at
|
40
|
+
creation, or later). This is the standard Yargi way to apply a 'select-and-do'
|
41
|
+
feature described below:
|
42
|
+
|
43
|
+
# These are node types
|
44
|
+
module Diamond; end
|
45
|
+
module Circle; end
|
46
|
+
|
47
|
+
# Let build a graph with 5 diamonds
|
48
|
+
graph = Yargi::Digraph.new
|
49
|
+
graph.add_n_vertices(5, Diamond) do |v,i|
|
50
|
+
v[:color] = (i%2==0 ? 'red' : 'blue')
|
51
|
+
end
|
52
|
+
|
53
|
+
# Let add 5 circles
|
54
|
+
graph.add_n_vertices(5, Circle)
|
55
|
+
|
56
|
+
# connect all diamonds to all circles
|
57
|
+
graph.connect(Diamond, Circle)
|
58
|
+
|
59
|
+
### Selection mechanism
|
60
|
+
|
61
|
+
Yargi helps you finding the nodes and edges you are looking for through a
|
62
|
+
declarative selection mechanism: almost all methods that return a set of
|
63
|
+
vertices or edges (Vertex.out_edges, for example) accept a predicate argument
|
64
|
+
to filter the result, module names being most-used shortcuts.
|
65
|
+
See Yargi::Predicate for details.
|
66
|
+
|
67
|
+
# [... previous example continued ...]
|
68
|
+
graph.vertices(Diamond) # selects all diamonds
|
69
|
+
graph.vertices(Diamond){|v| v.color=='blue'} # selects blue diamonds only
|
70
|
+
|
71
|
+
# Proc variant, find sink states
|
72
|
+
sink = Yargi.predicate {|v| v.out_edges.empty?}
|
73
|
+
graph.vertices(sink & Circle) # select all Circle sink states (no one)
|
74
|
+
|
75
|
+
# Or selection
|
76
|
+
is_blue = Yargi.predicate {|v| Diamond===v and v.color=='blue'}
|
77
|
+
graph.vertices(is_blue|Circle) # select blue diamonds and circles
|
78
|
+
|
79
|
+
### VertexSet and EdgeSet
|
80
|
+
|
81
|
+
The selection mechanism always returns arrays ... being instances of
|
82
|
+
Yargi::VertexSet and Yargi::EdgeSet. These classes help you walking your graph
|
83
|
+
easily:
|
84
|
+
|
85
|
+
# [... previous example continued ...]
|
86
|
+
circles = graph.vertices(Diamond).adjacent
|
87
|
+
puts graph.vertices(Circle)==circles
|
88
|
+
# => true
|
89
|
+
|
90
|
+
### Select-and-do
|
91
|
+
|
92
|
+
Many graph methods accept sets of vertices and edges as well as selection queries
|
93
|
+
to work. Instead of connecting one source to one target at a time by passing the
|
94
|
+
vertices, describe what you want and Yardi does it. Add, connect, remove, mark,
|
95
|
+
reconnect many vertices/edges with a single method call:
|
96
|
+
|
97
|
+
graph.vertices(Diamond).add_marks(:label => '', :shape => 'diamond')
|
98
|
+
graph.vertices(Circle).add_marks(:label => '', :shape => 'circle')
|
99
|
+
puts graph.to_dot
|
100
|
+
graph.remove_vertices(Circle) # remove all circles
|
101
|
+
|
102
|
+
### Mutable graphs
|
103
|
+
|
104
|
+
Graphs here are mutable, mutable and mutable and this is the reason why Yargi
|
105
|
+
exists. It comes from a project where manipulating graphs by reconnecting edges,
|
106
|
+
removing vertices is the norm, not the exception.
|
107
|
+
|
108
|
+
### Complexity don't care
|
109
|
+
|
110
|
+
The digraph implementation uses an incident list data structure. This graph
|
111
|
+
library has not been designed with efficiency in mind so that complexities
|
112
|
+
are not documented nor guaranteed. That is not to say that improvements are
|
113
|
+
not welcome, of course.
|
114
|
+
|
115
|
+
## Distribution & Credits
|
116
|
+
|
117
|
+
_Yargi_ is freely available (under a MIT licence) as a 'yargi' gem on
|
118
|
+
{http://rubygems.org/gems/yargi rubygems.org}. Use 'gem install yargi' to
|
119
|
+
install it. The sources are on {http://github.com/blambeau/yargi github}, which
|
120
|
+
is also the place where bugs and issues can be reported.
|
121
|
+
|
122
|
+
This work is supported by the {http://www.uclouvain.be/en-ingi.html department
|
123
|
+
of computer science} of the {http://www.uclouvain.be/en-index.html University of
|
124
|
+
Louvain} (EPL/INGI, Universite Catholique de Louvain, UCL, Louvain-la-Neuve,
|
125
|
+
Belgium).
|
126
|
+
|
127
|
+
This work was also partially supported by the Regional Government of Wallonia
|
128
|
+
(GISELE project, RW Conv. 616425).
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
begin
|
2
|
+
gem "bundler", "~> 1.0"
|
3
|
+
require "bundler/setup"
|
4
|
+
rescue LoadError => ex
|
5
|
+
puts ex.message
|
6
|
+
abort "Bundler failed to load, (did you run 'gem install bundler' ?)"
|
7
|
+
end
|
8
|
+
|
9
|
+
# Dynamically load the gem spec
|
10
|
+
$gemspec_file = File.expand_path('../yargi.gemspec', __FILE__)
|
11
|
+
$gemspec = Kernel.eval(File.read($gemspec_file))
|
12
|
+
|
13
|
+
# We run tests by default
|
14
|
+
task :default => :test
|
15
|
+
|
16
|
+
#
|
17
|
+
# Install all tasks found in tasks folder
|
18
|
+
#
|
19
|
+
# See .rake files there for complete documentation.
|
20
|
+
#
|
21
|
+
Dir["tasks/*.rake"].each do |taskfile|
|
22
|
+
load taskfile
|
23
|
+
end
|
data/examples/fs2dot.rb
CHANGED
@@ -50,7 +50,7 @@ graph.vertices(FileVertex){|v| /\.rb (.*)$/ =~ v.label}.add_marks(
|
|
50
50
|
graph.vertices(FileVertex){|v| /\.rb (.*)$/ =~ v.label}.in_adjacent.add_marks do |v|
|
51
51
|
{:fillcolor => 'gold', :fontcolor => 'black'}
|
52
52
|
end
|
53
|
-
|
53
|
+
|
54
54
|
# Save it
|
55
55
|
File.open(File.join(File.dirname(__FILE__), 'fs2dot.dot'), 'w') do |f|
|
56
56
|
f << graph.to_dot
|
data/examples/random.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
require 'yargi'
|
3
|
+
|
4
|
+
COLORS = ["blue", "red", "yellow", "green"]
|
5
|
+
|
6
|
+
# We build a random graph with approximately 20 vertices
|
7
|
+
# and 60 edges (will be stripped by default). Each vertex
|
8
|
+
# will have a color picked at random.
|
9
|
+
graph = Yargi::Digraph.random(20,60) do |r|
|
10
|
+
r.vertex_builder = lambda{|v,i|
|
11
|
+
v[:color] = COLORS[Kernel.rand(COLORS.size)]
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
# The label of each vertex will be its depth
|
16
|
+
Yargi::Decorate::DEPTH.execute(graph)
|
17
|
+
graph.vertices.add_marks{|v| {:label => v[:depth]}}
|
18
|
+
|
19
|
+
# Print as a dot graph
|
20
|
+
puts graph.to_dot
|
data/lib/yargi.rb
CHANGED
@@ -1,25 +1,22 @@
|
|
1
|
+
require 'yargi/version'
|
2
|
+
require 'yargi/loader'
|
1
3
|
require 'yargi/predicate'
|
2
|
-
|
3
4
|
module Yargi
|
4
|
-
|
5
|
-
# Current Yargi version
|
6
|
-
VERSION = "0.1.2".freeze
|
7
|
-
|
5
|
+
|
8
6
|
# When _what_ is not nil, converts it to a predicate (typically a module).
|
9
7
|
# Otherwise, a block is expected, which is converted to a LambdaPredicate.
|
10
8
|
# Otherwise, return ALL.
|
11
9
|
def self.predicate(what=nil, &block)
|
12
10
|
Predicate.to_predicate(what, &block)
|
13
11
|
end
|
14
|
-
|
12
|
+
|
15
13
|
# Predicates that always return true
|
16
14
|
ALL = Yargi::Predicate.to_predicate(true)
|
17
|
-
|
15
|
+
|
18
16
|
# Predicates that always return false
|
19
17
|
NONE = Yargi::Predicate.to_predicate(false)
|
20
|
-
|
21
|
-
end
|
22
18
|
|
19
|
+
end
|
23
20
|
require 'yargi/markable'
|
24
21
|
require 'yargi/digraph'
|
25
22
|
require 'yargi/digraph_vertex'
|
@@ -27,4 +24,5 @@ require 'yargi/digraph_edge'
|
|
27
24
|
require 'yargi/element_set'
|
28
25
|
require 'yargi/vertex_set'
|
29
26
|
require 'yargi/edge_set'
|
30
|
-
|
27
|
+
require 'yargi/decorate'
|
28
|
+
require 'yargi/random'
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Yargi
|
2
|
+
class Decorate
|
3
|
+
attr_accessor :key
|
4
|
+
attr_accessor :bottom
|
5
|
+
attr_accessor :d0
|
6
|
+
attr_accessor :suppremum
|
7
|
+
attr_accessor :propagate
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
yield(self) if block_given?
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute(digraph, initials = digraph.vertices(0))
|
14
|
+
# all to bottom except initial states
|
15
|
+
digraph.each_vertex{|s| s[key] = bottom}
|
16
|
+
initials.each{|s| s[key] = d0}
|
17
|
+
|
18
|
+
# main loop
|
19
|
+
to_explore = initials
|
20
|
+
until to_explore.empty?
|
21
|
+
source = to_explore.pop
|
22
|
+
source.out_edges.each do |edge|
|
23
|
+
target = edge.target
|
24
|
+
p_decor = propagate.call(source[key], edge)
|
25
|
+
p_decor = suppremum.call(target[key], p_decor)
|
26
|
+
unless p_decor == target[key]
|
27
|
+
target[key] = p_decor
|
28
|
+
to_explore << target unless to_explore.include?(target)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
DEPTH = Decorate.new{|d|
|
35
|
+
d.key = :depth
|
36
|
+
d.bottom = 1.0/0
|
37
|
+
d.d0 = 0
|
38
|
+
d.suppremum = lambda{|d1,d2| d1 < d2 ? d1 : d2}
|
39
|
+
d.propagate = lambda{|d,e| d+1}
|
40
|
+
}
|
41
|
+
|
42
|
+
SHORTEST_PATH = Decorate.new{|d|
|
43
|
+
d.key = :shortest_path
|
44
|
+
d.bottom = nil
|
45
|
+
d.d0 = []
|
46
|
+
d.suppremum = lambda{|d1,d2|
|
47
|
+
d1.nil? ? d2 : (d2.nil? ? d1 : (d1.size < d2.size ? d1 : d2))
|
48
|
+
}
|
49
|
+
d.propagate = lambda{|d,e|
|
50
|
+
d.nil? ? [e] : (d + [e])
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
end # class Decorate
|
55
|
+
end # module Yargi
|
data/lib/yargi/digraph.rb
CHANGED
@@ -6,12 +6,12 @@ module Yargi
|
|
6
6
|
#
|
7
7
|
# Directed graph implementation.
|
8
8
|
#
|
9
|
-
# * All selection methods (vertices, each_vertex, edges, each_edge) implement the
|
10
|
-
# predicate-selection mechanism (they accept a predicate filter as well as a
|
9
|
+
# * All selection methods (vertices, each_vertex, edges, each_edge) implement the
|
10
|
+
# predicate-selection mechanism (they accept a predicate filter as well as a
|
11
11
|
# predicate block, with a AND semantics when used conjointly).
|
12
12
|
# * Removal methods (remove_vertex, remove_vertices, remove_edge, remove_edges)
|
13
|
-
# accept varying arguments that are automatically converted to set of vertices
|
14
|
-
# or edges. Recognized arguments are Vertex and Edge instances, VertexSet and
|
13
|
+
# accept varying arguments that are automatically converted to set of vertices
|
14
|
+
# or edges. Recognized arguments are Vertex and Edge instances, VertexSet and
|
15
15
|
# EdgeSet instances, Array instances, and predicates. When multiple arguments
|
16
16
|
# are used conjointly, a OR-semantics is applied.
|
17
17
|
#
|
@@ -21,22 +21,33 @@ module Yargi
|
|
21
21
|
#
|
22
22
|
class Digraph
|
23
23
|
include Yargi::Markable
|
24
|
-
|
24
|
+
|
25
25
|
# Creates an empty graph instance
|
26
26
|
def initialize
|
27
27
|
@vertices = VertexSet[]
|
28
28
|
@edges = EdgeSet[]
|
29
29
|
@marks = {}
|
30
|
+
yield(self) if block_given?
|
30
31
|
end
|
31
|
-
|
32
|
+
|
32
33
|
### Vertex management ################################################
|
33
|
-
|
34
|
+
|
35
|
+
# Returns number of graph vertices
|
36
|
+
def vertex_count
|
37
|
+
@vertices.size
|
38
|
+
end
|
39
|
+
|
40
|
+
# Returns the i-th vertex
|
41
|
+
def ith_vertex(i)
|
42
|
+
@vertices[i]
|
43
|
+
end
|
44
|
+
|
34
45
|
# Returns all graph vertices for which the 'filter and block' predicate
|
35
46
|
# evaluates to true (see Yargi::Predicate).
|
36
47
|
def vertices(filter=nil, &block)
|
37
48
|
@vertices.filter(filter, &block)
|
38
49
|
end
|
39
|
-
|
50
|
+
|
40
51
|
# Calls block on each graph vertex for with the 'filter and block' predicate
|
41
52
|
# evaluates to true.
|
42
53
|
def each_vertex(filter=nil, &block)
|
@@ -46,20 +57,20 @@ module Yargi
|
|
46
57
|
vertices(filter).each &block
|
47
58
|
end
|
48
59
|
end
|
49
|
-
|
50
|
-
# Adds a vertex. _args_ can be module instances or hashes,
|
51
|
-
# which are all installed on the vertex _v_ using <tt>v.tag</tt>
|
52
|
-
# and <tt>v.add_marks</tt>, respectively.
|
60
|
+
|
61
|
+
# Adds a vertex. _args_ can be module instances or hashes,
|
62
|
+
# which are all installed on the vertex _v_ using <tt>v.tag</tt>
|
63
|
+
# and <tt>v.add_marks</tt>, respectively.
|
53
64
|
def add_vertex(*args)
|
54
65
|
vertex = Digraph::Vertex.new(self, @vertices.length)
|
55
66
|
apply_arg_conventions(vertex, args)
|
56
67
|
@vertices << vertex
|
57
68
|
vertex
|
58
69
|
end
|
59
|
-
|
60
|
-
# Creates n vertices. _args_ can be module instances or hashes,
|
61
|
-
# which are all installed on vertices _v_ using <tt>v.tag</tt>
|
62
|
-
# and <tt>v.add_marks</tt>, respectively. If a block is given,
|
70
|
+
|
71
|
+
# Creates n vertices. _args_ can be module instances or hashes,
|
72
|
+
# which are all installed on vertices _v_ using <tt>v.tag</tt>
|
73
|
+
# and <tt>v.add_marks</tt>, respectively. If a block is given,
|
63
74
|
# it is called after each vertex creation. The vertex is passed
|
64
75
|
# as first argument and the iteration index (from 0 to n-1) as
|
65
76
|
# second one.
|
@@ -72,8 +83,8 @@ module Yargi
|
|
72
83
|
end
|
73
84
|
VertexSet.new(vertices)
|
74
85
|
end
|
75
|
-
|
76
|
-
# Removes all vertices returned by evaluating the _vertices_ selection
|
86
|
+
|
87
|
+
# Removes all vertices returned by evaluating the _vertices_ selection
|
77
88
|
# expression.
|
78
89
|
def remove_vertices(*vertices)
|
79
90
|
vertices = to_vertices(*vertices).sort{|v1,v2| v2<=>v1}
|
@@ -85,16 +96,26 @@ module Yargi
|
|
85
96
|
@vertices.each_with_index {|v,i| v.index=i}
|
86
97
|
self
|
87
98
|
end
|
88
|
-
alias :remove_vertex :remove_vertices
|
89
|
-
|
99
|
+
alias :remove_vertex :remove_vertices
|
100
|
+
|
90
101
|
### Edge management ##################################################
|
91
|
-
|
102
|
+
|
103
|
+
# Returns number of graph edges
|
104
|
+
def edge_count
|
105
|
+
@edges.size
|
106
|
+
end
|
107
|
+
|
108
|
+
# Returns the i-th edge
|
109
|
+
def ith_edge(i)
|
110
|
+
@edges[i]
|
111
|
+
end
|
112
|
+
|
92
113
|
# Returns all graph edges for which the 'filter and block' predicate
|
93
114
|
# evaluates to true (see Yargi::Predicate).
|
94
115
|
def edges(filter=nil, &block)
|
95
116
|
@edges.filter(filter, &block)
|
96
117
|
end
|
97
|
-
|
118
|
+
|
98
119
|
# Calls block on each graph edge for with the 'filter and block' predicate
|
99
120
|
# evaluates to true.
|
100
121
|
def each_edge(filter=nil, &block)
|
@@ -104,10 +125,10 @@ module Yargi
|
|
104
125
|
edges(filter).each &block
|
105
126
|
end
|
106
127
|
end
|
107
|
-
|
108
|
-
# Connects source to target state(s). _source_ and _target_ may be any
|
109
|
-
# selection expression that can lead to vertex sets. _args_ can be module
|
110
|
-
# instances or hashes, which are all installed on edges _e_ using
|
128
|
+
|
129
|
+
# Connects source to target state(s). _source_ and _target_ may be any
|
130
|
+
# selection expression that can lead to vertex sets. _args_ can be module
|
131
|
+
# instances or hashes, which are all installed on edges _e_ using
|
111
132
|
# <tt>e.tag</tt> and <tt>e.add_marks</tt>, respectively.
|
112
133
|
def add_edge(source, target, *args)
|
113
134
|
if Vertex===source and Vertex===target
|
@@ -129,7 +150,7 @@ module Yargi
|
|
129
150
|
end
|
130
151
|
end
|
131
152
|
alias :connect :add_edge
|
132
|
-
|
153
|
+
|
133
154
|
# Adds many edges at once
|
134
155
|
def add_edges(*extremities)
|
135
156
|
extremities.collect do |extr|
|
@@ -137,8 +158,8 @@ module Yargi
|
|
137
158
|
end
|
138
159
|
end
|
139
160
|
alias :connect_all :add_edges
|
140
|
-
|
141
|
-
# Removes all edges returned by evaluating the _edges_ selection
|
161
|
+
|
162
|
+
# Removes all edges returned by evaluating the _edges_ selection
|
142
163
|
# expression.
|
143
164
|
def remove_edges(*edges)
|
144
165
|
edges = to_edges(edges).sort{|e1,e2| e2<=>e1}
|
@@ -152,9 +173,9 @@ module Yargi
|
|
152
173
|
self
|
153
174
|
end
|
154
175
|
alias :remove_edge :remove_edges
|
155
|
-
|
176
|
+
|
156
177
|
# Reconnects some edge(s). _source_ and _target_ are expected to be
|
157
|
-
# Vertex instances, or nil. _edges_ may be any selection expression
|
178
|
+
# Vertex instances, or nil. _edges_ may be any selection expression
|
158
179
|
# that can be converted to an edge set. This method reconnects all
|
159
180
|
# edges to the specified source and target vertices (at least one is
|
160
181
|
# expected not to be nil).
|
@@ -165,7 +186,7 @@ module Yargi
|
|
165
186
|
if source
|
166
187
|
edge.source.remove_out_edge(edge)
|
167
188
|
source.add_out_edge(edge)
|
168
|
-
end
|
189
|
+
end
|
169
190
|
if target
|
170
191
|
edge.target.remove_in_edge(edge)
|
171
192
|
target.add_in_edge(edge)
|
@@ -176,7 +197,7 @@ module Yargi
|
|
176
197
|
end
|
177
198
|
|
178
199
|
### Standard exports #################################################
|
179
|
-
|
200
|
+
|
180
201
|
# Encodes this graph for dot graphviz
|
181
202
|
def to_dot(buffer='')
|
182
203
|
buffer << "digraph G {\n"
|
@@ -189,10 +210,10 @@ module Yargi
|
|
189
210
|
end
|
190
211
|
buffer << "}\n"
|
191
212
|
end
|
192
|
-
|
213
|
+
|
193
214
|
### Argument conventions #############################################
|
194
215
|
protected
|
195
|
-
|
216
|
+
|
196
217
|
# Converts a hash to dot attributes
|
197
218
|
def to_dot_attributes(hash)
|
198
219
|
# TODO: fix uncompatible key names
|
@@ -214,36 +235,38 @@ module Yargi
|
|
214
235
|
end
|
215
236
|
buffer
|
216
237
|
end
|
217
|
-
|
238
|
+
|
218
239
|
# Checks if _arg_ looks like an element set
|
219
240
|
def looks_a_set?(arg)
|
220
241
|
Array===arg or ElementSet===arg
|
221
242
|
end
|
222
|
-
|
243
|
+
|
223
244
|
# Checks graph sanity
|
224
245
|
def check_sanity
|
225
|
-
@vertices.each_with_index do |v,i|
|
246
|
+
@vertices.each_with_index do |v,i|
|
226
247
|
raise "Removed vertex in vertex list" unless v.index==i
|
227
248
|
v.in_edges.each do |ine|
|
228
249
|
raise "Removed edge in vertex incoming edges" if ine.index<0
|
229
|
-
raise "Vertex and edge don't agree on target" unless ine.target==v
|
250
|
+
raise "Vertex and edge don't agree on target" unless ine.target==v
|
230
251
|
end
|
231
252
|
v.out_edges.each do |oute|
|
232
253
|
raise "Removed edge in vertex outgoing edges" if oute.index<0
|
233
|
-
raise "Vertex and edge don't agree on source" unless oute.source==v
|
254
|
+
raise "Vertex and edge don't agree on source" unless oute.source==v
|
234
255
|
end
|
235
256
|
end
|
236
|
-
@edges.each_with_index do |e,i|
|
257
|
+
@edges.each_with_index do |e,i|
|
237
258
|
raise "Removed edge in edge list" unless e.index==i
|
238
259
|
raise "Edge in-connected to a removed vertex" if e.source.index<0
|
239
260
|
raise "Edge out-connected to a removed vertex" if e.target.index<0
|
240
261
|
end
|
241
262
|
end
|
242
|
-
|
263
|
+
|
243
264
|
# Applies argument conventions about selection of vertices
|
244
265
|
def to_vertices(*args)
|
245
266
|
selected = args.collect do |arg|
|
246
267
|
case arg
|
268
|
+
when Integer
|
269
|
+
[@vertices[arg]]
|
247
270
|
when VertexSet
|
248
271
|
arg
|
249
272
|
when Array
|
@@ -257,11 +280,13 @@ module Yargi
|
|
257
280
|
end.flatten.uniq
|
258
281
|
VertexSet.new(selected)
|
259
282
|
end
|
260
|
-
|
283
|
+
|
261
284
|
# Applies argument conventions about selection of edges
|
262
285
|
def to_edges(*args)
|
263
286
|
selected = args.collect do |arg|
|
264
287
|
case arg
|
288
|
+
when Integer
|
289
|
+
[@edges[arg]]
|
265
290
|
when EdgeSet
|
266
291
|
arg
|
267
292
|
when Array
|
@@ -275,7 +300,7 @@ module Yargi
|
|
275
300
|
end.flatten.uniq
|
276
301
|
EdgeSet.new(selected)
|
277
302
|
end
|
278
|
-
|
303
|
+
|
279
304
|
# Applies argument conventions on _element_
|
280
305
|
def apply_arg_conventions(element, args)
|
281
306
|
args.each do |arg|
|
@@ -290,7 +315,7 @@ module Yargi
|
|
290
315
|
end
|
291
316
|
element
|
292
317
|
end
|
293
|
-
|
318
|
+
|
294
319
|
end # class Digraph
|
295
|
-
|
320
|
+
|
296
321
|
end
|