bit_set 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,213 @@
1
+ # encoding: UTF-8
2
+ =begin
3
+ Copyright GodObject Team <dev@godobject.net>, 2012-2016
4
+
5
+ This file is part of BitSet.
6
+
7
+ Permission to use, copy, modify, and/or distribute this software for any
8
+ purpose with or without fee is hereby granted, provided that the above
9
+ copyright notice and this permission notice appear in all copies.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
12
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
13
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
16
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17
+ PERFORMANCE OF THIS SOFTWARE.
18
+ =end
19
+
20
+ module GodObject
21
+ module BitSet
22
+
23
+ # A Configuration defines the digits of a BitSet. Additionally it can hold
24
+ # information on how to represent the digits in a String representation.
25
+ class Configuration
26
+
27
+ # @return [String] the default String representation for enabled digits
28
+ UNNAMED_ENABLED = '1'.freeze
29
+
30
+ # @return [String] the default String representation for disabled digits
31
+ UNNAMED_DISABLED = '0'.freeze
32
+
33
+ # @return [String] the default String representation for disabled digits
34
+ # which have a custom enabled representation
35
+ NAMED_DISABLED = '-'.freeze
36
+
37
+ class << self
38
+
39
+ # @overload build(configuration)
40
+ # Returns an existing instance of GodObject::BitSet::Configuration.
41
+ # @param [GodObject::BitSet::Configuration] configuration an already
42
+ # existing Configuration
43
+ # @return [GodObject::BitSet::Configuration] the same Configuration object
44
+ #
45
+ # @overload build(digits)
46
+ # Returns a new Configuration object with given attributes.
47
+ # @param [Array<Symbol>] digits a list of digit names
48
+ # @return [GodObject::PosixMode::Mode] a new Configuration object
49
+ #
50
+ # @overload build(enabled_representations_by_digits)
51
+ # Returns a new Configuration object with given attributes.
52
+ # @param [Hash<Symbol => String>] enabled_representations_by_digits
53
+ # digit names mapped to their enabled character representations
54
+ # @return [GodObject::PosixMode::Mode] a new Configuration object
55
+ #
56
+ # @overload build(representations_by_digits)
57
+ # Returns a new Configuration object with given attributes.
58
+ # @param [Hash<Symbol => Array<String>>] representations_by_digits
59
+ # digit names mapped to their enabled and disabled character
60
+ # representations
61
+ # @return [GodObject::PosixMode::Mode] a new Configuration object
62
+ def build(configuration)
63
+ if configuration.is_a?(Configuration)
64
+ configuration
65
+ else
66
+ new(configuration)
67
+ end
68
+ end
69
+
70
+ end
71
+
72
+ # @return [Range<Integer>] the Range in which an Integer representation of a
73
+ # BitSet with this Configuration can be.
74
+ attr_reader :valid_range
75
+
76
+ # Initializes a new BitSet::Configuration
77
+ #
78
+ # @overload initialize(digits)
79
+ # @param [Array<Symbol>] digits a list of digit names
80
+ #
81
+ # @overload initialize(enabled_representations_by_digits)
82
+ # @param [Hash<Symbol => String>] enabled_representations_by_digits
83
+ # digit names mapped to their enabled character representations
84
+ #
85
+ # @overload initialize(representations_by_digits)
86
+ # @param [Hash<Symbol => Array<String>>] representations_by_digits
87
+ # digit names mapped to their enabled and disabled character
88
+ # representations
89
+ def initialize(configuration)
90
+ @digits = {}
91
+ @enabled = {}
92
+ @disabled = {}
93
+
94
+ configuration.each do |digit, display|
95
+ digit = digit.to_sym
96
+
97
+ @digits[digit] = nil
98
+
99
+ case
100
+ when display.respond_to?(:all?) && display.all?{|s| s.respond_to?(:to_str) && s.to_str.length == 1}
101
+ @enabled[digit] = display.first.to_str.dup.freeze
102
+ @disabled[digit] = display.last.to_str.dup.freeze
103
+ when display.respond_to?(:to_str) && display.to_str.length == 1
104
+ @enabled[digit] = display.to_str.dup.freeze
105
+ @disabled[digit] = NAMED_DISABLED
106
+ when display.nil?
107
+ @enabled[digit] = UNNAMED_ENABLED
108
+ @disabled[digit] = UNNAMED_DISABLED
109
+ else
110
+ raise ArgumentError, 'Invalid configuration'
111
+ end
112
+ end
113
+
114
+ raise ArgumentError, 'At least one digit must be configured' if digits.count < 1
115
+
116
+ @digits.keys.reverse.each_with_index{|digit, index| @digits[digit] = 2 ** index }
117
+
118
+ @valid_range = Range.new(0, (@digits.values.first * 2) - 1).freeze
119
+
120
+ @unique_characters = !@enabled.values.dup.uniq!
121
+ end
122
+
123
+ # Answers if all digits have unique enabled representations.
124
+ #
125
+ # @return [true, false] true if all digits have unique enabled
126
+ # representations, false otherwise
127
+ def unique_characters?
128
+ @unique_characters
129
+ end
130
+
131
+ # @attribute digits [readonly]
132
+ # @return [Array<Symbol>] an ordered list of all configured digit names
133
+ def digits
134
+ @digits.keys
135
+ end
136
+
137
+ # @param [Integer, Array<Symbol>] state either the octal state of the
138
+ # BitSet or a list of enabled digits
139
+ # @return [GodObject::BitSet] a new BitSet object with the current
140
+ # configuration
141
+ def new(*state)
142
+ BitSet.new(*state, self)
143
+ end
144
+
145
+ # @return [Integer] the Integer representation of a BitSet where
146
+ # only the given digit is enabled.
147
+ def binary_position(digit)
148
+ @digits[find_digit(digit)]
149
+ end
150
+
151
+ # @param [Symbol, Integer] digit the name or index of the digit
152
+ # @return [String] the String representation for the given digit when
153
+ # it is disabled
154
+ def disabled_character(digit)
155
+ @disabled[find_digit(digit)]
156
+ end
157
+
158
+ # @param [Symbol, Integer] digit the name or index of the digit
159
+ # @return [String] the String representation for the given digit when
160
+ # it is enabled
161
+ def enabled_character(digit)
162
+ @enabled[find_digit(digit)]
163
+ end
164
+
165
+ # @param [Symbol, Integer] index_or_digit the name or index of the digit
166
+ # @return [Symbol] the digit's name
167
+ def find_digit(index_or_digit)
168
+ case
169
+ when index_or_digit.respond_to?(:to_sym)
170
+ digit = index_or_digit.to_sym
171
+
172
+ raise ArgumentError, "Invalid digit name (#{index_or_digit})" unless @digits.keys.include?(digit)
173
+ else
174
+ digit = @digits.keys[index_or_digit.to_int]
175
+ end
176
+
177
+ raise ArgumentError, "Invalid index or digit (#{index_or_digit})" unless digit
178
+
179
+ digit
180
+ end
181
+
182
+ # Answers if another object is equal.
183
+ #
184
+ # Equality is defined as having the same ordered list of digits.
185
+ #
186
+ # @param [Object] other an object to be checked for equality
187
+ # @return [true, false] true if the object is considered equal, false
188
+ # otherwise
189
+ def ==(other)
190
+ digits == other.digits
191
+ rescue NoMethodError
192
+ false
193
+ end
194
+
195
+ # Answers if another object is equal and of the same type family.
196
+ #
197
+ # @see GodObject::Configuration#==
198
+ # @param [Object] other an object to be checked for equality
199
+ # @return [true, false] true if the object is considered equal and of
200
+ # the same type familiy, false otherwise
201
+ def eql?(other)
202
+ self == other && other.kind_of?(self.class)
203
+ end
204
+
205
+ # @return (see Hash#hash) identity hash for hash table usage
206
+ def hash
207
+ @digits.hash
208
+ end
209
+
210
+ end
211
+
212
+ end
213
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: UTF-8
2
+ =begin
3
+ Copyright GodObject Team <dev@godobject.net>, 2012-2016
4
+
5
+ This file is part of BitSet.
6
+
7
+ Permission to use, copy, modify, and/or distribute this software for any
8
+ purpose with or without fee is hereby granted, provided that the above
9
+ copyright notice and this permission notice appear in all copies.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
12
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
13
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
16
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17
+ PERFORMANCE OF THIS SOFTWARE.
18
+ =end
19
+
20
+ module GodObject
21
+ module BitSet
22
+
23
+ # The currently loaded version.
24
+ #
25
+ # Using Semantic Versioning (2.0.0) rules
26
+ # @see http://semver.org/spec/v2.0.0.html
27
+ VERSION = '0.1.0'.freeze
28
+
29
+ end
30
+ end
@@ -0,0 +1,487 @@
1
+ # encoding: UTF-8
2
+ =begin
3
+ Copyright GodObject Team <dev@godobject.net>, 2012-2016
4
+
5
+ This file is part of BitSet.
6
+
7
+ Permission to use, copy, modify, and/or distribute this software for any
8
+ purpose with or without fee is hereby granted, provided that the above
9
+ copyright notice and this permission notice appear in all copies.
10
+
11
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
12
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
13
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
16
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17
+ PERFORMANCE OF THIS SOFTWARE.
18
+ =end
19
+
20
+ module GodObject
21
+ module BitSet
22
+
23
+ describe BitSet do
24
+ let(:traffic_light_configuration) { Configuration.new(red: 'r', yellow: 'y', green: 'g') }
25
+ let(:generic_configuration) { Configuration.new([:a, :b, :c, :d, :e])}
26
+
27
+ describe ".new" do
28
+ it "should accept a configuration" do
29
+ result = BitSet.new(generic_configuration)
30
+
31
+ expect(result).to be_a(BitSet)
32
+ expect(result.to_i).to eql 0
33
+ expect(result.configuration).to eql generic_configuration
34
+ end
35
+
36
+ it "should accept an Integer as initial state and a configuration" do
37
+ result = BitSet.new(0b01010, generic_configuration)
38
+
39
+ expect(result).to be_a(BitSet)
40
+ expect(result.to_i).to eql 0b01010
41
+ expect(result.configuration).to eql generic_configuration
42
+ end
43
+
44
+ it "should accept multiple arguments of enabled digits as initial state and a configuration" do
45
+ result = BitSet.new(:red, :green, traffic_light_configuration)
46
+
47
+ expect(result).to be_a(BitSet)
48
+ expect(result.to_i).to eql 0b101
49
+ expect(result.configuration).to eql traffic_light_configuration
50
+ end
51
+
52
+ it "should accept a list of enabled digits as initial state and a configuration" do
53
+ result = BitSet.new(Set[:red, :green], traffic_light_configuration)
54
+
55
+ expect(result).to be_a(BitSet)
56
+ expect(result.to_i).to eql 0b101
57
+ expect(result.configuration).to eql traffic_light_configuration
58
+ end
59
+
60
+ it "should accept on-the-fly configurations" do
61
+ result = BitSet.new(Set[:symmetric, :transitive], symmetric: nil, transitive: 't', antisymmetric: ['a', 'x'])
62
+
63
+ configuration = Configuration.build(symmetric: nil, transitive: 't', antisymmetric: ['a', 'x'])
64
+
65
+ expect(result).to be_a(BitSet)
66
+ expect(result.to_i).to eql 0b110
67
+ expect(result.to_s).to eql "1tx"
68
+ expect(result.configuration).to eql configuration
69
+ end
70
+
71
+ it "should complain about invalid digits" do
72
+ expect {
73
+ BitSet.new(Set[:white, :blue, :green], traffic_light_configuration)
74
+ }.to raise_error(ArgumentError, "Invalid digit(s): :white, :blue")
75
+ end
76
+
77
+ it "should complain about invalid state" do
78
+ expect {
79
+ BitSet.new(:invalid, traffic_light_configuration)
80
+ }.to raise_error(ArgumentError, "Invalid digit(s): :invalid")
81
+ end
82
+
83
+ it "should complain about invalid configuration" do
84
+ expect {
85
+ BitSet.new(3, invalid: 12)
86
+ }.to raise_error(ArgumentError, 'Invalid configuration')
87
+ end
88
+ end
89
+
90
+ context "magic attributes" do
91
+ it "should allow accessing each digit's on/off state by name" do
92
+ bit_set = BitSet.new(0b001, traffic_light_configuration)
93
+
94
+ expect(bit_set.red).to eq false
95
+ expect(bit_set.yellow).to eq false
96
+ expect(bit_set.green).to eq true
97
+ end
98
+
99
+ it "should allow accessing each digit's on/off state by a question mark method" do
100
+ bit_set = BitSet.new(0b01101, generic_configuration)
101
+
102
+ expect(bit_set.a?).to eq false
103
+ expect(bit_set.b?).to eq true
104
+ expect(bit_set.c?).to eq true
105
+ expect(bit_set.d?).to eq false
106
+ expect(bit_set.e?).to eq true
107
+ end
108
+
109
+ it "should complain about invalid methods as usual" do
110
+ expect {
111
+ BitSet.new(0b101, traffic_light_configuration).fnord
112
+ }.to raise_error(NoMethodError, /^undefined method `fnord'/)
113
+ end
114
+ end
115
+
116
+ [:state, :attributes].each do |method_name|
117
+ describe "##{method_name}" do
118
+ it "should list all digits and their on/off state" do
119
+ bit_set = BitSet.new(0b10101, generic_configuration)
120
+
121
+ expect(bit_set.public_send(method_name)).to eql(
122
+ a: true,
123
+ b: false,
124
+ c: true,
125
+ d: false,
126
+ e: true
127
+ )
128
+ end
129
+ end
130
+ end
131
+
132
+ describe "#to_i" do
133
+ it "should return the Integer state" do
134
+ bit_set = BitSet.new(6, traffic_light_configuration)
135
+
136
+ expect(bit_set.to_i).to eql 6
137
+ end
138
+ end
139
+
140
+ describe "#[]" do
141
+ it "should return true if the digit given by symbol is on" do
142
+ bit_set = BitSet.new(0b100, traffic_light_configuration)
143
+
144
+ expect(bit_set[:red]).to eq true
145
+ end
146
+
147
+ it "should return true if the digit given by index is on" do
148
+ bit_set = BitSet.new(0b100, traffic_light_configuration)
149
+
150
+ expect(bit_set[0]).to eq true
151
+ end
152
+
153
+ it "should return false if the digit given by symbol is off" do
154
+ bit_set = BitSet.new(0b11101, generic_configuration)
155
+
156
+ expect(bit_set[:d]).to eq false
157
+ end
158
+
159
+ it "should return false if the digit given by index is off" do
160
+ bit_set = BitSet.new(0b11101, generic_configuration)
161
+
162
+ expect(bit_set[3]).to eq false
163
+ end
164
+ end
165
+
166
+ describe "#enabled_digits" do
167
+ it "should return the set of digits which are on" do
168
+ bit_set = BitSet.new(0b10101, generic_configuration)
169
+
170
+ expect(bit_set.enabled_digits).to eql Set[:a, :c, :e]
171
+ end
172
+ end
173
+
174
+ describe "#disabled_digits" do
175
+ it "should return the set of digits which are off" do
176
+ bit_set = BitSet.new(0b001, traffic_light_configuration)
177
+
178
+ expect(bit_set.disabled_digits).to eql Set[:red, :yellow]
179
+ end
180
+ end
181
+
182
+ describe "#invert" do
183
+ it "should return a copy with every digit on/off state toggled" do
184
+ bit_set = BitSet.new(0b101, traffic_light_configuration)
185
+
186
+ result = bit_set.invert
187
+ expect(result).to be_a(BitSet)
188
+ expect(result.configuration).to eql traffic_light_configuration
189
+ expect(result.to_i).to eql 0b010
190
+ expect(result).not_to equal bit_set
191
+ end
192
+ end
193
+
194
+ describe "#+" do
195
+ it "should return a copy with all added digits switched to on (given a BitSet)" do
196
+ bit_set = BitSet.new(0b01000, generic_configuration)
197
+
198
+ result = bit_set + BitSet.new([:a, :d], generic_configuration)
199
+
200
+ expect(result).to be_a(BitSet)
201
+ expect(result.configuration).to eql generic_configuration
202
+ expect(result.to_i).to eql 0b11010
203
+ expect(result).not_to equal bit_set
204
+ end
205
+
206
+ it "should return a copy with all added digits switched to on (given an Enumerable)" do
207
+ bit_set = BitSet.new(0b01010, generic_configuration)
208
+
209
+ result = bit_set + Set[:a, :d]
210
+
211
+ expect(result).to be_a(BitSet)
212
+ expect(result.configuration).to eql generic_configuration
213
+ expect(result.to_i).to eql 0b11010
214
+ expect(result).not_to equal bit_set
215
+ end
216
+ end
217
+
218
+ describe "#-" do
219
+ it "should return a copy with all added digits switched to on (given a BitSet)" do
220
+ bit_set = BitSet.new(0b110, traffic_light_configuration)
221
+
222
+ result = bit_set - BitSet.new([:yellow, :green], traffic_light_configuration)
223
+
224
+ expect(result).to be_a(BitSet)
225
+ expect(result.configuration).to eql traffic_light_configuration
226
+ expect(result.to_i).to eql 0b100
227
+ expect(result).not_to equal bit_set
228
+ end
229
+
230
+ it "should return a copy with all added digits switched to on (given an Enumerable)" do
231
+ bit_set = BitSet.new(0b101, traffic_light_configuration)
232
+
233
+ result = bit_set - [:red, :yellow]
234
+
235
+ expect(result).to be_a(BitSet)
236
+ expect(result.configuration).to eql traffic_light_configuration
237
+ expect(result.to_i).to eql 0b001
238
+ expect(result).not_to equal bit_set
239
+ end
240
+ end
241
+
242
+ [:union, :|].each do |method_name|
243
+ describe "##{method_name}" do
244
+ it "should return a copy with all enabled digits of both operands switched to on (given a BitSet)" do
245
+ bit_set = BitSet.new(0b01101, generic_configuration)
246
+
247
+ result = bit_set.public_send(method_name,
248
+ BitSet.new([:d, :e], generic_configuration))
249
+
250
+ expect(result).to be_a(BitSet)
251
+ expect(result.configuration).to eql generic_configuration
252
+ expect(result.to_i).to eql 0b01111
253
+ expect(result).not_to equal bit_set
254
+ end
255
+
256
+ it "should return a copy with all enabled digits of both operands switched to on (given an Integer)" do
257
+ bit_set = BitSet.new(0b01101, generic_configuration)
258
+
259
+ result = bit_set.public_send(method_name, 0b00011)
260
+
261
+ expect(result).to be_a(BitSet)
262
+ expect(result.configuration).to eql generic_configuration
263
+ expect(result.to_i).to eql 0b01111
264
+ expect(result).not_to equal bit_set
265
+ end
266
+ end
267
+ end
268
+
269
+ [:intersection, :&].each do |method_name|
270
+ describe "##{method_name}" do
271
+ it "should return a copy with only those digits switched to on which are on in both operands (given a BitSet)" do
272
+ bit_set = BitSet.new(0b110, traffic_light_configuration)
273
+
274
+ result = bit_set.public_send(method_name,
275
+ BitSet.new([:yellow, :green], traffic_light_configuration))
276
+
277
+ expect(result).to be_a(BitSet)
278
+ expect(result.configuration).to eql traffic_light_configuration
279
+ expect(result.to_i).to eql 0b010
280
+ expect(result).not_to equal bit_set
281
+ end
282
+
283
+ it "should return a copy with only those digits switched to on which are on in both operands (given an Integer)" do
284
+ bit_set = BitSet.new(0b110, traffic_light_configuration)
285
+
286
+ result = bit_set.public_send(method_name, 0b011)
287
+
288
+ expect(result).to be_a(BitSet)
289
+ expect(result.configuration).to eql traffic_light_configuration
290
+ expect(result.to_i).to eql 0b010
291
+ expect(result).not_to equal bit_set
292
+ end
293
+ end
294
+ end
295
+
296
+ [:symmetric_difference, :^].each do |method_name|
297
+ describe "##{method_name}" do
298
+ it "should return a copy with only those digits switched to on which are uniquely enabled in both operands (given a BitSet)" do
299
+ bit_set = BitSet.new(0b01101, generic_configuration)
300
+
301
+ result = bit_set.public_send(method_name,
302
+ BitSet.new([:d, :e], generic_configuration))
303
+
304
+ expect(result).to be_a(BitSet)
305
+ expect(result.configuration).to eql generic_configuration
306
+ expect(result.to_i).to eql 0b01110
307
+ expect(result).not_to equal bit_set
308
+ end
309
+
310
+ it "should return a copy with only those digits switched to on which are uniquely enabled in both operands (given an Integer)" do
311
+ bit_set = BitSet.new(0b01101, generic_configuration)
312
+
313
+ result = bit_set.public_send(method_name, 0b00011)
314
+
315
+ expect(result).to be_a(BitSet)
316
+ expect(result.configuration).to eql generic_configuration
317
+ expect(result.to_i).to eql 0b01110
318
+ expect(result).not_to equal bit_set
319
+ end
320
+ end
321
+ end
322
+
323
+ describe "#==" do
324
+ let(:bit_set) { BitSet.new(0b110, traffic_light_configuration) }
325
+
326
+ it "should return true if state and configuration is equal" do
327
+ expect(bit_set).to eq(BitSet.new(0b110, traffic_light_configuration))
328
+ end
329
+
330
+ it "should return true if state and configuration is equal (different class)" do
331
+ expect(bit_set).to eq(OpenStruct.new(integer_representation: 0b110, configuration: traffic_light_configuration))
332
+ end
333
+
334
+ it "should return false if state is equal but configuration differs" do
335
+ expect(bit_set).not_to eq(BitSet.new(0b110, generic_configuration))
336
+ end
337
+
338
+ it "should return false if configuration is equal but state differs" do
339
+ expect(bit_set).not_to eq(BitSet.new(0b010, traffic_light_configuration))
340
+ end
341
+
342
+ it "should return false if both state and configuration differ" do
343
+ expect(bit_set).not_to eq(BitSet.new(0b010, generic_configuration))
344
+ end
345
+
346
+ it "should return false if compared to incompatible type" do
347
+ expect(bit_set).not_to eq(:incompatible)
348
+ end
349
+ end
350
+
351
+ describe "#eql?" do
352
+ let(:bit_set) { BitSet.new(0b110, traffic_light_configuration) }
353
+
354
+ it "should return true if state and configuration is equal" do
355
+ expect(bit_set).to eql BitSet.new(0b110, traffic_light_configuration)
356
+ end
357
+
358
+ it "should return false if state and configuration is equal (different class)" do
359
+ expect(bit_set).not_to eql OpenStruct.new(integer_representation: 0b110, configuration: traffic_light_configuration)
360
+ end
361
+
362
+ it "should return false if state is equal but configuration differs" do
363
+ expect(bit_set).not_to eql BitSet.new(0b110, generic_configuration)
364
+ end
365
+
366
+ it "should return false if configuration is equal but state differs" do
367
+ expect(bit_set).not_to eql BitSet.new(0b010, traffic_light_configuration)
368
+ end
369
+
370
+ it "should return false if both state and configuration differ" do
371
+ expect(bit_set).not_to eql BitSet.new(0b010, generic_configuration)
372
+ end
373
+
374
+ it "should return false if compared to an incompatible object" do
375
+ expect(bit_set).not_to eql :incompatible
376
+ end
377
+ end
378
+
379
+ describe "#<=>" do
380
+ let(:bit_set) { BitSet.new(11, generic_configuration) }
381
+
382
+ it "should return -1 if state is lower than that of the compared" do
383
+ expect(bit_set <=> BitSet.new(12, generic_configuration)).to eql -1
384
+ end
385
+
386
+ it "should return 0 if state is equal to that of the compared" do
387
+ expect(bit_set <=> BitSet.new(11, generic_configuration)).to eql 0
388
+ end
389
+
390
+ it "should return 1 if state is higher than that of the compared" do
391
+ expect(bit_set <=> BitSet.new(10, generic_configuration)).to eql 1
392
+ end
393
+
394
+ it "should return nil if configuration differs" do
395
+ expect(bit_set <=> BitSet.new(11, traffic_light_configuration)).to be_nil
396
+ end
397
+
398
+ it "should return nil if compared to an incompatible object" do
399
+ expect(bit_set <=> :incompatible).to be_nil
400
+ end
401
+
402
+ it "should make BitSet sortable" do
403
+ result = [BitSet.new(11, generic_configuration),
404
+ BitSet.new(10, generic_configuration),
405
+ BitSet.new(12, generic_configuration)].sort
406
+
407
+ expect(result).to eql [
408
+ BitSet.new(10, generic_configuration),
409
+ BitSet.new(11, generic_configuration),
410
+ BitSet.new(12, generic_configuration)
411
+ ]
412
+ end
413
+ end
414
+
415
+ describe "#hash" do
416
+ let(:bit_set) { BitSet.new(0b01011, generic_configuration) }
417
+
418
+ it "should be stable over multiple calls" do
419
+ expect(bit_set.hash).to eql bit_set.hash
420
+ end
421
+
422
+ it "should differ if state is equal but configuration differs" do
423
+ expect(bit_set.hash).not_to eql BitSet.new(0b01011, traffic_light_configuration).hash
424
+ end
425
+
426
+ it "should differ if configuration is equal but state differs" do
427
+ expect(bit_set.hash).not_to eql BitSet.new(0b00010, generic_configuration).hash
428
+ end
429
+
430
+ it "should differ if both state and configuration differ" do
431
+ expect(bit_set.hash).not_to eql BitSet.new(0b00010, traffic_light_configuration).hash
432
+ end
433
+ end
434
+
435
+ describe "#inspect" do
436
+ it "should return a decent string representation for debugging" do
437
+ result = BitSet.new(0b011, traffic_light_configuration).inspect
438
+
439
+ expect(result).to eql '#<GodObject::BitSet::BitSet: "-yg">'
440
+ end
441
+ end
442
+
443
+ describe "#to_s" do
444
+ it "should complain about invalid formats" do
445
+ expect {
446
+ BitSet.new(0b00101, generic_configuration).to_s(:medium)
447
+ }.to raise_error(ArgumentError, "Invalid format: :medium")
448
+ end
449
+
450
+ context "long mode" do
451
+ it "should by default display every digit as either 0 or 1" do
452
+ result = BitSet.new(0b00101, generic_configuration).to_s
453
+
454
+ expect(result).to eql "00101"
455
+ end
456
+
457
+ it "should display every digit as either it's assigned character or a dash if digits have assigned characters" do
458
+ result = BitSet.new(0b101, traffic_light_configuration).to_s
459
+
460
+ expect(result).to eql "r-g"
461
+ end
462
+ end
463
+
464
+ context "short mode" do
465
+ it "should not be available if configuration has no unique characters per digit" do
466
+ expect {
467
+ BitSet.new(3, generic_configuration).to_s(:short)
468
+ }.to raise_error(ArgumentError, 'Short format only available for configurations with unique characters for each digit')
469
+ end
470
+
471
+ it "should only display enabled digits" do
472
+ result = BitSet.new(0b110, traffic_light_configuration).to_s(:short)
473
+
474
+ expect(result).to eql "ry"
475
+ end
476
+
477
+ it "should diplay one dash when no digit is on" do
478
+ result = BitSet.new(0, traffic_light_configuration).to_s(:short)
479
+
480
+ expect(result).to eql "-"
481
+ end
482
+ end
483
+ end
484
+ end
485
+
486
+ end
487
+ end