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/lib/yargi/loader.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
|
data/lib/yargi/markable.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
module Yargi
|
2
|
-
|
2
|
+
|
3
3
|
#
|
4
|
-
# Allows users to put its own marks on graph elements. A Hash-like API for setting
|
5
|
-
# and getting these marks is provided through <tt>[]</tt> and <tt>[]=</tt>. When a
|
6
|
-
# Symbol object is used as a mark key (and provided it does'nt lead to a name
|
7
|
-
# collision), accessors are automatically defined as singleton methods (without
|
4
|
+
# Allows users to put its own marks on graph elements. A Hash-like API for setting
|
5
|
+
# and getting these marks is provided through <tt>[]</tt> and <tt>[]=</tt>. When a
|
6
|
+
# Symbol object is used as a mark key (and provided it does'nt lead to a name
|
7
|
+
# collision), accessors are automatically defined as singleton methods (without
|
8
8
|
# creating an instance variable however).
|
9
9
|
#
|
10
10
|
module Markable
|
@@ -13,7 +13,7 @@ module Yargi
|
|
13
13
|
def tag(*modules)
|
14
14
|
modules.each {|mod| self.extend(mod)}
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
# Checks if a given mark exists
|
18
18
|
def has_mark?(key)
|
19
19
|
@marks and @marks.has_key?(key)
|
@@ -21,10 +21,10 @@ module Yargi
|
|
21
21
|
|
22
22
|
# Returns the mark value installed under _key_. Returns nil if no such mark.
|
23
23
|
def get_mark(key)
|
24
|
-
@marks ? @marks[key] : nil;
|
24
|
+
@marks ? @marks[key] : nil;
|
25
25
|
end
|
26
26
|
alias :[] :get_mark
|
27
|
-
|
27
|
+
|
28
28
|
# Sets a key/value pair as a mark. Overrides previous mark value if _key_ is
|
29
29
|
# already in used. Automatically creates accessors if _key_ is a Symbol and such
|
30
30
|
# methods do not already exists.
|
@@ -41,7 +41,7 @@ module Yargi
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
alias :[]= :set_mark
|
44
|
-
|
44
|
+
|
45
45
|
# Add all marks provided by a Hash instance _marks_.
|
46
46
|
def add_marks(marks=nil)
|
47
47
|
marks.each_pair {|k,v| self[k]=v} if marks
|
@@ -51,7 +51,7 @@ module Yargi
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
alias :merge_marks :add_marks
|
54
|
-
|
54
|
+
|
55
55
|
# Converts this Markable to a Hash. When _nonil_ is true, nil mark values
|
56
56
|
# do not lead to hash entries.
|
57
57
|
def to_h(nonil=true)
|
@@ -62,7 +62,7 @@ module Yargi
|
|
62
62
|
end
|
63
63
|
marks
|
64
64
|
end
|
65
|
-
|
65
|
+
|
66
66
|
end # module Markable
|
67
|
-
|
67
|
+
|
68
68
|
end
|
data/lib/yargi/predicate.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Yargi
|
2
|
-
|
2
|
+
|
3
3
|
#
|
4
4
|
# Predicate for graph elements.
|
5
5
|
#
|
@@ -13,7 +13,7 @@ module Yargi
|
|
13
13
|
# [otherwise] an ArgumentError is raised.
|
14
14
|
#
|
15
15
|
class Predicate
|
16
|
-
|
16
|
+
|
17
17
|
class << self
|
18
18
|
# Builds a 'what and block' predicate, according the automatic conversions
|
19
19
|
# descibed above.
|
@@ -30,6 +30,8 @@ module Yargi
|
|
30
30
|
ALL
|
31
31
|
when FalseClass
|
32
32
|
NONE
|
33
|
+
when Integer
|
34
|
+
IndexPredicate.new(what)
|
33
35
|
else
|
34
36
|
raise ArgumentError, "Unable to convert #{what} to a predicate"
|
35
37
|
end
|
@@ -40,136 +42,136 @@ module Yargi
|
|
40
42
|
end
|
41
43
|
end
|
42
44
|
end
|
43
|
-
|
45
|
+
|
44
46
|
# Builds a 'self and right' predicate
|
45
47
|
def &(right)
|
46
48
|
AndPredicate.new(*[self, Predicate.to_predicate(right)])
|
47
49
|
end
|
48
|
-
|
50
|
+
|
49
51
|
# Builds a 'self or right' predicate
|
50
52
|
def |(right)
|
51
53
|
OrPredicate.new(*[self, Predicate.to_predicate(right)])
|
52
54
|
end
|
53
|
-
|
55
|
+
|
54
56
|
# Builds a 'not(self)' predicate
|
55
57
|
def not()
|
56
58
|
NotPredicate.new(self)
|
57
59
|
end
|
58
|
-
|
60
|
+
|
59
61
|
end # class Predicate
|
60
62
|
|
61
63
|
# Decorates _true_ as a predicate
|
62
64
|
class TruePredicate < Predicate
|
63
|
-
|
65
|
+
|
64
66
|
# Always returns true
|
65
67
|
def ===(elm)
|
66
68
|
true
|
67
69
|
end
|
68
|
-
|
70
|
+
|
69
71
|
# Returns 'true'
|
70
72
|
def inspect
|
71
73
|
"true"
|
72
74
|
end
|
73
|
-
|
75
|
+
|
74
76
|
end # class TruePredicate
|
75
77
|
|
76
78
|
# Decorates _false_ as a predicate
|
77
79
|
class FalsePredicate < Predicate
|
78
|
-
|
80
|
+
|
79
81
|
# Always returns false
|
80
82
|
def ===(elm)
|
81
83
|
false
|
82
84
|
end
|
83
|
-
|
85
|
+
|
84
86
|
# Returns 'false'
|
85
87
|
def inspect
|
86
88
|
"false"
|
87
89
|
end
|
88
|
-
|
90
|
+
|
89
91
|
end # class TruePredicate
|
90
92
|
|
91
93
|
# Decorates a module instance as a predicate
|
92
94
|
class TagPredicate < Predicate
|
93
|
-
|
95
|
+
|
94
96
|
# Creates a Tag predicate
|
95
97
|
def initialize(mod)
|
96
98
|
raise ArgumentError, "Module expected, #{mod} received" unless Module===mod
|
97
99
|
@mod = mod
|
98
100
|
end
|
99
|
-
|
101
|
+
|
100
102
|
# Predicate implementation
|
101
103
|
def ===(elm)
|
102
104
|
@mod===elm
|
103
105
|
end
|
104
|
-
|
106
|
+
|
105
107
|
# Helps debugging predicates
|
106
108
|
def inspect
|
107
109
|
"is_a?(#{@mod})"
|
108
110
|
end
|
109
|
-
|
111
|
+
|
110
112
|
end # class TagPredicate
|
111
|
-
|
113
|
+
|
112
114
|
# Decorates a block as a predicate
|
113
115
|
class LambdaPredicate < Predicate
|
114
|
-
|
116
|
+
|
115
117
|
# Creates a Tag predicate
|
116
118
|
def initialize(&block)
|
117
119
|
raise ArgumentError, "Block of arity 1 expected" unless (block and block.arity==1)
|
118
120
|
@block = block
|
119
121
|
end
|
120
|
-
|
122
|
+
|
121
123
|
# Predicate implementation
|
122
124
|
def ===(elm)
|
123
125
|
@block.call(elm)
|
124
126
|
end
|
125
|
-
|
127
|
+
|
126
128
|
# Helps debugging predicates
|
127
129
|
def inspect
|
128
130
|
"lambda{...}"
|
129
131
|
end
|
130
|
-
|
132
|
+
|
131
133
|
end # class LambdaPredicate
|
132
|
-
|
134
|
+
|
133
135
|
# Negates another predicate
|
134
136
|
class NotPredicate < Predicate
|
135
|
-
|
137
|
+
|
136
138
|
# Creates a 'not(negated)' predicate
|
137
139
|
def initialize(negated)
|
138
140
|
@negated = Predicate.to_predicate(negated)
|
139
141
|
end
|
140
|
-
|
142
|
+
|
141
143
|
# Predicate implementation
|
142
144
|
def ===(elm)
|
143
145
|
not(@negated === elm)
|
144
146
|
end
|
145
|
-
|
147
|
+
|
146
148
|
# Helps debugging predicates
|
147
149
|
def inspect
|
148
150
|
"not(#{@negated})"
|
149
151
|
end
|
150
|
-
|
152
|
+
|
151
153
|
end # class NotPredicate
|
152
|
-
|
154
|
+
|
153
155
|
# Conjunction predicate
|
154
156
|
class AndPredicate < Predicate
|
155
|
-
|
157
|
+
|
156
158
|
# Builds a AND predicate
|
157
159
|
def initialize(*anded)
|
158
160
|
raise ArgumentError, "Predicates expected" unless anded.all?{|p| Predicate===p}
|
159
161
|
@anded = anded
|
160
162
|
end
|
161
|
-
|
163
|
+
|
162
164
|
# Predicate implementation
|
163
165
|
def ===(elm)
|
164
166
|
@anded.all?{|p| p===elm}
|
165
167
|
end
|
166
|
-
|
168
|
+
|
167
169
|
# Pushes _right_ in the anded array
|
168
170
|
def &(right)
|
169
171
|
@anded << Predicate.to_predicate(right)
|
170
172
|
self
|
171
173
|
end
|
172
|
-
|
174
|
+
|
173
175
|
# Helps debugging predicates
|
174
176
|
def inspect
|
175
177
|
@anded.inject('') do |memo,p|
|
@@ -180,29 +182,29 @@ module Yargi
|
|
180
182
|
end
|
181
183
|
end
|
182
184
|
end
|
183
|
-
|
185
|
+
|
184
186
|
end # class AndPredicate
|
185
|
-
|
187
|
+
|
186
188
|
# Disjunction predicate
|
187
189
|
class OrPredicate < Predicate
|
188
|
-
|
190
|
+
|
189
191
|
# Builds a OR predicate
|
190
192
|
def initialize(*ored)
|
191
193
|
raise ArgumentError, "Predicates expected" unless ored.all?{|p| Predicate===p}
|
192
194
|
@ored = ored
|
193
195
|
end
|
194
|
-
|
196
|
+
|
195
197
|
# Predicate implementation
|
196
198
|
def ===(elm)
|
197
199
|
@ored.any?{|p| p===elm}
|
198
200
|
end
|
199
|
-
|
201
|
+
|
200
202
|
# Pushes _right_ in the ored array
|
201
203
|
def |(right)
|
202
204
|
@ored << Predicate.to_predicate(right)
|
203
205
|
self
|
204
206
|
end
|
205
|
-
|
207
|
+
|
206
208
|
# Helps debugging predicates
|
207
209
|
def inspect
|
208
210
|
@ored.inject('') do |memo,p|
|
@@ -213,17 +215,34 @@ module Yargi
|
|
213
215
|
end
|
214
216
|
end
|
215
217
|
end
|
216
|
-
|
218
|
+
|
217
219
|
end # class OrPredicate
|
218
|
-
|
220
|
+
|
221
|
+
# Index predicate
|
222
|
+
class IndexPredicate < Predicate
|
223
|
+
|
224
|
+
def initialize(index)
|
225
|
+
@index = index
|
226
|
+
end
|
227
|
+
|
228
|
+
def ===(elm)
|
229
|
+
elm.index == @index
|
230
|
+
end
|
231
|
+
|
232
|
+
def inspect
|
233
|
+
"elm.index == #{index}"
|
234
|
+
end
|
235
|
+
|
236
|
+
end # class IndexPredicate
|
237
|
+
|
219
238
|
class Predicate
|
220
|
-
|
239
|
+
|
221
240
|
# Predicates that always return true
|
222
241
|
ALL = TruePredicate.new
|
223
|
-
|
242
|
+
|
224
243
|
# Predicates that always return false
|
225
244
|
NONE = FalsePredicate.new
|
226
|
-
|
245
|
+
|
227
246
|
end # class Predicate
|
228
|
-
|
247
|
+
|
229
248
|
end
|
data/lib/yargi/random.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
module Yargi
|
2
|
+
|
3
|
+
#
|
4
|
+
# Random graph generator
|
5
|
+
#
|
6
|
+
class Random
|
7
|
+
|
8
|
+
# @return [Integer] number of vertices to generate
|
9
|
+
attr_accessor :vertex_count
|
10
|
+
|
11
|
+
# @return [Proc] proc to call on vertex generation
|
12
|
+
attr_accessor :vertex_builder
|
13
|
+
|
14
|
+
# @return [Integer] number of edges to generate
|
15
|
+
attr_accessor :edge_count
|
16
|
+
|
17
|
+
# @return [Proc] proc to call on vertex generation
|
18
|
+
attr_accessor :edge_builder
|
19
|
+
|
20
|
+
# @returns [Boolean] strip the generated graph to one connex component
|
21
|
+
# reachable from the first vertex?
|
22
|
+
attr_accessor :strip
|
23
|
+
|
24
|
+
# Creates an algorithm instance
|
25
|
+
def initialize
|
26
|
+
@vertex_count = 50
|
27
|
+
@vertex_builder = nil
|
28
|
+
@edge_count = 150
|
29
|
+
@edge_builder = nil
|
30
|
+
@strip = true
|
31
|
+
yield(self) if block_given?
|
32
|
+
end
|
33
|
+
|
34
|
+
# Executes the random generation
|
35
|
+
def execute
|
36
|
+
graph = Digraph.new{|g|
|
37
|
+
vertex_count.times do |i|
|
38
|
+
vertex = g.add_vertex
|
39
|
+
vertex_builder.call(vertex,i) if vertex_builder
|
40
|
+
end
|
41
|
+
edge_count.times do |i|
|
42
|
+
source = g.ith_vertex(Kernel.rand(vertex_count))
|
43
|
+
target = g.ith_vertex(Kernel.rand(vertex_count))
|
44
|
+
edge = g.connect(source, target)
|
45
|
+
edge_builder.call(edge,i) if edge_builder
|
46
|
+
end
|
47
|
+
}
|
48
|
+
strip ? _strip(graph) : graph
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
|
53
|
+
def _strip(graph)
|
54
|
+
Decorate::DEPTH.execute(graph)
|
55
|
+
graph.remove_vertices graph.vertices{|v| v[:depth] == 1.0/0}
|
56
|
+
graph
|
57
|
+
end
|
58
|
+
|
59
|
+
end # class Random
|
60
|
+
|
61
|
+
class Digraph
|
62
|
+
|
63
|
+
#
|
64
|
+
# Generates a random graph.
|
65
|
+
#
|
66
|
+
# @param [Integer] vertex_count number of vertices to generate
|
67
|
+
# @param [Integer] edge_count number of edges to generate
|
68
|
+
# @param [Integer] strip strip the graph (reachability for first vertex)
|
69
|
+
# @return [Digraph] a directed graph randomly generated
|
70
|
+
#
|
71
|
+
# Example:
|
72
|
+
#
|
73
|
+
# # Generate a random graph with 20 vertices
|
74
|
+
# # and 60 edges exactly (no stripping)
|
75
|
+
# Digraph.random(20,60,false)
|
76
|
+
#
|
77
|
+
# # Even more powerful, r is a Digraph::Random
|
78
|
+
# Digraph.random do |r|
|
79
|
+
# r.vertex_count = 20
|
80
|
+
# r.edge_count = 60
|
81
|
+
# r.strip = false
|
82
|
+
# r.vertex_builder = lambda{|v,i| ... } # as digraph.add_n_vertices
|
83
|
+
# r.edge_builder = lambda{|e,i| ... } # similarl
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
def self.random(vertex_count = nil, edge_count = nil, strip = nil)
|
87
|
+
r = Random.new{|rand|
|
88
|
+
rand.vertex_count = vertex_count if vertex_count
|
89
|
+
rand.edge_count = edge_count if edge_count
|
90
|
+
rand.strip = strip if strip
|
91
|
+
}
|
92
|
+
yield(r) if block_given?
|
93
|
+
r.execute
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
end # module Yargi
|
data/lib/yargi/vertex_set.rb
CHANGED
@@ -1,30 +1,30 @@
|
|
1
1
|
module Yargi
|
2
|
-
|
2
|
+
|
3
3
|
# A set of vertices
|
4
4
|
class VertexSet < ElementSet
|
5
|
-
|
5
|
+
|
6
6
|
### Factory section #######################################################
|
7
|
-
|
7
|
+
|
8
8
|
# Creates a VertexSet instance using _elements_ varargs.
|
9
9
|
def self.[](*elements)
|
10
10
|
VertexSet.new(elements)
|
11
11
|
end
|
12
|
-
|
13
|
-
|
12
|
+
|
13
|
+
|
14
14
|
### Walking section #######################################################
|
15
|
-
|
15
|
+
|
16
16
|
# Returns incoming edges of all vertices of this set
|
17
17
|
def in_edges(filter=nil, &block)
|
18
18
|
r = self.collect {|v| v.in_edges(filter, &block) }
|
19
19
|
EdgeSet.new(r).flatten.uniq
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
# Returns all back-adjacent vertices reachable from this set
|
23
23
|
def in_adjacent(filter=nil, &block)
|
24
24
|
r = self.collect {|v| v.in_adjacent(filter, &block) }
|
25
25
|
VertexSet.new(r).flatten.uniq
|
26
26
|
end
|
27
|
-
|
27
|
+
|
28
28
|
# Returns all outgoing edges of all vertices of this set
|
29
29
|
def out_edges(filter=nil, &block)
|
30
30
|
r = self.collect {|v| v.out_edges(filter, &block) }
|
@@ -36,16 +36,16 @@ module Yargi
|
|
36
36
|
r = self.collect {|v| v.out_adjacent(filter, &block) }
|
37
37
|
VertexSet.new(r).flatten.uniq
|
38
38
|
end
|
39
|
-
|
39
|
+
|
40
40
|
# Returns all adjacent vertices reachable from this set
|
41
41
|
def adjacent(filter=nil, &block)
|
42
42
|
(in_adjacent(filter, &block)+out_adjacent(filter, &block)).uniq
|
43
43
|
end
|
44
|
-
|
45
|
-
|
44
|
+
|
45
|
+
|
46
46
|
### Protected section #####################################################
|
47
47
|
protected
|
48
|
-
|
48
|
+
|
49
49
|
# Extends with VertexSet instead of ElementSet
|
50
50
|
def extend_result(result)
|
51
51
|
VertexSet.new(result)
|