keso 0.1.3

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.
@@ -0,0 +1,151 @@
1
+
2
+ require 'set'
3
+
4
+ class ImmutableSet
5
+
6
+ def initialize(*args)
7
+
8
+ @set = Set.new;
9
+
10
+ args.each do |value|
11
+ if value.is_a? ImmutableSet
12
+ @set.merge value.set
13
+ elsif value.is_a? Set
14
+ @set.merge value
15
+ elsif value.is_a? Array
16
+ @set.merge value
17
+ elsif not value.nil?
18
+ @set.add value
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ def size
25
+ @set.size
26
+ end
27
+
28
+ alias :count :size
29
+
30
+ def each &block
31
+ @set.to_a.each do |value|
32
+ block.call value
33
+ end
34
+ end
35
+
36
+ def add *values
37
+
38
+ new_set = @set.clone
39
+
40
+ values.each do |value|
41
+ if value.is_a? ImmutableSet
42
+ new_set.merge(value.to_a)
43
+ elsif value.is_a? Set
44
+ new_set.merge(value)
45
+ elsif value.is_a? Array
46
+ new_set.merge(value)
47
+ else
48
+ new_set.add(value)
49
+ end
50
+ end
51
+
52
+ ImmutableSet.new(new_set)
53
+ end
54
+
55
+ def hash
56
+ @set.hash
57
+ end
58
+
59
+ def eql? other
60
+ self == other
61
+ end
62
+
63
+ def == other
64
+ if other.equal?(self)
65
+ true
66
+ elsif !self.class.equal?(other.class)
67
+ false
68
+ else
69
+ self.set.eql?(other.set)
70
+ end
71
+ end
72
+
73
+ def delete *values
74
+
75
+ new_set = @set.clone
76
+
77
+ values.each do |value|
78
+ if value.is_a? ImmutableSet
79
+ new_set.subtract(value.to_a)
80
+ elsif value.is_a? Set
81
+ new_set.subtract(value)
82
+ elsif value.is_a? Array
83
+ new_set.subtract(value)
84
+ else
85
+ new_set.delete(value)
86
+ end
87
+ end
88
+
89
+ ImmutableSet.new(new_set)
90
+ end
91
+
92
+ alias :remove :delete
93
+
94
+ def to_a
95
+ @set.to_a
96
+ end
97
+
98
+ def include? value
99
+ @set.include? value
100
+ end
101
+
102
+
103
+
104
+
105
+
106
+
107
+
108
+ def subset? other_set
109
+ self.set.subset?(other_set.set)
110
+ end
111
+
112
+ def superset? other_set
113
+ self.set.superset?(other_set.set)
114
+ end
115
+
116
+ alias :superset_of? :superset?
117
+
118
+ def proper_subset? other_set
119
+ self.set.proper_subset?(other_set.set)
120
+ end
121
+
122
+ alias :proper_subset_of? :proper_subset?
123
+
124
+ def proper_superset? other_set
125
+ self.set.proper_superset?(other_set.set)
126
+ end
127
+
128
+ alias :proper_superset_of? :proper_superset?
129
+
130
+ def union *values
131
+ self.add *values
132
+ end
133
+
134
+ def complement other_set
135
+ ImmutableSet.new(self.set - other_set.set)
136
+ end
137
+
138
+ def intersect other_set
139
+ ImmutableSet.new(self.set & other_set.set)
140
+ end
141
+
142
+
143
+
144
+ protected
145
+
146
+ def set
147
+ @set
148
+ end
149
+
150
+ end
151
+
@@ -0,0 +1,455 @@
1
+
2
+ #
3
+ # Subset,Superset
4
+ #
5
+ # Union, Intersection, Complement, Cartesian, join
6
+ # natrual_join: Joins two relations based on all common parts of there headers
7
+ #
8
+
9
+
10
+ #
11
+ # Ref
12
+ #
13
+ # http://en.wikipedia.org/wiki/Set_%28mathematics%29
14
+ # http://en.wikipedia.org/wiki/Relational_algebra
15
+ #
16
+ # Set methods
17
+ #
18
+ # r1.subset(r2) # boolean result
19
+ # r1.proper_subset(r2) # boolean result
20
+ # r1.superset(r2) # boolean result
21
+ # r1.proper_superset(r2) # boolean result
22
+ # r1.union(r2) # r1 + r2, set result
23
+ # r1.intersect(r2) # all in r1 that is also in r2, set result
24
+ # r1.complement(r2) # r1 - r2, set result
25
+
26
+
27
+
28
+ #
29
+ # Relational methods
30
+ #
31
+ # r1.project('Name','age') # results in a relation of only the supplied attributes, set result
32
+ # r1.project_all_but('Name','age') # results in a relation with all attributes exepct the supplied ones, set result
33
+ # r1.rename('Name','PersonName') # changes the attribute name from "Name" to "PersonName", set result
34
+ # r1.select('age' >= 18) # results in a raltion with where ('age' >= 18) is true, set result
35
+ # r1.join(r2) do |tupel| ... end
36
+ # r1.cartesian_product(r2) # new relation with r1 * r2 tuples with all attributes of r1 and r2, set result
37
+ # r1.natrual_join(r2) # cartesian_product with a selection based on equal attributes having to have equal value, set result
38
+ # r1.group
39
+ # r1.ungroup
40
+ # r1.summarize(:name) do |tuples,new_tuple|
41
+ # new_tuple.add('avrange_age',avg(tuples,'age'))
42
+ # new_tuple.add('max_age',max(tuples,'age'))
43
+ # new_tuple.add('min_age',min(tuples,'age'))
44
+ # new_tuple.add('median_age',median(tuples,'age'))
45
+ # end
46
+
47
+ #
48
+ # Add a to_keso to active record and also create a gem
49
+ #
50
+
51
+ # Tutorial D
52
+ #
53
+ # r1.project('Name','Age').group(['Age'],'Names') # results in a new relation as r{'Age' => integer, 'Names' => r{'Name' => String}}
54
+ # r1.project('Name','Age').group(['Age'],'Names').ungroup('Names'[,{'Name' => 'New_name'}]) # results in a new relation as r{'Age' => integer, 'Name' => String}
55
+ #
56
+
57
+ # A relation is a set of tupels constrainted to what can be described as a relational type by a heading, this means that
58
+ # the relation only takes tuples with the same heading as the relation.
59
+ # It has a body
60
+ # It has a heading
61
+ #
62
+ # All parts of the body must of the heading
63
+ #
64
+ #
65
+ #
66
+ #
67
+ #
68
+
69
+ class Relation
70
+
71
+ attr_reader :body, :heading
72
+
73
+ def initialize *value
74
+ value = value[0]
75
+ if value.is_a? Tuple
76
+ @heading = value.heading
77
+ @body = ImmutableSet.new value
78
+ elsif value.is_a? Heading
79
+ @heading = value
80
+ @body = ImmutableSet.new
81
+ elsif value.is_a? Relation
82
+ @heading = value.heading
83
+ @body = value.body
84
+ elsif value.is_a? Hash
85
+ @heading = Heading.new value
86
+ @body = ImmutableSet.new
87
+ elsif value.nil?
88
+ # oo... so you want an empty relation ?
89
+ @heading = Heading.new
90
+ @body = ImmutableSet.new
91
+ else
92
+ raise "Only accept Tuple,Heading,Relation or Hash types"
93
+ end
94
+ end
95
+
96
+ def add tuple
97
+ if tuple.is_a? Tuple
98
+ if tuple.heading == @heading
99
+ Relation.new(@heading).set_body(@body.add tuple)
100
+ else
101
+ throw 'its not of the same heading!'+", #{tuple.heading.inspect} != #{@heading.inspect}"
102
+ end
103
+ else
104
+ throw "Only tuples are supported"
105
+ end
106
+ end
107
+
108
+ def subset_of? other_relation
109
+ @body.subset? other_relation.get_body
110
+ end
111
+
112
+ def proper_subset_of? other_relation
113
+ @body.proper_subset_of? other_relation.get_body
114
+ end
115
+
116
+ def superset_of? other_relation
117
+ @body.superset_of? other_relation.get_body
118
+ end
119
+
120
+ def proper_superset_of? other_relation
121
+ @body.proper_superset_of? other_relation.get_body
122
+ end
123
+
124
+ def union other_relation
125
+ if self.heading == other_relation.heading
126
+ new_body = self.get_body.union(other_relation.get_body)
127
+ new_relation = Relation.new self.heading
128
+ new_relation.set_body new_body
129
+ return new_relation
130
+ else
131
+ throw "Not of the same heading"
132
+ end
133
+ end
134
+
135
+ def intersect other_relation
136
+ if self.heading == other_relation.heading
137
+ new_body = self.get_body.intersect other_relation.get_body
138
+ new_relation = Relation.new self.heading
139
+ new_relation.set_body new_body
140
+ return new_relation
141
+ else
142
+ throw "Not of the same heading"
143
+ end
144
+ end
145
+
146
+ def complement other_relation
147
+ if self.heading == other_relation.heading
148
+ new_body = self.get_body.complement other_relation.get_body
149
+ new_relation = Relation.new self.heading
150
+ new_relation.set_body new_body
151
+ return new_relation
152
+ else
153
+ throw "Not of the same heading"
154
+ end
155
+ end
156
+
157
+
158
+
159
+ def project *args
160
+
161
+ new_heading = Heading.new
162
+
163
+ args.each do |symbol|
164
+ if self.heading[symbol].nil?
165
+ throw "no attribute with the name #{symbol}"
166
+ end
167
+
168
+ new_heading = new_heading.add(self.heading[symbol])
169
+ end
170
+
171
+ if new_heading.count == 0
172
+ return Relation.new new_heading
173
+ end
174
+
175
+ new_relation = Relation.new new_heading
176
+ @body.each do |tuple|
177
+ new_tuple = Tuple.new
178
+
179
+ args.each do |symbol|
180
+ new_tuple = new_tuple.add(symbol => tuple[symbol])
181
+ end
182
+
183
+ new_relation = new_relation.add new_tuple
184
+ end
185
+
186
+
187
+
188
+ return new_relation
189
+ end
190
+
191
+ def project_all_but *args
192
+
193
+ new_heading = Heading.new self.heading
194
+
195
+ args.each do |symbol|
196
+ if self.heading[symbol].nil?
197
+ throw "no attribute with the name #{symbol}"
198
+ end
199
+
200
+ new_heading = new_heading.remove symbol
201
+ end
202
+
203
+ if new_heading.count == 0
204
+ return Relation.new new_heading
205
+ end
206
+
207
+ new_relation = Relation.new new_heading
208
+ @body.each do |tuple|
209
+ new_tuple = Tuple.new
210
+
211
+ new_heading.each do |attribute|
212
+ new_tuple = new_tuple.add(attribute => tuple[attribute.name])
213
+ end
214
+
215
+ new_relation = new_relation.add new_tuple
216
+ end
217
+
218
+ return new_relation
219
+ end
220
+
221
+ def rename from,to
222
+ new_relation = Relation.new self.heading.rename(from,to)
223
+ self.each do |tuple|
224
+ new_relation = new_relation.add tuple.rename(from,to)
225
+ end
226
+
227
+ new_relation
228
+ end
229
+
230
+
231
+
232
+ def count
233
+ @body.size
234
+ end
235
+
236
+ def size
237
+ self.count
238
+ end
239
+
240
+ def length
241
+ self.count
242
+ end
243
+
244
+ def each &block
245
+ @body.each do |value|
246
+ block.call value
247
+ end
248
+ end
249
+
250
+
251
+ def select &block
252
+ new_relation = Relation.new self.heading
253
+ self.each do |tuple|
254
+ new_relation = new_relation.add(tuple) if block.call(tuple)
255
+ end
256
+
257
+ new_relation
258
+ end
259
+
260
+
261
+ def join other_relation,&block
262
+ new_relation = Relation.new(self.heading.add(other_relation.heading))
263
+ self.each do |tuple|
264
+ other_relation.each do |tuple2|
265
+ tuple3 = tuple.add(tuple2)
266
+ new_relation = new_relation.add(tuple3) if block.call(tuple3)
267
+ end
268
+ end
269
+
270
+ new_relation
271
+ end
272
+
273
+ def cartesian_product other_relation
274
+ self.join other_relation do |tuple|
275
+ true
276
+ end
277
+ end
278
+
279
+ def natrual_join other_relation
280
+
281
+ the_same = []
282
+
283
+ self.heading.each do |attribute|
284
+ other_relation.heading.each do |attribute2|
285
+ if attribute == attribute2
286
+ the_same.push attribute
287
+ end
288
+ end
289
+ end
290
+
291
+ old_and_new_name = {}
292
+ the_same.each do |value|
293
+ other_relation = other_relation.rename(value.name,value.name+"_2")
294
+ old_and_new_name[value.name] = value.name+"_2"
295
+ end
296
+
297
+ to_return = self.join(other_relation) do |tuple|
298
+
299
+ r = true
300
+
301
+ old_and_new_name.each do |name,name2|
302
+ unless tuple[name] == tuple[name2]
303
+ r = false
304
+ end
305
+ end
306
+
307
+ r
308
+ end
309
+
310
+
311
+ to_return = to_return.project_all_but *(old_and_new_name.values)
312
+
313
+ to_return
314
+ end
315
+
316
+
317
+ def summarize column_names, &block
318
+
319
+ column_names = [column_names] unless column_names.is_a? Array
320
+
321
+ new_tuples = []
322
+
323
+ self.group(column_names,"temp_12345").each do |tuple|
324
+ new_tuples.push(block.call(tuple.remove("temp_12345"),tuple['temp_12345']))
325
+ end
326
+
327
+ to_return = Relation.new new_tuples.first.heading
328
+
329
+ new_tuples.each do |tuple|
330
+ to_return = to_return.add tuple
331
+ end
332
+
333
+ to_return
334
+ end
335
+
336
+
337
+ def ungroup column_name
338
+
339
+ new_tuples = []
340
+ self.each do |tuple|
341
+ inner_relation = tuple[column_name]
342
+ tuple = tuple.remove column_name
343
+ inner_relation.each do |inner_tuple|
344
+ temp = tuple
345
+ inner_tuple.each do |attribute,value|
346
+ temp = temp.add({attribute => value})
347
+ new_tuples.push(temp)
348
+ end
349
+ end
350
+ end
351
+
352
+ new_heading = self.heading.remove column_name
353
+ self.heading[column_name].type.each do |attribute|
354
+ new_heading = new_heading.add attribute
355
+ end
356
+
357
+ to_return = Relation.new new_heading
358
+ new_tuples.each do |tuple|
359
+ to_return = to_return.add tuple
360
+ end
361
+
362
+ to_return
363
+ end
364
+
365
+ def group column_names,new_column_name
366
+
367
+ # create the headings
368
+ new_heading = Heading.new
369
+ new_inner_heading = self.heading
370
+
371
+ column_names.each do |column_name|
372
+ throw "there is no #{column_name} in [#{self.heading.names.join(',')}]" if self.heading[column_name].nil?
373
+ new_heading = new_heading.add self.heading[column_name]
374
+ end
375
+
376
+ column_names.each do |column_name|
377
+ new_inner_heading = new_inner_heading.remove column_name
378
+ end
379
+
380
+ new_heading = new_heading.add(:name => new_column_name, :type => new_inner_heading)
381
+
382
+ # split the data
383
+ temp_data = {}
384
+
385
+ self.each do |tuple|
386
+ new_tuple = Tuple.new
387
+ new_inner_tuple = Tuple.new
388
+
389
+ new_heading.each do |attribute|
390
+ new_tuple = new_tuple.add(attribute => tuple[attribute]) unless attribute.name.to_s == new_column_name.to_s
391
+ end
392
+
393
+ new_inner_heading.each do |attribute|
394
+ new_inner_tuple = new_inner_tuple.add(attribute => tuple[attribute])
395
+ end
396
+
397
+ temp_data[new_tuple] ||= []
398
+ temp_data[new_tuple].push new_inner_tuple
399
+ end
400
+
401
+
402
+ # create the new realation
403
+ new_relation = Relation.new new_heading
404
+
405
+ temp_data.each do |tuple,array_of_tuples|
406
+
407
+ inner_relation = Relation.new new_inner_heading
408
+
409
+ array_of_tuples.each do |inner_tuple|
410
+ inner_relation = inner_relation.add(inner_tuple)
411
+ end
412
+
413
+ tuple = tuple.add(new_column_name => inner_relation)
414
+ new_relation = new_relation.add(tuple)
415
+ end
416
+
417
+ new_relation
418
+ end
419
+
420
+ def eql? other
421
+ self == other
422
+ end
423
+
424
+ def == other
425
+ if other.equal?(self)
426
+ true
427
+ elsif !self.class.equal?(other.class)
428
+ false
429
+ else
430
+ self.get_body.eql?(other.get_body)
431
+ end
432
+ end
433
+
434
+ def hash
435
+ self.get_body.hash
436
+ end
437
+
438
+ protected
439
+
440
+ def set_body body
441
+ @body = body
442
+ self
443
+ end
444
+
445
+ def get_body
446
+ @body
447
+ end
448
+
449
+
450
+ end
451
+
452
+
453
+
454
+
455
+