bit_set 0.1.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,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