ruleby 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -103,115 +103,5 @@ module Ruleby
103
103
  end
104
104
  end
105
105
 
106
- class MatchContext
107
- def initialize(fact_id=nil, match_results=[])
108
- @match_hash = DoubleHash.new fact_id, match_results
109
- end
110
-
111
- def clear
112
- @match_hash = DoubleHash.new
113
- end
114
-
115
- def add(ids,mr)
116
- @match_hash.add ids, mr
117
- end
118
-
119
- def add_uniq(ids,mr)
120
- @match_hash.add_uniq ids, mr
121
- end
122
-
123
- def each_match_result
124
- @match_hash.each do |ids,mr|
125
- yield(ids,mr)
126
- end
127
- end
128
-
129
- def each_fact_id
130
- @match_hash.each_id do |id|
131
- yield(id)
132
- end
133
- end
134
-
135
- def contains?(mr)
136
- return @match_hash.value?(mr)
137
- end
138
-
139
- def +(mc)
140
- # TODO make better
141
- new_mc = MatchContext.new
142
- new_mc.match_hash = @match_hash + mc.match_hash
143
- return new_mc
144
- end
145
-
146
- def concat(match_context)
147
- @match_hash.concat match_context.match_hash
148
- return self
149
- end
150
-
151
- def concat_uniq(match_context)
152
- @match_hash.concat_uniq match_context.match_hash
153
- return self
154
- end
155
-
156
- def default
157
- return @match_hash.default
158
- end
159
-
160
- def remove(id)
161
- return @match_hash.remove(id)
162
- end
163
-
164
- def delete_if
165
- @match_hash.delete_if do |mr|
166
- yield(mr)
167
- end
168
- end
169
-
170
- def match_results
171
- v = @match_hash.values
172
- if v.kind_of?(Hash)
173
- # QUESTION What the heck is going on here? Why did this return a Hash
174
- # instead of an Array?
175
- return v.values
176
- end
177
- return v
178
- end
179
-
180
- def dup
181
- dup_mc = MatchContext.new
182
- dup_mc.match_hash = @match_hash.dup
183
- return dup_mc
184
- end
185
-
186
- def subset(fact_id)
187
- mrs = @match_hash.values_by_id(fact_id)
188
- return MatchContext.new(fact_id, mrs.dup)
189
- end
190
-
191
- def satisfied?
192
- return !@match_hash.values.empty?
193
- end
194
-
195
- def to_s
196
- s = '[MatchContext('
197
- each_match_result do |ids,mr|
198
- s = s + "[#{ids.join(',')}->#{mr}]"
199
- end
200
- s = s + ')]'
201
- end
202
-
203
- def ==(mc)
204
- return false if mc == nil
205
- mc.match_hash.keys.each do |id|
206
- return false if @match_hash.keys.index(id) == nil
207
- end
208
- mc.match_results.values.each do |mr|
209
- return false if match_results.values.index(mr) == nil
210
- end
211
- return true
212
- end
213
-
214
- attr :match_hash, true
215
- end
216
106
  end
217
107
  end
@@ -1,100 +1,32 @@
1
1
  module Ruleby
2
2
  module Core
3
3
 
