bit_magic 0.1.1

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,103 @@
1
+ require_relative "./error"
2
+
3
+ module BitMagic
4
+
5
+ # Helper class to encapsulate bit field values and read/write operations
6
+ # Note that indices are based off ruby bit reading, so least-significant bits
7
+ # are to the right and negative numbers are handled as two's complement.
8
+ #
9
+ # @attr [Integer] value the current integer value that contains the bit fields
10
+ class BitField
11
+ attr_reader :value
12
+
13
+ # Initialize the BitField with an optional value. Default is 0
14
+ #
15
+ # @param [Integer] value the integer that contains the bit fields
16
+ def initialize(value = 0)
17
+ if value.is_a?(Integer)
18
+ @value = value
19
+ else
20
+ raise InputError.new("BitField#new expects an integer value, #{value.inspect} is not an integer")
21
+ end
22
+ end
23
+
24
+ # Read the specified bit indices into a hash with bit index as key
25
+ #
26
+ # @param [Integer] bits one or more bit indices to read.
27
+ #
28
+ # @example Read a list of bits into a hash
29
+ # bit_field = BitField.new(5)
30
+ # bit_field.read_bits(0, 1, 2)
31
+ # #=> {0=>1, 1=>0, 2=>1}
32
+ # # because 5 is 101 in binary
33
+ #
34
+ # @return [Hash] a hash with the bit index as key and bit (1 or 0) as value
35
+ def read_bits(*args)
36
+ {}.tap do |m|
37
+ args.each { |bit| m[bit] = @value[bit] }
38
+ end
39
+ end
40
+
41
+ # Read the specified bit indices as a group, in the order given
42
+ #
43
+ # @param [Integer] bits one or more bit indices to read. Order matters!
44
+ #
45
+ # @example Read bits or a list of bits into an integer
46
+ # bit_field = BitField.new(101) # 1100101 in binary, lsb on the right
47
+ # bit_field.read_field(0, 1, 2) #=> 5 # or 101
48
+ # bit_field.read_field(0) #= 1
49
+ # bit_field.read_field( (2..6).to_a ) #=> 25 # or 11001
50
+ #
51
+ # @return [Integer] the value of the bits read together into an integer
52
+ def read_field(*args)
53
+ m = 0
54
+ args.flatten.each_with_index do |bit, i|
55
+ if bit.is_a?(Integer)
56
+ m |= ((@value[bit] || 0) << i)
57
+ end
58
+ end
59
+ m
60
+ end
61
+
62
+ alias :[] :read_field
63
+
64
+ # Write to the specified bits, changing the internal @value to the new value
65
+ #
66
+ # @param [Hash] bit_values a hash with the key being a bit index and value
67
+ # being the value (must be 1, 0, true or false)
68
+ #
69
+ # @example Write new bit withs with their corresponding values
70
+ # bit_field = BitField.new
71
+ # bit_field.write_bits(0 => true) #=> 1
72
+ # bit_field.write_bits(1 => true, 4 => true) #=> 19 # 10011
73
+ # bit_field.write_bits(0 => false, 4 => false) #=> 2 # 10
74
+ #
75
+ # @return [Integer] the value after writing the new bits their new values
76
+ def write_bits(bit_values = {})
77
+ bit_values.each_pair do |index, val|
78
+
79
+ if !index.is_a?(Integer)
80
+ raise InputError.new("BitField#write can only access bits by their index, #{index.inspect} is not a valid index")
81
+ end
82
+
83
+ if index < 0
84
+ raise InputError.new("BitField#write can not write to negative indices")
85
+ end
86
+
87
+ if !(val === true) and !(val === false) and !(val === 1) and !(val === 0)
88
+ raise InputError.new("BitField#write must have a boolean value, #{val.inspect} is not a boolean")
89
+ end
90
+
91
+ if val === true or val === 1
92
+ @value |= (1 << index)
93
+ else
94
+ @value &= ~(1 << index)
95
+ end
96
+
97
+ end
98
+
99
+ @value
100
+ end
101
+
102
+ end
103
+ end
@@ -0,0 +1,285 @@
1
+ require_relative './bit_field'
2
+
3
+ module BitMagic
4
+ # This is a wrapper class for objects that want bitfield functionality.
5
+ # It implements bit field read, write and attribute field read and updating.
6
+ #
7
+ # This is usually used alongside Magician (an adapter helper class).
8
+ #
9
+ # If you're using this class directly, you can subclass it and set DEFAULT_OPTIONS
10
+ # on your subclass to change default options.
11
+ #
12
+ # @example Subclass this class to change defaults
13
+ # # (do not set attribute_name to 'object_id' in real code, it's an example)
14
+ # class MyBits < BitMagic::Bits
15
+ # DEFAULT_OPTIONS = BitMagic::Bits::DEFAULT_OPTIONS.merge({default: 99, :attribute_name => 'object_id'})
16
+ # end
17
+ # # and now, if you initialize a new MyBits...
18
+ # MyBits.new(Object.new).options
19
+ # # will have default: 99 (instead of 0), and attribute_name: 'object_id'
20
+ #
21
+ #
22
+ # @attr_reader instance the instance we're doing operations for
23
+ # @attr_reader [Hash] field_list a hash of field name => field bits key-pairs
24
+ # @attr_reader [Hash] options options for this instance
25
+ class Bits
26
+ # This casts a given input value into a boolean.
27
+ BOOLEAN_CASTER = lambda {|i| !(i == false or i == 0) }
28
+
29
+ # Default options
30
+ #
31
+ # The bool_caster is expected to be overwritten depending on your use-case.
32
+ # eg, form fields can send '0' or 'f' to mean false.
33
+ DEFAULT_OPTIONS = {
34
+ :attribute_name => 'flags',
35
+ :default => 0,
36
+ :updater => Proc.new {|bits, new_value| bits.instance.send(:"#{bits.attribute_name}=", new_value) },
37
+ :bool_caster => BOOLEAN_CASTER
38
+ }.freeze
39
+
40
+ attr_reader :instance, :field_list, :options
41
+
42
+ # This class wraps around any arbitrary objects (instance) that respond to
43
+ # certain methods (a getter and setter for the flag field).
44
+ #
45
+ # @param [Object] instance some arbitrary object with bit_magic interest
46
+ # @param [BitMagic::Adapters::Magician, Hash] magician_or_field_list either
47
+ # an instance of Magician (usually from an adapter) or a Hash with
48
+ # field name => bit/field bits array as key-pair.
49
+ # @param [Hash] options additional options to override defaults
50
+ # @option options [String] :attribute_name the name for the attribute, will
51
+ # be used as the getter and setter (by appending '=') on the instance object
52
+ # default: 'flags'
53
+ # @option options [Integer] :default the default value. default: 0
54
+ # @option options [Proc] :updater a callable (Proc/lambda/Method) used to
55
+ # update the bit field attribute after it has been changed.
56
+ # default: calls "#{attribute_name}=(newValue)" on instance
57
+ # @option options [Proc] :bool_caster a callable (Proc/lambda/Method) used to
58
+ # cast some input value into a boolean (used with #write)
59
+ # default: cast false or 0 (integer) as false, everything else as true
60
+ #
61
+ # @example Initialize a Bits object
62
+ # Example = Struct.new('Example', :flags)
63
+ # # above, Example is a class with the 'flags' instance method
64
+ # # below, we initialize an instance with flags set to 0
65
+ # bits = BitMagic::Bits.new(Example.new(0), {:is_odd => 0, :amount => [1, 2, 3], :is_cool => 4})
66
+ #
67
+ # @return [BitMagic::Bits] a Bits object
68
+ def initialize(instance, magician_or_field_list = {}, options = {})
69
+ if defined?(BitMagic::Adapters::Magician) and magician_or_field_list.is_a?(BitMagic::Adapters::Magician)
70
+ @magician = magician_or_field_list
71
+ @field_list = @magician.field_list
72
+ @options = @magician.action_options.merge(options)
73
+ else
74
+ @field_list = magician_or_field_list
75
+ @options = self.class::DEFAULT_OPTIONS.merge(options)
76
+ end
77
+ @instance = instance
78
+ end
79
+
80
+ # Get the attribute name option (a method on the instance that returns the
81
+ # current bit field value).
82
+ #
83
+ # In the future, attribute name may be a Proc. This class could also possibly
84
+ # be subclassed and this method overwritten if advanced lookup is necessary.
85
+ #
86
+ # @return [String] name of the method we will use to get the bit field value
87
+ def attribute_name
88
+ @options[:attribute_name]
89
+ end
90
+
91
+ # Get the current value from the instance. It should return an integer if the
92
+ # value exists, otherwise anything falsy will be set to the default value
93
+ # given during initialization.
94
+ #
95
+ # @return [Integer] the current value for the bit field
96
+ def value
97
+ value = @instance.send(attribute_name)
98
+ value ||= @options[:default]
99
+ value
100
+ end
101
+
102
+ # Update the bit field value on the instance with a new value using the updater
103
+ # Proc given during initialization.
104
+ #
105
+ # @param [Integer] new_value the new value for the bit field
106
+ #
107
+ # @return returns the value returned by the updater, usually is the new_value
108
+ def update(new_value)
109
+ if @options[:updater].respond_to?(:call)
110
+ @options[:updater].call(self, new_value)
111
+ end
112
+ end
113
+
114
+ # Check whether all the given field name or bits are enabled (true or 1)
115
+ # On fields with more than one bit, will return true if any of the bits are
116
+ # enabled (value > 0)
117
+ #
118
+ # @param [Symbol, Integer] fields one or more field names or bit indices
119
+ #
120
+ # @example Check fields to see if they are enabled
121
+ # # The struct is just an example, normally you would define a new class
122
+ # Example = Struct.new('Example', :flags)
123
+ # exo = Example.new(0)
124
+ # bits = Bits.new(exo, {:is_odd => 0, :amount => [1, 2, 3], :is_cool => 4})
125
+ # # we initialized flags to 0, so nothing is enabled
126
+ # bits.enabled?(:is_odd) #=> false
127
+ # bits.enabled?(:amount, :is_cool) #=> false
128
+ # bits.enabled?(10, 5, :is_odd) #=> false
129
+ #
130
+ # # We now change flags on our instance object
131
+ # exo.flags = 5 # is_odd = 1, amount = 2, is_cool = 0
132
+ # bits.enabled?(:is_odd) #=> true
133
+ # bits.enabled?(:amount, :is_cool) #=> false
134
+ # bits.enabled?(:amount, :is_odd) #=> true
135
+ # bits.enabled?(:is_cool) #=> false
136
+ #
137
+ #
138
+ # @return [Boolean] true if ALL the given field bits are enabled
139
+ def enabled?(*fields)
140
+ memo = true
141
+ field = self.field
142
+
143
+ fields.flatten.each do |name|
144
+ break unless memo
145
+ memo &&= (read(name, field) >= 1)
146
+ end
147
+
148
+ memo
149
+ end
150
+
151
+ # Check whether all the given field names or bits are disabled (false or 0)
152
+ # On fields with more than one bit, will return true only if the value of the
153
+ # field is 0 (no bits set).
154
+ #
155
+ # @param [Symbol, Integer] fields one or more field names or bit indices
156
+ #
157
+ # @example Check fields to see if they are disabled
158
+ # # The struct is just an example, normally you would define a new class
159
+ # Example = Struct.new('Example', :flags)
160
+ # exo = Example.new(0)
161
+ # bits = Bits.new(exo, {:is_odd => 0, :amount => [1, 2, 3], :is_cool => 4})
162
+ # # we initialized flags to 0, so everything is disabled
163
+ # bits.disabled?(:is_odd) #=> true
164
+ # bits.disabled?(:amount, :is_cool) #=> true
165
+ # bits.disabled?(10, 5, :is_odd) #=> true
166
+ #
167
+ # # We now change flags on our instance object
168
+ # exo.flags = 5 # is_odd = 1, amount = 2, is_cool = 0
169
+ # bits.disabled?(:is_odd) #=> false
170
+ # bits.disabled?(:amount, :is_cool) #=> false
171
+ # bits.disabled?(:amount, :is_odd) #=> false
172
+ # bits.disabled?(:is_cool) #=> true
173
+ #
174
+ # @return [Boolean] true if ALL the given field bits are disabled (all bits
175
+ # are not set or false)
176
+ def disabled?(*fields)
177
+ memo = true
178
+ field = self.field
179
+
180
+ fields.flatten.each do |name|
181
+ break unless memo
182
+ memo &&= (read(name, field) == 0)
183
+ end
184
+
185
+ memo
186
+ end
187
+
188
+ # Get a BitField instance for the current value.
189
+ #
190
+ # Note: Value changes are NOT tracked and updated into the instance, so call
191
+ # this method directly as needed.
192
+ #
193
+ # @return [BitMagic::BitField] a BitField object with the current value
194
+ def field
195
+ BitField.new(self.value)
196
+ end
197
+
198
+ # Read a field or bit from its bit index or name
199
+ #
200
+ # @param [Symbol, Integer] name either the name of the bit (a key in field_list)
201
+ # or a integer bit position
202
+ # @param [BitField optional] field a specific BitField to read from.
203
+ # default: return value of #field
204
+ #
205
+ # @example Read bit values
206
+ # # The struct is just an example, normally you would define a new class
207
+ # Example = Struct.new('Example', :flags)
208
+ # exo = Example.new(9)
209
+ # bits = Bits.new(exo, {:is_odd => 0, :amount => [1, 2, 3], :is_cool => 4})
210
+ # bits.read(:is_odd) #=> 1
211
+ # bits.read(:amount) #=> 4
212
+ # bits.read(:is_cool) #=> 0
213
+ # bits.read(:amount, BitField.new(78)) #=> 7
214
+ # # Bonus: aliased as []
215
+ # bits[:is_odd] #=> 1
216
+ #
217
+ # @return [Integer] a value of the bit (0 or 1) or bits (number from 0 to
218
+ # (2**bit_length) - 1) or nil if the field name is not in the list
219
+ def read(name, field = nil)
220
+ field ||= self.field
221
+
222
+ if name.is_a?(Integer)
223
+ field.read_field(name)
224
+ elsif bits = @field_list[name]
225
+ field.read_field(bits)
226
+ end
227
+ end
228
+
229
+ alias :[] :read
230
+
231
+ # Write a field or bit from its field name or index
232
+ #
233
+ # Note: only the total bits of the field is used from the given value, so
234
+ # any additional bits are ignored. eg: writing a field with one bit as value
235
+ # of 4 will set the bit to 0, writing 5 sets it to 1.
236
+ #
237
+ # @param [Symbol, Integer, Array<Integer>] name a field name, or bit position,
238
+ # or array of bit positions
239
+ # @param [Integer, Array<Integer>] target_value the target value for the field
240
+ # (note: technically, this can be anything that responds to :[](index), but
241
+ # usage in that type of context is discouraged without adapter support)
242
+ #
243
+ # @example Write values to bit fields
244
+ # # The struct is just an example, normally you would define a new class
245
+ # Example = Struct.new('Example', :flags)
246
+ # exo = Example.new(0)
247
+ # bits = Bits.new(exo, {:is_odd => 0, :amount => [1, 2, 3], :is_cool => 4})
248
+ # bits.write(:is_odd, 1) #=> 1
249
+ # bits.write(:amount, 5) #=> 11
250
+ # exo.flags #=> 11
251
+ # # Bonus, aliased as :[]=, but note in this mode, the return value is same as given value
252
+ # bits[:is_cool] = 1 #=> 1
253
+ # exo.flags #=> 27
254
+ #
255
+ # @return the return value of the updater Proc, usually is equal to the final
256
+ # master value (with all the bits) after writing this bit
257
+ def write(name, target_value)
258
+ if name.is_a?(Symbol)
259
+ self.write(@field_list[name], target_value)
260
+ elsif name.is_a?(Integer)
261
+ self.update self.field.write_bits(name => @options[:bool_caster].call(target_value))
262
+ elsif name.respond_to?(:[]) and target_value.respond_to?(:[])
263
+ bits = {}
264
+
265
+ name.each_with_index do |bit, i|
266
+ bits[bit] = @options[:bool_caster].call(target_value[i])
267
+ end
268
+
269
+ self.update self.field.write_bits bits
270
+ end
271
+ end
272
+
273
+ alias :[]= :write
274
+
275
+ # Inspect output.
276
+ #
277
+ # @return [String] an #inspect value for this instance
278
+ def inspect
279
+ short_options = {}
280
+ short_options[:default] = @options[:default]
281
+ short_options[:attribute_name] = @options[:attribute_name]
282
+ "#<#{self.class.to_s} #{@magician ? "bit_magic=#{@magician.bit_magic_name.inspect} " : nil}value=#{self.value}> options=#{short_options.inspect}"
283
+ end
284
+ end
285
+ end
@@ -0,0 +1,399 @@
1
+ require_relative './bits'
2
+
3
+ module BitMagic
4
+ # This module generates integers that are bit representations of given bits,
5
+ # arrays of possible values with given bits, and arrays of possible values
6
+ # given some condition on the bits.
7
+ #
8
+ # In short, it gives you a list of possible integer values from a list of bits.
9
+ #
10
+ # @attr_reader [Hash] field_list a hash of :name => bit or :name => [bit, bit, ..., bits]
11
+ # key-value pairs for easier access of named bits/bit ranges
12
+ # @attr_reader [Hash] options options given to the generator
13
+ # @attr_reader [Integer] length the total number of bits specified
14
+ # @attr_reader [Integer] bits an array of bits used in field_list, list of
15
+ # all the bits with which we will be working with
16
+ #
17
+ class BitsGenerator
18
+ attr_reader :field_list, :options, :length, :bits
19
+ DEFAULT_OPTIONS = {default: 0, bool_caster: Bits::BOOLEAN_CASTER}.freeze
20
+
21
+ # Initialize the generator.
22
+ #
23
+ # @param [BitMagic::Adapters::Magician, Hash] magician_or_field_list a Magician
24
+ # object that contains field_list, bits, and options OR a Hash object of
25
+ # :name => bit index or bit index array, key-value pairs
26
+ # @param [Hash] options options and defaults, will override Magician action_options
27
+ # if given
28
+ # @option options [Integer] :default a default value, default: 0
29
+ # @option options [Proc] :bool_caster a callable Method, Proc or lambda that
30
+ # is used to cast a value into a boolean
31
+ #
32
+ # @return [BitsGenerator] the resulting object
33
+ def initialize(magician_or_field_list, options = {})
34
+ if defined?(BitMagic::Adapters::Magician) and magician_or_field_list.is_a?(BitMagic::Adapters::Magician)
35
+ @magician = magician_or_field_list
36
+ @field_list = @magician.field_list
37
+ @options = @magician.action_options.merge(options)
38
+ @length = @magician.bits_length
39
+ @bits = @magician.bits.uniq
40
+ else
41
+ @field_list = magician_or_field_list
42
+ @options = DEFAULT_OPTIONS.merge(options)
43
+ @bits = @field_list.values.flatten.uniq
44
+ @length = @bits.length
45
+ end
46
+ end
47
+
48
+ # Given a field name or list of field names, return their corresponding bits
49
+ # Field names are the key values of the field_list hash during initialization
50
+ #
51
+ # @param [Symbol, Integer] one or more keys for the field name or an integer
52
+ # for a known bit index
53
+ #
54
+ # @example Get a list of bits
55
+ # gen = BitMagic::BitsGenerator.new(:is_odd => 0, :amount => [1, 2, 3], :is_cool => 4)
56
+ # gen.bits_for(:is_odd) #=> [0]
57
+ # gen.bits_for(:amount) #=> [1, 2, 3]
58
+ # gen.bits_for(:is_odd, :amount) #=> [0, 1, 2, 3]
59
+ # gen.bits_for(:is_cool, 5, 6) #=> [4, 5, 6]
60
+ # gen.bits_for(9, 10) #=> [9, 10]
61
+ #
62
+ # @return [Array<Integer>] an array of bit indices
63
+ def bits_for(*field_names)
64
+ bits = []
65
+
66
+ field_names.flatten.each do |i|
67
+ if i.is_a?(Integer)
68
+ bits << i
69
+ next
70
+ end
71
+
72
+ if i.respond_to?(:to_sym) and @field_list[i.to_sym]
73
+ bits << @field_list[i.to_sym]
74
+ end
75
+ end
76
+
77
+ bits.flatten
78
+ end
79
+
80
+ # Iterates over the entire combination of all possible values utilizing the
81
+ # list of bits we are given.
82
+ #
83
+ # Warning: Because we are iteration over possible values, the total available
84
+ # values grows exponentially with the given number of bits. For example, if
85
+ # you use only 8 bits, there are 2*8 = 256 possible values, with 20 bits it
86
+ # grows to 2**20 = 1048576. At 32 bits, 2**32 = 4294967296.
87
+ #
88
+ # Warning 2: We're using combinations to generate each individual number, so
89
+ # there's additional overhead causing O(n!) complexity. Use carefully when
90
+ # you have large bit lists (more than 16 bits total). Check timing and memory.
91
+ #
92
+ # @param [optional, Array<Integer>] each_bits a list of bits used to generate
93
+ # the combination list. default: the list of bits given during initialization
94
+ # @param [Proc] block a callable method to yield individual values
95
+ #
96
+ # @yield num will yield to the given block multiple times, each time with one
97
+ # of the integer values that are possible from the given bits list
98
+ #
99
+ # @example Iterate over a list of bits
100
+ # gen = BitsGenerator.new(:is_odd => 0, :amount => [1, 2, 3], :is_cool => 4)
101
+ # values = []
102
+ # gen.each_value { |val| values << val } #=> 32
103
+ # values #=> [0, 1, 2, 4, 8, 16, 3, 5, 9, 17, 6, 10, 18, 12, 20, 24, 7, 11, 19, 13, 21, 25, 14, 22, 26, 28, 15, 23, 27, 29, 30, 31]
104
+ # values2 = []
105
+ # gen.each_value([0, 5]) { |val| values2 << val } #=> 4
106
+ # values2 #=> [0, 1, 32, 33]
107
+ #
108
+ #
109
+ # @return [Integer] total number of values yielded
110
+ def each_value(each_bits = nil, &block)
111
+ # Warning! This has exponential complexity (time and space)
112
+ # 2**n to be precise, use sparingly
113
+
114
+ yield 0
115
+ count = 1
116
+
117
+ if @options[:default] != 0
118
+ yield @options[:default]
119
+ count += 1
120
+ end
121
+
122
+ each_bits = self.bits if each_bits == nil
123
+
124
+ 1.upto(each_bits.length).each do |i|
125
+ each_bits.combination(i).each do |bits_list|
126
+ num = bits_list.reduce(0) { |m, j| m |= (1 << j) }
127
+ yield num
128
+ count += 1
129
+ end
130
+ end
131
+
132
+ count
133
+ end
134
+
135
+ # Gives you an array of all possible integer values based off the bit
136
+ # combinations of the bit list.
137
+ #
138
+ # Note: This will include all possible values from the bit list, but there
139
+ # are no guarantees on their order. If you need an ordered list, sort the result.
140
+ #
141
+ # Warning: Please see the warnings on each_value.
142
+ #
143
+ # Warning: Memory usage grows exponentially to the number of bits! For example,
144
+ # on a 64 bit platform (assuming pointers are 8 bytes) if you have 8 bits,
145
+ # this array will have 256 values, taking up 2KB of memory. At 20 bits, it's
146
+ # 1048576 values, taking up 8MB. At 32 bits, 4294967296 values take up 34GB!
147
+ #
148
+ # @param [optional, Array<Integer>] each_bits a list of bits used to generate
149
+ # the combination list. default: the list of bits given during initialization
150
+ # @param [Hash] opts additional options
151
+ # @option opts [Integer] :warn_threshold will output warning messages if
152
+ # the total number of bits is above this number. false to disable. default: 20
153
+ #
154
+ # @example Get an array for all values from our bit list
155
+ # gen = BitsGenerator.new(:is_odd => 0, :amount => [1, 2, 3], :is_cool => 4)
156
+ # gen.all_values
157
+ #
158
+ # @return [Array<Integer>] an array of all possible values from the bit list
159
+ # Order is not guaranteed to be consistent.
160
+ def all_values(each_bits = nil, opts = {warn_threshold: 12})
161
+ # Shuffle things around so that people can call #all_values(warn_threshold: false)
162
+ if each_bits.is_a?(Hash)
163
+ opts = each_bits
164
+ each_bits = nil
165
+ end
166
+
167
+ each_bits = self.bits if each_bits == nil
168
+
169
+ if opts[:warn_threshold] and each_bits.length > opts[:warn_threshold]
170
+ warn "There are #{each_bits.length} bits. You will have #{2**(each_bits.length)} values in the result. Please carefully benchmark the execution time and memory usage of your use-case."
171
+ warn "You can disable this warning by using #all_values(warn_threshold: false)"
172
+ end
173
+
174
+ values = []
175
+
176
+ self.each_value(each_bits) {|num| values << num }
177
+
178
+ values
179
+ end
180
+
181
+ # Gives you an array of values where at least one of the bits of the field
182
+ # names list is set to true (ie any of the bits are true).
183
+ #
184
+ # Possible values are derived from the bits list during initialization.
185
+ #
186
+ # Note: Order is not guaranteed. All numbers will be present, but there is
187
+ # no expectation that the numbers will be in the same order every time. If
188
+ # you need an ordered list, you can sort the result.
189
+ #
190
+ # @param [Symbol, Integer] one or more keys for the field name or an integer
191
+ # for a known bit index
192
+ #
193
+ # @example Retrieve a list for odd numbers or cool numbers
194
+ # gen = BitsGenerator.new(:is_odd => 0, :amount => [1, 2, 3], :is_cool => 4)
195
+ # gen.any_of(:is_odd) #=> [1, 3, 5, 9, 17, 7, 11, 19, 13, 21, 25, 15, 23, 27, 29, 31]
196
+ # gen.any_of(:is_cool) #=> [16, 17, 18, 20, 24, 19, 21, 25, 22, 26, 28, 23, 27, 29, 30, 31]
197
+ # gen.any_of(:is_odd, :is_cool) # is_odd or is_cool, same as union of arrays above
198
+ # #=> [1, 16, 3, 5, 9, 17, 18, 20, 24, 7, 11, 19, 13, 21, 25, 22, 26, 28, 15, 23, 27, 29, 30, 31]
199
+ #
200
+ #
201
+ # @return [Array<Integer>] an array of integer values with at least one of
202
+ # the bits set (true, bit is 1). Will be blank if no field names given.
203
+ def any_of(*field_names)
204
+ [].tap do |list|
205
+ any_num = any_of_number(*field_names)
206
+ self.each_value { |num| list << num if (num & any_num) > 0 }
207
+ end
208
+ end
209
+ alias :with_any :any_of
210
+
211
+ # Gives you an array of values where all of these bits of the field names
212
+ # list is set to false (ie without all of these bits as true).
213
+ #
214
+ # Possible values are derived from the bits list during initialization.
215
+ #
216
+ # Note: Order is not guaranteed. All numbers will be present, but there is
217
+ # no expectation that the numbers will be in the same order every time. If
218
+ # you need an ordered list, you can sort the result.
219
+ #
220
+ # @param [Symbol, Integer] one or more keys for the field name or an integer
221
+ # for a known bit index
222
+ #
223
+ # @example Retrieve a list for even numbers, or uncool numbers
224
+ # gen = BitsGenerator.new(:is_odd => 0, :amount => [1, 2, 3], :is_cool => 4)
225
+ # gen.none_of(:is_odd) #=> [0, 2, 4, 8, 16, 6, 10, 18, 12, 20, 24, 14, 22, 26, 28, 30]
226
+ # gen.none_of(:is_cool) #=> [0, 1, 2, 4, 8, 3, 5, 9, 6, 10, 12, 7, 11, 13, 14, 15]
227
+ # gen.none_of(:is_odd, :is_cool) #=> [0, 2, 4, 8, 6, 10, 12, 14]
228
+ # gen.none_of(:is_odd, :is_cool, :amount) #=> [0]
229
+ #
230
+ # @return [Array<Integer>] an array of integer values with all bits of given
231
+ # field names unset (false, bit is 0). Will be blank if no field names given.
232
+ def none_of(*field_names)
233
+ [].tap do |list|
234
+ lack_num = any_of_number(*field_names)
235
+ self.each_value { |num| list << num if (num & lack_num) == 0 }
236
+ end
237
+ end
238
+ alias :without_all :none_of
239
+
240
+ # Gives you an array of values where at least one of the bits of the field
241
+ # names list is set to false (ie without any of these bits as true).
242
+ #
243
+ # Possible values are derived from the bits list during initialization.
244
+ #
245
+ # Note: Order is not guaranteed. All numbers will be present, but there is
246
+ # no expectation that the numbers will be in the same order every time. If
247
+ # you need an ordered list, you can sort the result.
248
+ #
249
+ # @param [Symbol, Integer] one or more keys for the field name or an integer
250
+ # for a known bit index
251
+ #
252
+ # @example Retrieve a list for even numbers, and uncool numbers
253
+ # gen = BitsGenerator.new(:is_odd => 0, :amount => [1, 2, 3], :is_cool => 4)
254
+ # gen.instead_of(:is_odd) #=> [0, 2, 4, 8, 16, 6, 10, 18, 12, 20, 24, 14, 22, 26, 28, 30]
255
+ # gen.instead_of(:is_odd, :is_cool) #=> [0, 1, 2, 4, 8, 16, 3, 5, 9, 6, 10, 18, 12, 20, 24, 7, 11, 13, 14, 22, 26, 28, 15, 30]
256
+ # gen.instead_of(:is_odd, :is_cool, :amount) #=> [0, 1, 2, 4, 8, 16, 3, 5, 9, 17, 6, 10, 18, 12, 20, 24, 7, 11, 19, 13, 21, 25, 14, 22, 26, 28, 15, 23, 27, 29, 30]
257
+ #
258
+ # @return [Array<Integer>] an array of integer values with at least one of
259
+ # the bits unset (false, bit is 0). Will be blank if no field names given.
260
+ def instead_of(*field_names)
261
+ [].tap do |list|
262
+ none_num = any_of_number(*field_names)
263
+ self.each_value { |num| list << num if (num & none_num) != none_num }
264
+ end
265
+ end
266
+ alias :without_any :instead_of
267
+
268
+ def all_of(*field_names)
269
+ [].tap do |list|
270
+ all_num = any_of_number(*field_names)
271
+ self.each_value { |num| list << num if (num & all_num) == all_num }
272
+ end
273
+ end
274
+ alias :with_all :all_of
275
+
276
+ # Gives you an array of values where the given field names are exactly
277
+ # equal to their given field values.
278
+ #
279
+ # Possible values are derived from the bits list during initialization.
280
+ #
281
+ # Note: Order is not guaranteed. All numbers will be present, but there is
282
+ # no expectation that the numbers will be in the same order every time. If
283
+ # you need an ordered list, you can sort the result.
284
+ #
285
+ # @param [Hash] one or more field names or an integer for a known bit index
286
+ # as the key, and the value (integer or boolean) for that field as the hash
287
+ # value. Values that have more bits than available will be truncated for
288
+ # comparison. eg. a field with one bit setting value to 2 means field is 0
289
+ #
290
+ # @example Get different amount values
291
+ # gen = BitsGenerator.new(:is_odd => 0, :amount => [1, 2, 3], :is_cool => 4)
292
+ # gen.equal_to(:amount => 5) #=> [10, 11, 26, 27]
293
+ # gen.equal_to(:amount => 7) #=> [14, 15, 30, 31]
294
+ # gen.equal_to(:amount => 7, :is_odd => 1, :is_cool => 1) #=> [31]
295
+ #
296
+ # @return [Array<Integer>] an array of integer values where the bits and values
297
+ # match the given input
298
+ def equal_to(field_values = {})
299
+ all_num, none_num = self.equal_to_numbers(field_values)
300
+
301
+ [].tap do |list|
302
+ self.each_value { |num| list << num if (num & all_num) == all_num and (num & none_num) == 0 }
303
+ end
304
+ end
305
+
306
+ # Will return an array of two numbers, the first of which has all bits set
307
+ # where the corresponding value bit is 1, and the second has all bits set
308
+ # where the corresponding value bit is 0.
309
+ # These numbers can be used in advanced bitwise operations to test fields
310
+ # for exact equality.
311
+ #
312
+ # @param [Hash] one or more field names or an integer for a known bit index
313
+ # as the key, and the value (integer or boolean) for that field as the hash
314
+ # value. Values that have more bits than available will be truncated for
315
+ # comparison. eg. a field with one bit setting value to 2 means field is 0
316
+ #
317
+ # @example Retrieve the representation for various amounts
318
+ # gen = BitsGenerator.new(:is_odd => 0, :amount => [1, 2, 3], :is_cool => 4)
319
+ # gen.equal_to_numbers(:amount => 5) #=> [10, 4]
320
+ # # 5 is 101, 10 is 1010 and 4 is 100. (Note that amount uses bits 1, 2, 3)
321
+ # gen.equal_to_numbers(:amount => 7) #=> [14, 0]
322
+ # gen.equal_to_numbers(:amount => 7, :is_odd => 1) #=> [15, 0]
323
+ #
324
+ #
325
+ # @return [Array<Integer>] an array of two integers, first representing bits
326
+ # of given field bit values as 1 set to 1, and the second representing bits
327
+ # of given field bit values as 0 set to 1. See the example.
328
+ def equal_to_numbers(field_values = {})
329
+ fields = {}
330
+
331
+ field_values.each_pair do |field_name, v|
332
+ bits = self.bits_for(field_name)
333
+ fields[bits] = v if bits.length > 0
334
+ end
335
+
336
+ all_num = 0
337
+ none_num = 0
338
+ fields.each_pair { |field_bits, val|
339
+ field_bits.each_with_index do |bit, i|
340
+ if @options[:bool_caster].call(val[i])
341
+ all_num |= (1 << bit)
342
+ else
343
+ none_num |= (1 << bit)
344
+ end
345
+ end
346
+ }
347
+
348
+ [all_num, none_num]
349
+ end
350
+
351
+ # Get an integer with the given field names' bits all set. This number can
352
+ # be used in bitwise operations to test field conditionals.
353
+ #
354
+ # @param [Symbol, Integer] one or more keys for the field name or an integer
355
+ # for a known bit index
356
+ #
357
+ # @example Get a number representing odd numbers, or an arbitrary amount
358
+ # gen = BitsGenerator.new(:is_odd => 0, :amount => [1, 2, 3], :is_cool => 4)
359
+ # gen.any_of_number(:is_odd) #=> 1 # because 1 in binary is 00001
360
+ # gen.any_of_number(:amount) #=> 14 # because 14 is 01110
361
+ # gen.any_of_number(:is_odd, :amount, :is_cool) #=> 31
362
+ # # 31 is 11111 because all five bits (0 to 4) are true (one)
363
+ #
364
+ #
365
+ # @return [Integer] the integer with all field names' bits are true
366
+ def any_of_number(*field_names)
367
+ self.bits_for(*field_names).reduce(0) { |m, bit| m | (1 << bit) }
368
+ end
369
+ alias :with_any_number :any_of_number
370
+ alias :with_all_number :any_of_number
371
+ alias :all_of_number :any_of_number
372
+
373
+ # Get an integer with the given field names' bits all unset. This number can
374
+ # be used in bitwise operations to test field conditionals.
375
+ #
376
+ # Note: Because of ruby's handling of two's complement, this number is
377
+ # almost always a negative number.
378
+ #
379
+ # @param [Symbol, Integer] one or more keys for the field name or an integer
380
+ # for a known bit index
381
+ #
382
+ # @example Get a number representing even numbers, or an arbitrary amount
383
+ # gen = BitsGenerator.new(:is_odd => 0, :amount => [1, 2, 3], :is_cool => 4)
384
+ # gen.none_of_number(:is_odd) #=> -2 # because -2 in binary is 1...11110
385
+ # gen.none_of_number(:amount) #=> -15
386
+ # # -15 in binary is 1...10001
387
+ # gen.none_of_number(:is_odd, :amount, :is_cool) #=> -32
388
+ # # -32 is 1...00000 because all five bits (0 to 4) are false (zero)
389
+ #
390
+ #
391
+ # @return [Integer] the integer with all the field names' bits set to false
392
+ def none_of_number(*field_names)
393
+ ~self.any_of_number(*field_names)
394
+ end
395
+ alias :without_any_number :none_of_number
396
+ alias :without_all_number :none_of_number
397
+
398
+ end
399
+ end