cantor 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,174 @@
1
+ module Cantor
2
+
3
+ #
4
+ # {AbstractSet} describes the common interface implemented by its concrete
5
+ # subclasses. The two main implementations are {RelativeSet} and
6
+ # {AbsoluteSet} which are each optimized for different kinds of set
7
+ # operations.
8
+ #
9
+ class AbstractSet
10
+
11
+ # True if the set includes the given `object`
12
+ # abstract :include?, :args => %w(object)
13
+
14
+ def member?(object)
15
+ include?(object)
16
+ end
17
+
18
+ def contain?(object)
19
+ include?(object)
20
+ end
21
+
22
+ # True if the set does not include the given `object`
23
+ def exclude?(object)
24
+ not include?(object)
25
+ end
26
+
27
+ # True if {#size} `< Infinity`
28
+ # abstract :finite?
29
+
30
+ # True if {#size} `== 0`
31
+ # abstract :empty?
32
+
33
+ # Returns the number of elements in the set
34
+ #
35
+ # @return [Numeric]
36
+ # abstract :size
37
+
38
+ # Returns the `other` set, converting it to an {AbstractSet} if it isn't
39
+ # already.
40
+ #
41
+ # @return [AbstractSet]
42
+ # abstract :replace, :args => %w(other)
43
+
44
+ # True if the set contains infinitely many elements
45
+ def infinite?
46
+ not finite?
47
+ end
48
+
49
+ # @group Set Operations
50
+ #########################################################################
51
+
52
+ # (see AbstractSet#~)
53
+ # abstract :complement
54
+
55
+ # (see AbstractSet#|)
56
+ # abstract :union, :args => %w(other)
57
+
58
+ # (see AbstractSet#-)
59
+ # abstract :difference, :args => %w(other)
60
+
61
+ # (see AbstractSet#^)
62
+ # abstract :symmetric_difference, :args => %w(other)
63
+
64
+ # (see AbstractSet#&)
65
+ # abstract :intersection, :args => %w(other)
66
+
67
+ # Computes the union of two sets: the set of elements in _either_ or
68
+ # _both_ sets
69
+ #
70
+ # @return [AbstractSet]
71
+ #
72
+ # @see http://en.wikipedia.org/wiki/Union_(set_theory)
73
+ def |(other) union(other) end
74
+
75
+ # (see AbstractSet#|)
76
+ def +(other) union(other) end
77
+
78
+ # Computes the difference of two sets: the set of elements elements in
79
+ # _this_ set and not the _other_.
80
+ #
81
+ # @return [AbstractSet]
82
+ #
83
+ # @see http://en.wikipedia.org/wiki/Set_difference#Relative_complement
84
+ def -(other) difference(other) end
85
+
86
+ # Computes the complement of the set: the set of elements _not_ in this
87
+ # set
88
+ #
89
+ # @return [AbstractSet]
90
+ #
91
+ # @see http://en.wikipedia.org/wiki/Complement_(set_theory)
92
+ def ~; complement end
93
+
94
+ # Computes the symmetric difference of two sets: the set of elements which
95
+ # are in _either_ of the two sets but _not_ in both.
96
+ #
97
+ # @return [AbstractSet]
98
+ #
99
+ # @see http://en.wikipedia.org/wiki/Symmetric_difference
100
+ def ^(other) symmetric_difference(other) end
101
+
102
+ # Computes the intersection of two sets: the set of elements common
103
+ # between _both_ sets.
104
+ #
105
+ # @return [AbstractSet]
106
+ #
107
+ # @see http://en.wikipedia.org/wiki/Intersection_(set_theory)
108
+ def &(other) intersection(other) end
109
+
110
+ # @endgroup
111
+ #########################################################################
112
+
113
+ # @group Set Ordering
114
+ #########################################################################
115
+
116
+ # True if every element in this set also belongs to the `other` set
117
+ #
118
+ # @see http://en.wikipedia.org/wiki/Subset
119
+ def subset?(other)
120
+ intersection(other) == self
121
+ end
122
+
123
+ # True if this set is a subset of the `other` set and there exists at
124
+ # least one element in the `other` set that doesn't belong to this set
125
+ #
126
+ # @see http://en.wikipedia.org/wiki/Subset
127
+ def proper_subset?(other)
128
+ other.size > size and subset?(other)
129
+ end
130
+
131
+ # True if every element in the `other` set also belongs to this set
132
+ #
133
+ # @see http://en.wikipedia.org/wiki/Subset
134
+ def superset?(other)
135
+ intersection(other) == other
136
+ end
137
+
138
+ # True if this set is a superset of the `other` set and there exists at
139
+ # least one element in this set that doesn't belong to the `other` set
140
+ #
141
+ # @see http://en.wikipedia.org/wiki/Subset
142
+ def proper_superset?(other)
143
+ other.size < size and superset?(other)
144
+ end
145
+
146
+ # True if this and the `other` set have no common elements
147
+ #
148
+ # @see http://en.wikipedia.org/wiki/Disjoint_sets
149
+ def disjoint?(other)
150
+ intersection(other).empty?
151
+ end
152
+
153
+ # True if this and the `other` set have exactly the same elements
154
+ #
155
+ # @return [Boolean]
156
+ # abstract :==, :args => %w(other)
157
+
158
+ # (see AbstractSet#proper_subset?)
159
+ def <(other) proper_subset?(other) end
160
+
161
+ # (see AbstractSet#proper_superset?)
162
+ def >(other) proper_superset?(other) end
163
+
164
+ # (see AbstractSet#subset?)
165
+ def <=(other) subset?(other) end
166
+
167
+ # (see AbstractSet#superset?)
168
+ def >=(other) superset?(other) end
169
+
170
+ # @endgroup
171
+ #########################################################################
172
+ end
173
+
174
+ end
@@ -0,0 +1,123 @@
1
+ # encoding: UTF-8
2
+
3
+ module Cantor
4
+
5
+ # @private
6
+ NullSet = Class.new(RelativeSet) do
7
+
8
+ def initialize
9
+ end
10
+
11
+ # @return [NullSet]
12
+ def build
13
+ self
14
+ end
15
+
16
+ # Yields each element in the set to the implicit block argument. Since
17
+ # there are no elements, the block is never executed
18
+ #
19
+ # @return [void]
20
+ def each
21
+ end
22
+
23
+ # Returns an {Array} containing each element in this set. Since there are
24
+ # no elements, the {Array} is empty
25
+ #
26
+ # @return [Array]
27
+ def to_a
28
+ []
29
+ end
30
+
31
+ # @return false
32
+ def include?(other)
33
+ false
34
+ end
35
+
36
+ # @return true
37
+ def finite?
38
+ true
39
+ end
40
+
41
+ # @return [Set] other
42
+ def replace(other)
43
+ Cantor.build(other)
44
+ end
45
+
46
+ # @return 0
47
+ def size
48
+ 0
49
+ end
50
+
51
+ # @return true
52
+ def empty?
53
+ true
54
+ end
55
+
56
+ # @group Set Operations
57
+ #########################################################################
58
+
59
+ # @return self
60
+ def map
61
+ self
62
+ end
63
+
64
+ # @return self
65
+ def select
66
+ self
67
+ end
68
+
69
+ # @return self
70
+ def reject
71
+ self
72
+ end
73
+
74
+ # @return UniversalSet
75
+ def complement
76
+ # ¬∅ = U
77
+ UniversalSet.build
78
+ end
79
+
80
+ # @return self
81
+ def intersection(other)
82
+ # ∅ ∩ A = ∅
83
+ self
84
+ end
85
+
86
+ # @return [Set] other
87
+ def union(other)
88
+ # ∅ ∪ A = A
89
+ Cantor.build(other)
90
+ end
91
+
92
+ # @return self
93
+ def difference(other)
94
+ # ∅ ∖ A = A
95
+ self
96
+ end
97
+
98
+ # @return [Set] other
99
+ def symmetric_difference(other)
100
+ # ∅ ⊖ A = A
101
+ Cantor.build(other)
102
+ end
103
+
104
+ # @group Set Ordering
105
+ #########################################################################
106
+
107
+ def ==(other)
108
+ eql?(other) or other.empty?
109
+ end
110
+
111
+ # @group Pretty Printing
112
+ #########################################################################
113
+
114
+ # @return [String]
115
+ def inspect
116
+ "NullSet"
117
+ end
118
+
119
+ # @endgroup
120
+ #########################################################################
121
+ end.new
122
+
123
+ end
@@ -0,0 +1,135 @@
1
+ # encoding: UTF-8
2
+
3
+ module Cantor
4
+
5
+ #
6
+ # This data type is an infinite and non-enumerable set of values that
7
+ # encodes the complement of a {RelativeSet}
8
+ #
9
+ class RelativeComplement < AbstractSet
10
+
11
+ def initialize(complement)
12
+ @complement = complement
13
+ end
14
+
15
+ # (see AbstractSet#include?)
16
+ def include?(object)
17
+ not @complement.include?(object)
18
+ end
19
+
20
+ # (see AbstractSet#size)
21
+ # @return Infinity
22
+ def size
23
+ 1.0 / 0.0
24
+ end
25
+
26
+ # (see AbstractSet#finite?)
27
+ # @return false
28
+ def finite?
29
+ false
30
+ end
31
+
32
+ # (see AbstractSet#empty?)
33
+ # @return false
34
+ def empty?
35
+ false
36
+ end
37
+
38
+ # (see AbstractSet#replace)
39
+ def replace(other)
40
+ Cantor.build(other)
41
+ end
42
+
43
+ # @group Set Operations
44
+ #########################################################################
45
+
46
+ # (see AbstractSet#complement)
47
+ def complement
48
+ # ¬(¬A) = A
49
+ @complement
50
+ end
51
+
52
+ # (see AbstractSet#intersection)
53
+ def intersection(other)
54
+ if other.is_a?(RelativeComplement)
55
+ # ¬A ∩ ¬B = ¬(A ∪ B)
56
+ complement.union(other.complement).complement
57
+ else
58
+ # ¬A ∩ B = B ∖ A
59
+ Cantor.build(other).difference(complement)
60
+ end
61
+ end
62
+
63
+ # (see AbstractSet#union)
64
+ def union(other)
65
+ if other.is_a?(RelativeComplement)
66
+ # ¬A ∪ ¬B = ¬(A ∩ B)
67
+ complement.intersection(other.complement).complement
68
+ else
69
+ # ¬A ∪ B = ¬(A ∖ B)
70
+ complement.difference(Cantor.build(other)).complement
71
+ end
72
+ end
73
+
74
+ # (see AbstractSet#symmetric_difference)
75
+ def symmetric_difference(other)
76
+ if other.is_a?(RelativeComplement)
77
+ # ¬A ⊖ ¬B = (¬A ∖ ¬B) ∪ (¬B ∖ ¬A)
78
+ # = (B ∖ A) ∪ (A ∖ B)
79
+ # = A ⊖ B
80
+ complement.symmetric_difference(other.complement)
81
+ else
82
+ # ¬A ⊖ B = (¬A ∖ B) ∪ (B ∖ ¬A)
83
+ # = (¬A ∩ ¬B) ∪ (B ∩ A)
84
+ # = (¬B ∖ A) ∪ (A ∖ ¬B)
85
+ # = A ⊖ ¬B
86
+ other = Cantor.build(other)
87
+
88
+ intersection(other.complement).
89
+ union(other.intersection(complement))
90
+ end
91
+ end
92
+
93
+ # (see AbstractSet#difference)
94
+ def difference(other)
95
+ if other.is_a?(RelativeComplement)
96
+ # ¬A ∖ ¬B = ¬A ∩ B = B ∖ A
97
+ other.complement.difference(complement)
98
+ else
99
+ # ¬A ∖ B = ¬A ∩ ¬B = ¬(A ∪ B)
100
+ complement.union(Cantor.build(other)).complement
101
+ end
102
+ end
103
+
104
+ # @group Set Ordering
105
+ #########################################################################
106
+
107
+ # (see AbstractSet#proper_subset?)
108
+ def proper_subset?(other)
109
+ other.is_a?(RelativeComplement) and intersection(other) == self
110
+ end
111
+
112
+ # (see AbstractSet#proper_superset?)
113
+ def proper_superset?(other)
114
+ other.is_a?(RelativeComplement) and intersection(other) == other
115
+ end
116
+
117
+ # (see AbstractSet#==)
118
+ def ==(other)
119
+ eql?(other) or
120
+ (other.is_a?(RelativeComplement) and complement == other.complement)
121
+ end
122
+
123
+ # @group Pretty Printing
124
+ #########################################################################
125
+
126
+ # @return [String]
127
+ def inspect
128
+ "RelativeComplement(#{@complement.inspect})"
129
+ end
130
+
131
+ # @endgroup
132
+ #########################################################################
133
+ end
134
+
135
+ end
@@ -0,0 +1,267 @@
1
+ # encoding: UTF-8
2
+
3
+ module Cantor
4
+
5
+ #
6
+ # This data type encodes a set of unique values that belong to an _infinite_
7
+ # universe of possible values (aka domain). Set operations generally perform
8
+ # worse than {AbsoluteSet}, as they operate on {Hash} values and require
9
+ # iterating the underlying {Hash} of at least one of the two sets in `O(n)`
10
+ # time.
11
+ #
12
+ # This is suitable for sets that don't have an inherently restricted domain
13
+ # (eg sets of arbitrary {String} values), including those where the domain
14
+ # is significantly large compared to the typical size of sets built from
15
+ # those values. {RelativeSet} also requires only a single step to execute
16
+ # {#include?}, while {AbsoluteSet} requires two.
17
+ #
18
+ class RelativeSet < AbstractSet
19
+ include Enumerable
20
+
21
+ def initialize(hash)
22
+ @hash = hash
23
+ end
24
+
25
+ # Yields each element in the set to the implicit block argument.
26
+ #
27
+ # @return [void]
28
+ def each
29
+ @hash.keys.each{|o| yield o }
30
+ end
31
+
32
+ # Returns an {Array} containing each element in this set
33
+ #
34
+ # @return [Array]
35
+ def to_a
36
+ @hash.keys
37
+ end
38
+
39
+ # (see AbstractSet#include?)
40
+ def include?(object)
41
+ @hash.include?(object)
42
+ end
43
+
44
+ # (see AbstractSet#finite?)
45
+ #
46
+ # @return true
47
+ def finite?
48
+ true
49
+ end
50
+
51
+ # Returns a single element from the set, with no guarantees about which
52
+ # element. If the set is {#empty?}, the return value is undefined.
53
+ def first
54
+ @hash.keys.first
55
+ end
56
+
57
+ # (see AbstractSet#replace)
58
+ def replace(other)
59
+ Cantor.build(other)
60
+ end
61
+
62
+ # (see AbstractSet#size)
63
+ def size
64
+ @hash.size
65
+ end
66
+
67
+ # (see AbstractSet#empty?)
68
+ def empty?
69
+ @hash.empty?
70
+ end
71
+
72
+ # @group Set Operations
73
+ #########################################################################
74
+
75
+ # @return [RelativeSet]
76
+ def map
77
+ RelativeSet.new(@hash.keys.inject({}) do |hash, key|
78
+ hash[yield(key)] = true
79
+ hash
80
+ end)
81
+ end
82
+
83
+ # @return [RelativeSet]
84
+ def select
85
+ RelativeSet.new(@hash.clone.delete_if{|o,_| not yield(o) })
86
+ end
87
+
88
+ # @return [RelativeSet]
89
+ def reject
90
+ RelativeSet.new(@hash.clone.delete_if{|o,_| yield(o) })
91
+ end
92
+
93
+ # (see AbstractSet#complement)
94
+ # @return [RelativeComplement]
95
+ def complement
96
+ RelativeComplement.new(self)
97
+ end
98
+
99
+ # (see AbstractSet#intersection)
100
+ def intersection(other)
101
+ if other.is_a?(RelativeComplement)
102
+ # A ∩ ¬B = ¬B ∩ A
103
+ other.intersection(self)
104
+ elsif other.is_a?(AbstractSet)
105
+ if other.is_a?(RelativeSet) and size > other.size
106
+ # For efficiency, iterate the smaller of the two sets: A ∩ B = B ∩ A
107
+ other.intersection(self)
108
+ elsif other.empty?
109
+ # A ∩ ∅ = ∅
110
+ NullSet.build
111
+ else
112
+ hash = @hash.clone.delete_if{|o,_| not other.include?(o) }
113
+
114
+ if hash.empty?
115
+ NullSet.build
116
+ else
117
+ RelativeSet.new(hash)
118
+ end
119
+ end
120
+ else
121
+ intersection(Cantor.build(other))
122
+ end
123
+ end
124
+
125
+ # (see AbstractSet#intersection)
126
+ def union(other)
127
+ if other.is_a?(RelativeComplement)
128
+ # A ∪ ¬B = ¬B ∪ A
129
+ other.union(self)
130
+ elsif other.is_a?(AbstractSet)
131
+ unless other.is_a?(RelativeSet) and size < other.size
132
+ hash = other.inject(@hash.clone){|h,o| h[o] = true; h }
133
+
134
+ if hash.empty?
135
+ NullSet.build
136
+ else
137
+ RelativeSet.new(hash)
138
+ end
139
+ else
140
+ # For efficiency, iterate the smaller of the two sets: A ∪ B = B ∪ A
141
+ if other.empty?
142
+ self
143
+ else
144
+ other.union(self)
145
+ end
146
+ end
147
+ else
148
+ union(Cantor.build(other))
149
+ end
150
+ end
151
+
152
+ # (see AbstractSet#difference)
153
+ def difference(other)
154
+ if other.is_a?(RelativeComplement)
155
+ # A ∖ ¬B = A ∩ B
156
+ intersection(other.complement)
157
+ elsif other.is_a?(AbstractSet)
158
+ if other.empty?
159
+ self
160
+ else
161
+ # A ∖ B = A ∩ ¬B
162
+ hash = @hash.clone.delete_if{|o,_| other.include?(o) }
163
+
164
+ if hash.empty?
165
+ NullSet.build
166
+ else
167
+ RelativeSet.new(hash)
168
+ end
169
+ end
170
+ else
171
+ difference(Cantor.build(other))
172
+ end
173
+ end
174
+
175
+ # (see AbstractSet#symmetric_difference)
176
+ def symmetric_difference(other)
177
+ if other.is_a?(RelativeComplement)
178
+ # A ⊖ ~B = (A ∖ ¬B) | (¬B ∖ A)
179
+ # = (A ∩ B) | (¬B ∩ ¬A)
180
+ # = (B ∖ ¬A) | (¬A ∖ B)
181
+ # = ~A ⊖ B
182
+ intersection(other.complement).
183
+ union(other.intersection(complement))
184
+ else
185
+ # A ⊖ B = (A ∖ B) | (B ∖ A)
186
+ # = (A ∪ B) - (A ∩ B)
187
+ other = Cantor.build(other)
188
+
189
+ if other.empty?
190
+ self
191
+ else
192
+ union(other).difference(intersection(other))
193
+ end
194
+ end
195
+ end
196
+
197
+ # @group Set Ordering
198
+ #########################################################################
199
+
200
+ # (see AbstractSet#==)
201
+ def ==(other)
202
+ eql?(other) or
203
+ (other.is_a?(Enumerable) and
204
+ @hash.keys == other.to_a)
205
+ end
206
+
207
+ # @group Pretty Printing
208
+ #########################################################################
209
+
210
+ # @return [void]
211
+ def pretty_print(q)
212
+ q.text("RelativeSet[#{size}]")
213
+ q.group(2, "(", ")") do
214
+ q.breakable ""
215
+
216
+ elements = to_a
217
+ elements.take(5).each do |e|
218
+ unless q.current_group.first?
219
+ q.text ","
220
+ q.breakable
221
+ end
222
+ q.pp e
223
+ end
224
+
225
+ if elements.length > 5
226
+ q.text ","
227
+ q.breakable
228
+ q.text "..."
229
+ end
230
+ end
231
+ end
232
+
233
+ # @return [String]
234
+ def inspect
235
+ "RelativeSet(#{to_a.map(&:inspect).join(', ')})"
236
+ end
237
+
238
+ # @endgroup
239
+ #########################################################################
240
+ end
241
+
242
+ class << RelativeSet
243
+ # @group Constructors
244
+ #########################################################################
245
+
246
+ # @return [RelativeSet]
247
+ def build(object)
248
+ if object.is_a?(RelativeSet)
249
+ object
250
+ elsif object.is_a?(Enumerable)
251
+ if object.empty?
252
+ NullSet.build
253
+ elsif object.is_a?(Hash)
254
+ new(object)
255
+ else
256
+ new(object.inject({}){|h,o| h[o] = true; h })
257
+ end
258
+ else
259
+ raise TypeError
260
+ end
261
+ end
262
+
263
+ # @endgroup
264
+ #########################################################################
265
+ end
266
+
267
+ end