cantor 1.2.0

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,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