4
- # We use this custom structure for our data so that we can hash the nodes by
5
- # the type they represent. This allows for faster results because we can limit
6
- # the number of nodes we compare against. In addition, this class does some of
7
- # the compiling of the rules when a node is added.
8
- class NodeNetworkArray
9
- def initialize
10
- @nodes = {}
11
- end
12
-
13
- def each(clazz)
14
- nodes = @nodes[clazz]
15
- if nodes
16
- nodes.each do |n|
17
- yield n
18
- end
19
- end
20
- end
21
-
22
- def each_internal
23
- @nodes.values.flatten.each do |n|
24
- yield n
25
- end
26
- end
27
- private:each_internal
28
-
29
- def has_node(node)
30
- @nodes[node.pattern.head].each do |node2|
31
- if node2 == node
32
- return true
33
- end
34
- end
35
- return false
36
- end
37
-
38
- def add_internal(node)
39
- nodes = @nodes[node.pattern.head]
40
- nodes = [] unless nodes
41
- nodes.push node
42
- @nodes[node.pattern.head] = nodes
43
- end
44
- private:add_internal
45
-
46
- # This method adds a node to our list. In addition, it creates bindings between
47
- # this node and any nodes that it references or is referenced by
48
- def add(object_node)
49
- object_node.pattern.atoms.each do |atom|
50
- if (atom.kind_of?(ReferenceAtom))
51
- each_internal do |local_node|
52
- added = false
53
- atom.vars.each do |var|
54
- local_node.pattern.atoms.each do |local_atom|
55
- if (var == local_atom.tag)
56
- atom.var_atoms[var] = local_atom
57
- unless added
58
- local_node.referenced_by.push object_node
59
- added = true
60
- end
61
- end
62
- end
63
- end
64
- end
65
- end
66
- unless atom
67
- # TODO it would be better to throw this exception before the
68
- # the rule is asserted
69
- raise "Reference not found for: " + atom.tag.to_s
70
- end
71
- end
72
- add_internal(object_node);
73
- end
74
-
75
- end
76
-
77
4
  # This class is used when we need to have a Hash where keys and values are mapped
78
- # many-to-many. This class allows for quick access of both key and value.
79
- class DoubleHash
80
- def initialize(fact_id=nil, values=[])
5
+ # many-to-many. This class allows for quick access of both key and value. It
6
+ # is similar to Multimap in C++ standard lib.
7
+ class MultiHash
8
+ def initialize(key=nil, values=[])
81
9
  @i = 0
82
10
  clear
83
- if fact_id
84
- @fact_ids = {fact_id => []}
11
+ if key
12
+ @keys = {key => []}
85
13
  values.each do |v|
86
- key = generate_key()
87
- key_list = @fact_ids[fact_id]
88
- key_list.push key
89
- @fact_ids[fact_id] = key_list
90
- @values = {key => v}
91
- @backward_hash = {key => [fact_id]}
14
+ xref = generate_xref()
15
+ xref_list = @keys[key]
16
+ xref_list.push xref
17
+ @keys[key] = xref_list
18
+ @values = {xref => v}
19
+ @backward_hash = {xref => [key]}
92
20
  end
93
21
  end
94
22
  end
95
23
 
24
+ def empty?
25
+ return @keys.empty?
26
+ end
27
+
96
28
  def rehash
97
- @fact_ids.rehash
29
+ @keys.rehash
98
30
  @values.rehash
99
31
  @backward_hash.rehash
100
32
  end
@@ -104,16 +36,16 @@ module Ruleby
104
36
  end
105
37
 
106
38
  def clear
107
- @fact_ids = {}
39
+ @keys = {}
108
40
  @values = {}
109
41
  @backward_hash = {}
110
42
  end
111
43
 
112
44
  def values_by_id(id)
113
- keys = @fact_ids[id]
45
+ xrefs = @keys[id]
114
46
  values = []
115
- if keys
116
- keys.each do |k|
47
+ if xrefs
48
+ xrefs.each do |k|
117
49
  values.push @values[k]
118
50
  end
119
51
  else
@@ -122,95 +54,103 @@ module Ruleby
122
54
  return values
123
55
  end
124
56
 
125
- def each_id
126
- @fact_ids.each_key do |id|
127
- yield(id)
57
+ def each_key
58
+ @keys.each_key do |key|
59
+ yield(key)
128
60
  end
129
61
  end
130
62
 
131
- def has_id?(id)
132
- return @fact_ids.has_key?(id)
63
+ def has_key?(key)
64
+ return @keys.has_key?(key)
65
+ end
66
+
67
+ def key?(key)
68
+ return has_key?(key)
133
69
  end
134
70
 
135
71
  def +(dh)
