yargi 0.1.2 → 0.2.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.
- 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
|