yargi 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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)