136
- # TODO this can be much faster
72
+ # TODO this can be faster
137
73
  new_dh = dh.dup
138
74
  dh.concat self.dup
139
75
  return new_dh
140
76
  end
141
77
 
142
78
  def add(ids,val)
143
- key = generate_key()
79
+ xref = generate_xref()
144
80
  ids.each do |id|
145
- key_list = @fact_ids[id]
146
- key_list = [] if key_list == @fact_ids.default
147
- key_list.push key
148
- @fact_ids[id] = key_list
81
+ xref_list = @keys[id]
82
+ xref_list = [] if xref_list == @keys.default
83
+ xref_list.push xref
84
+ @keys[id] = xref_list
149
85
  end
150
- @values[key] = val
151
- @backward_hash[key] = ids
86
+ @values[xref] = val
87
+ @backward_hash[xref] = ids
152
88
  end
153
89
 
90
+ # WARN this method adds a value to the MultiHash only if it is unique. It
91
+ # can be a fairly costly operation, and should be avoided. We only
92
+ # implemented this as part of a hack to get things working early on.
154
93
  def add_uniq(ids,val)
155
- key = generate_key()
94
+ xref = generate_xref()
156
95
  exist_list = []
157
96
  ids.each do |id|
158
- key_list = @fact_ids[id]
159
- if key_list != @fact_ids.default
160
- key_list.each do |existing_key|
161
- existing_val = @values[existing_key]
97
+ xref_list = @keys[id]
98
+ if xref_list != @keys.default
99
+ xref_list.each do |existing_xref|
100
+ existing_val = @values[existing_xref]
162
101
  if existing_val
163
102
  if val == existing_val
164
- key = existing_key
103
+ xref = existing_xref
165
104
  exist_list.push id
166
105
  break
167
106
  end
168
107
  else
169
- # HACK there shouldn't be any keys like this in the
108
+ # HACK there shouldn't be any xrefs like this in the
170
109
  # hash to being with. Why are they there?
171
- key_list.delete(existing_key)
172
- @fact_ids[id] = key_list
110
+ xref_list.delete(existing_xref)
111
+ @keys[id] = xref_list
173
112
  end
174
113
  end
175
114
  end
176
115
  end
177
116
  add_list = ids - exist_list
178
117
  add_list.each do |id|
179
- key_list = @fact_ids[id]
180
- key_list = [] if key_list == @fact_ids.default
181
- key_list.push key
182
- @fact_ids[id] = key_list
118
+ xref_list = @keys[id]
119
+ xref_list = [] if xref_list == @keys.default
120
+ xref_list.push xref
121
+ @keys[id] = xref_list
183
122
  end
184
- @values[key] = val if exist_list.empty?
185
- b_list = @backward_hash[key]
123
+ @values[xref] = val if exist_list.empty?
124
+ b_list = @backward_hash[xref]
186
125
  if b_list
187
- @backward_hash[key] = b_list | ids
126
+ @backward_hash[xref] = b_list | ids
188
127
  else
189
- @backward_hash[key] = ids
128
+ @backward_hash[xref] = ids
190
129
  end
191
130
  end
192
131
 
193
132
  def each
194
- @values.each do |key,val|
195
- ids = @backward_hash[key]
133
+ @values.each do |xref,val|
134
+ ids = @backward_hash[xref]
196
135
  yield(ids,val)
197
136
  end
198
137
  end
199
138
 
200
139
  def each_internal
201
- @values.each do |key,val|
202
- ids = @backward_hash[key]
203
- yield(ids,key,val)
140
+ @values.each do |xref,val|
141
+ ids = @backward_hash[xref]
142
+ yield(ids,xref,val)
204
143
  end
205
144
  end
206
145
  private:each_internal
207
146
 
208
- def concat(double_hash)
209
- double_hash.each do |ids,val|
147
+ def concat(multi_hash)
148
+ multi_hash.each do |ids,val|
210
149
  add(ids,val)
