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.
Files changed (54) hide show
  1. data/CHANGELOG.md +54 -0
  2. data/Gemfile +7 -0
  3. data/Gemfile.lock +23 -0
  4. data/LICENCE.md +22 -0
  5. data/Manifest.txt +15 -0
  6. data/README.md +128 -0
  7. data/Rakefile +23 -0
  8. data/examples/fs2dot.rb +1 -1
  9. data/examples/random.rb +20 -0
  10. data/lib/yargi.rb +8 -10
  11. data/lib/yargi/decorate.rb +55 -0
  12. data/lib/yargi/digraph.rb +71 -46
  13. data/lib/yargi/digraph_edge.rb +23 -23
  14. data/lib/yargi/digraph_vertex.rb +27 -27
  15. data/lib/yargi/edge_set.rb +12 -12
  16. data/lib/yargi/element_set.rb +54 -54
  17. data/lib/yargi/loader.rb +1 -0
  18. data/lib/yargi/markable.rb +12 -12
  19. data/lib/yargi/predicate.rb +62 -43
  20. data/lib/yargi/random.rb +98 -0
  21. data/lib/yargi/version.rb +14 -0
  22. data/lib/yargi/vertex_set.rb +12 -12
  23. data/spec/spec_helper.rb +2 -0
  24. data/spec/test_decorate.rb +28 -0
  25. data/spec/test_digraph.rb +42 -0
  26. data/spec/test_random.rb +45 -0
  27. data/spec/test_yargi.rb +8 -0
  28. data/tasks/debug_mail.rake +75 -0
  29. data/tasks/debug_mail.txt +13 -0
  30. data/tasks/gem.rake +68 -0
  31. data/tasks/spec_test.rake +71 -0
  32. data/tasks/unit_test.rake +76 -0
  33. data/tasks/yard.rake +51 -0
  34. data/test/test_all.rb +1 -1
  35. data/test/yargi/README-example.gif +0 -0
  36. data/test/yargi/digraph_set_features_test.rb +14 -16
  37. data/test/yargi/digraph_test.rb +33 -33
  38. data/test/yargi/digraph_vertex_test.rb +9 -9
  39. data/test/yargi/documentation_test.rb +7 -7
  40. data/test/yargi/edge_set_test.rb +3 -3
  41. data/test/yargi/element_set_test.rb +2 -2
  42. data/test/yargi/hypotheses_test.rb +4 -4
  43. data/test/yargi/markable_test.rb +11 -11
  44. data/test/yargi/predicate_test.rb +14 -14
  45. data/test/yargi/source-sink.gif +0 -0
  46. data/test/yargi/vertex_set_test.rb +7 -7
  47. data/yargi.gemspec +187 -0
  48. data/yargi.noespec +23 -0
  49. metadata +110 -38
  50. data/CONTRIBUTE +0 -11
  51. data/LICENCE +0 -25
  52. data/README +0 -79
  53. data/examples/fs2dot.dot +0 -78
  54. data/examples/fs2dot.gif +0 -0
@@ -0,0 +1 @@
1
+
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,14 @@
1
+ module Yargi
2
+ module Version
3
+
4
+ MAJOR = 0
5
+ MINOR = 2
6
+ TINY = 0
7
+
8
+ def self.to_s
9
+ [ MAJOR, MINOR, TINY ].join('.')
10
+ end
11
+
12
+ end
13
+ VERSION = Version.to_s
14
+ end
@@ -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)