bit_magic 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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