211
150
  end
212
151
  end
213
152
 
153
+ # WARN see comments in add_uniq
214
154
  def concat_uniq(double_hash)
215
155
  double_hash.each do |ids,val|
216
156
  add_uniq(ids,val)
@@ -222,58 +162,58 @@ module Ruleby
222
162
  end
223
163
 
224
164
  def remove(id)
225
- key_list = @fact_ids.delete(id)
226
- if key_list != @fact_ids.default
165
+ xref_list = @keys.delete(id)
166
+ if xref_list != @keys.default
227
167
  removed_values = []
228
- key_list.each do |key|
229
- value = @values.delete(key)
168
+ xref_list.each do |xref|
169
+ value = @values.delete(xref)
230
170
  removed_values.push value
231
- id_list = @backward_hash.delete(key)
171
+ id_list = @backward_hash.delete(xref)
232
172
  id_list.each do |next_id|
233
- remove_internal(next_id,key) if next_id != id
173
+ remove_internal(next_id,xref) if next_id != id
234
174
  end
235
175
  end
236
176
  return removed_values
237
177
  else
238
- # puts 'WARN: tried to remove from DoubleHash where id does not exist'
178
+ # puts 'WARN: tried to remove from MultiHash where id does not exist'
239
179
  return default
240
180
  end
241
181
  end
242
182
 
243
- def remove_internal(id,key)
244
- key_list = @fact_ids[id]
245
- if key_list # BUG this shouldn't be nil!
246
- key_list.delete(key)
247
- if key_list.empty?
248
- @fact_ids.delete(id)
183
+ def remove_internal(id,xref)
184
+ xref_list = @keys[id]
185
+ if xref_list # BUG this shouldn't be nil!
186
+ xref_list.delete(xref)
187
+ if xref_list.empty?
188
+ @keys.delete(id)
249
189
  else
250
- @fact_ids[id] = key_list
190
+ @keys[id] = xref_list
251
191
  end
252
192
  end
253
193
  end
254
194
  private:remove_internal
255
195
 
256
- def remove_by_key(ids,key)
196
+ def remove_by_xref(ids,xref)
257
197
  ids.each do |id|
258
- key_list = @fact_ids[id]
259
- key_list.delete(key)
260
- if key_list.empty?
261
- @fact_ids.delete(id)
198
+ xref_list = @keys[id]
199
+ xref_list.delete(xref)
200
+ if xref_list.empty?
201
+ @keys.delete(id)
262
202
  else
263
- @fact_ids[id] = key_list
203
+ @keys[id] = xref_list
264
204
  end
265
205
  end
266
- @values.delete(key)
267
- @backward_hash.delete(key)
206
+ @values.delete(xref)
207
+ @backward_hash.delete(xref)
268
208
  end
269
- private:remove_by_key
209
+ private:remove_by_xref
270
210
 
271
211
  def delete_if
272
- @values.delete_if do |key,v|
212
+ @values.delete_if do |xref,v|
273
213
  if yield(v)
274
- id_list = @backward_hash.delete(key)
214
+ id_list = @backward_hash.delete(xref)
275
215
  id_list.each do |next_id|
276
- remove_internal(next_id,key)
216
+ remove_internal(next_id,xref)
277
217
  end
278
218
  true
279
219
  else
@@ -287,66 +227,66 @@ module Ruleby
287
227
  end
288
228
 
289
229
  def keys
290
- return @fact_ids.keys
230
+ return @keys.keys
291
231
  end
292
232
 
293
233
  def dup
294
- dup_mc = DoubleHash.new
234
+ dup_mc = MultiHash.new
295
235
  each do |ids,v|
296
236
  dup_mc.add ids, v.dup
297
237
  end
298
238
  return dup_mc
299
239
  end
300
240
 
301
- def generate_key()
241
+ def generate_xref()
302
242
  @i = @i + 1
303
243
  return @i
304
244
  end
