ruleby 0.1 → 0.2

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.
@@ -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