cantor 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +97 -0
- data/Rakefile +81 -0
- data/lib/cantor.rb +54 -0
- data/lib/cantor/absolute_set.rb +295 -0
- data/lib/cantor/abstract_set.rb +174 -0
- data/lib/cantor/null_set.rb +123 -0
- data/lib/cantor/relative_complement.rb +135 -0
- data/lib/cantor/relative_set.rb +267 -0
- data/lib/cantor/universal_set.rb +102 -0
- data/spec/examples/absolute_set.example +1513 -0
- data/spec/examples/null_set.example +2 -0
- data/spec/examples/relative_set.example +2 -0
- data/spec/examples/universal_set.example +1 -0
- data/spec/spec_helper.rb +30 -0
- metadata +81 -0
@@ -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
|