305
- private:generate_key
245
+ private:generate_xref
306
246
 
307
247
  # This method is for testing. It ensures that all the Hash's
308
- # and Array's are in order, and not corrupted (ex. some fact_id points
309
- # to a key that does not exist in the match_results Hash).
248
+ # and Array's are in order, and not corrupted (ex. some key points
249
+ # to a xref that does not exist in the match_results Hash).
310
250
  def valid?
311
- @fact_ids.each do |id,keys|
312
- # key_list = @fact_ids[id]
313
- # if key_list != @fact_ids.default
314
- # key_list.each do |key|
315
- # id_list = @backward_hash[key]
251
+ @keys.each do |id,xrefs|
252
+ # xref_list = @keys[id]
253
+ # if xref_list != @keys.default
254
+ # xref_list.each do |xref|
255
+ # id_list = @backward_hash[xref]
316
256
  # unless id_list
317
257
  # puts 'yup'
318
258
  # return false
319
259
  # end
320
260
  # end
321
261
  # end
322
- keys.each do |key|
262
+ xrefs.each do |xref|
323
263
  count = 0
324
- keys.each do |key2|
325
- if key == key2
264
+ xrefs.each do |xref2|
265
+ if xref == xref2
326
266
  count = count + 1
327
267
  if count > 1
328
- puts '(0) Duplicate keys in entry for fact_ids'
268
+ puts '(0) Duplicate xrefs in entry for keys'
329
269
  return false
330
270
  end
331
271
  end
332
272
  end
333
273
 
334
- mr = @match_results[key]
274
+ mr = @match_results[xref]
335
275
  if mr == @match_results.default
336
- puts '(1) Missing entry in @match_results for key'
276
+ puts '(1) Missing entry in @match_results for xref'
337
277
  return false
338
278
  end
339
279
 
340
- # @match_results.each do |mr_key,other_mr|
341
- # if other_mr == mr && mr_key != key
280
+ # @match_results.each do |mr_xref,other_mr|
281
+ # if other_mr == mr && mr_xref != xref
342
282
  # puts '(1a) Duplicate entry in @match_results'
343
283
  # return false
344
284
  # end
345
285
  # end
346
286
 
347
- id_list = @backward_hash[key]
287
+ id_list = @backward_hash[xref]
348
288
  if id_list == @backward_hash.default
349
- puts '(2) Missing entry in backward_hash for key'
289
+ puts '(2) Missing entry in backward_hash for xref'
350
290
  return false
351
291
  end
352
292
 
@@ -357,16 +297,16 @@ module Ruleby
357
297
 
358
298
  id_list.each do |ref_id|
359
299
  unless ref_id == id
360
- ref_key_list = @fact_ids[ref_id]
361
- if ref_key_list == @fact_ids.default
362
- puts '(4) Missing entry in fact_ids for backward_hash id'
363
- puts "#{id},#{mr},#{key},#{ref_id}"
300
+ ref_xref_list = @keys[ref_id]
301
+ if ref_xref_list == @keys.default
302
+ puts '(4) Missing entry in keys for backward_hash id'
303
+ puts "#{id},#{mr},#{xref},#{ref_id}"
364
304
  return false
365
305
  end
366
306
 
367
- if ref_key_list.index(key) == nil
368
- puts '(5) Entry in fact_ids is missing key'
369
- puts "#{id},#{mr},#{key},#{ref_id}"
307
+ if ref_xref_list.index(xref) == nil
308
+ puts '(5) Entry in keys is missing xref'
309
+ puts "#{id},#{mr},#{xref},#{ref_id}"
370
310
  return false
371
311
  end
372
312
  end
@@ -381,10 +321,6 @@ module Ruleby
381
321
  # TODO need to implement this
382
322
  return super
383
323
  end
384
-
385
- attr_reader :fact_ids
386
- attr_reader :values
387
- attr_reader :backward_hash
388
324
  end
389
325
  end
390
326
  end