Class: BitMagic::BitsGenerator
- Inherits:
-
Object
- Object
- BitMagic::BitsGenerator
- Defined in:
- lib/bit_magic/bits_generator.rb
Overview
This module generates integers that are bit representations of given bits, arrays of possible values with given bits, and arrays of possible values given some condition on the bits.
In short, it gives you a list of possible integer values from a list of bits.
Constant Summary collapse
- DEFAULT_OPTIONS =
{default: 0, bool_caster: Bits::BOOLEAN_CASTER}.freeze
Instance Attribute Summary collapse
-
#bits ⇒ Integer
readonly
an array of bits used in field_list, list of all the bits with which we will be working with.
-
#field_list ⇒ Hash
readonly
a hash of :name => bit or :name => [bit, bit, …, bits] key-value pairs for easier access of named bits/bit ranges.
-
#length ⇒ Integer
readonly
the total number of bits specified.
-
#options ⇒ Hash
readonly
options given to the generator.
Instance Method Summary collapse
- #all_of(*field_names) ⇒ Object (also: #with_all)
-
#all_values(each_bits = nil, opts = {warn_threshold: 12}) ⇒ Array<Integer>
Gives you an array of all possible integer values based off the bit combinations of the bit list.
-
#any_of(*field_names) ⇒ Array<Integer>
(also: #with_any)
Gives you an array of values where at least one of the bits of the field names list is set to true (ie any of the bits are true).
-
#any_of_number(*field_names) ⇒ Integer
(also: #with_any_number, #with_all_number, #all_of_number)
Get an integer with the given field names’ bits all set.
-
#bits_for(*field_names) ⇒ Array<Integer>
Given a field name or list of field names, return their corresponding bits Field names are the key values of the field_list hash during initialization.
-
#each_value(each_bits = nil, &block) { ... } ⇒ Integer
Iterates over the entire combination of all possible values utilizing the list of bits we are given.
-
#equal_to(field_values = {}) ⇒ Array<Integer>
Gives you an array of values where the given field names are exactly equal to their given field values.
-
#equal_to_numbers(field_values = {}) ⇒ Array<Integer>
Will return an array of two numbers, the first of which has all bits set where the corresponding value bit is 1, and the second has all bits set where the corresponding value bit is 0.
-
#initialize(magician_or_field_list, options = {}) ⇒ BitsGenerator
constructor
Initialize the generator.
-
#instead_of(*field_names) ⇒ Array<Integer>
(also: #without_any)
Gives you an array of values where at least one of the bits of the field names list is set to false (ie without any of these bits as true).
-
#none_of(*field_names) ⇒ Array<Integer>
(also: #without_all)
Gives you an array of values where all of these bits of the field names list is set to false (ie without all of these bits as true).
-
#none_of_number(*field_names) ⇒ Integer
(also: #without_any_number, #without_all_number)
Get an integer with the given field names’ bits all unset.
Constructor Details
#initialize(magician_or_field_list, options = {}) ⇒ BitsGenerator
Initialize the generator.
33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/bit_magic/bits_generator.rb', line 33 def initialize(magician_or_field_list, = {}) if defined?(BitMagic::Adapters::Magician) and magician_or_field_list.is_a?(BitMagic::Adapters::Magician) @magician = magician_or_field_list @field_list = @magician.field_list @options = @magician..merge() @length = @magician.bits_length @bits = @magician.bits.uniq else @field_list = magician_or_field_list @options = DEFAULT_OPTIONS.merge() @bits = @field_list.values.flatten.uniq @length = @bits.length end end |
Instance Attribute Details
#bits ⇒ Integer (readonly)
an array of bits used in field_list, list of all the bits with which we will be working with
17 18 19 |
# File 'lib/bit_magic/bits_generator.rb', line 17 def bits @bits end |
#field_list ⇒ Hash (readonly)
a hash of :name => bit or :name => [bit, bit, …, bits] key-value pairs for easier access of named bits/bit ranges
17 18 19 |
# File 'lib/bit_magic/bits_generator.rb', line 17 def field_list @field_list end |
#length ⇒ Integer (readonly)
the total number of bits specified
17 18 19 |
# File 'lib/bit_magic/bits_generator.rb', line 17 def length @length end |
#options ⇒ Hash (readonly)
options given to the generator
17 18 19 |
# File 'lib/bit_magic/bits_generator.rb', line 17 def @options end |
Instance Method Details
#all_of(*field_names) ⇒ Object Also known as: with_all
269 270 271 272 273 274 |
# File 'lib/bit_magic/bits_generator.rb', line 269 def all_of(*field_names) [].tap do |list| all_num = any_of_number(*field_names) self.each_value { |num| list << num if (num & all_num) == all_num } end end |
#all_values(each_bits = nil, opts = {warn_threshold: 12}) ⇒ Array<Integer>
Gives you an array of all possible integer values based off the bit combinations of the bit list.
Note: This will include all possible values from the bit list, but there are no guarantees on their order. If you need an ordered list, sort the result.
Warning: Please see the warnings on each_value.
Warning: Memory usage grows exponentially to the number of bits! For example, on a 64 bit platform (assuming pointers are 8 bytes) if you have 8 bits, this array will have 256 values, taking up 2KB of memory. At 20 bits, it’s 1048576 values, taking up 8MB. At 32 bits, 4294967296 values take up 34GB!
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'lib/bit_magic/bits_generator.rb', line 161 def all_values(each_bits = nil, opts = {warn_threshold: 12}) # Shuffle things around so that people can call #all_values(warn_threshold: false) if each_bits.is_a?(Hash) opts = each_bits each_bits = nil end each_bits = self.bits if each_bits == nil if opts[:warn_threshold] and each_bits.length > opts[:warn_threshold] 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." warn "You can disable this warning by using #all_values(warn_threshold: false)" end values = [] self.each_value(each_bits) {|num| values << num } values end |
#any_of(*field_names) ⇒ Array<Integer> Also known as: with_any
Gives you an array of values where at least one of the bits of the field names list is set to true (ie any of the bits are true).
Possible values are derived from the bits list during initialization.
Note: Order is not guaranteed. All numbers will be present, but there is no expectation that the numbers will be in the same order every time. If you need an ordered list, you can sort the result.
204 205 206 207 208 209 |
# File 'lib/bit_magic/bits_generator.rb', line 204 def any_of(*field_names) [].tap do |list| any_num = any_of_number(*field_names) self.each_value { |num| list << num if (num & any_num) > 0 } end end |
#any_of_number(*field_names) ⇒ Integer Also known as: with_any_number, with_all_number, all_of_number
Get an integer with the given field names’ bits all set. This number can be used in bitwise operations to test field conditionals.
367 368 369 |
# File 'lib/bit_magic/bits_generator.rb', line 367 def any_of_number(*field_names) self.bits_for(*field_names).reduce(0) { |m, bit| m | (1 << bit) } end |
#bits_for(*field_names) ⇒ Array<Integer>
Given a field name or list of field names, return their corresponding bits Field names are the key values of the field_list hash during initialization
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/bit_magic/bits_generator.rb', line 63 def bits_for(*field_names) bits = [] field_names.flatten.each do |i| if i.is_a?(Integer) bits << i next end if i.respond_to?(:to_sym) and @field_list[i.to_sym] bits << @field_list[i.to_sym] end end bits.flatten end |
#each_value(each_bits = nil, &block) { ... } ⇒ Integer
Iterates over the entire combination of all possible values utilizing the list of bits we are given.
Warning: Because we are iteration over possible values, the total available values grows exponentially with the given number of bits. For example, if you use only 8 bits, there are 2*8 = 256 possible values, with 20 bits it grows to 2**20 = 1048576. At 32 bits, 2**32 = 4294967296.
Warning 2: We’re using combinations to generate each individual number, so there’s additional overhead causing O(n * 2^(n-1)) time complexity. Carefully
benchmark when you have large bit lists (more than 16 bits total) and check both timing and memory for your use case.
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/bit_magic/bits_generator.rb', line 111 def each_value(each_bits = nil, &block) # Warning! This has exponential complexity (time and space) # 2**n to be precise, use sparingly yield 0 count = 1 if @options[:default] != 0 yield @options[:default] count += 1 end each_bits = self.bits if each_bits == nil 1.upto(each_bits.length).each do |i| each_bits.combination(i).each do |bits_list| num = bits_list.reduce(0) { |m, j| m |= (1 << j) } yield num count += 1 end end count end |
#equal_to(field_values = {}) ⇒ Array<Integer>
Gives you an array of values where the given field names are exactly equal to their given field values.
Possible values are derived from the bits list during initialization.
Note: Order is not guaranteed. All numbers will be present, but there is no expectation that the numbers will be in the same order every time. If you need an ordered list, you can sort the result.
299 300 301 302 303 304 305 |
# File 'lib/bit_magic/bits_generator.rb', line 299 def equal_to(field_values = {}) all_num, none_num = self.equal_to_numbers(field_values) [].tap do |list| self.each_value { |num| list << num if (num & all_num) == all_num and (num & none_num) == 0 } end end |
#equal_to_numbers(field_values = {}) ⇒ Array<Integer>
Will return an array of two numbers, the first of which has all bits set where the corresponding value bit is 1, and the second has all bits set where the corresponding value bit is 0. These numbers can be used in advanced bitwise operations to test fields for exact equality.
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
# File 'lib/bit_magic/bits_generator.rb', line 329 def equal_to_numbers(field_values = {}) fields = {} field_values.each_pair do |field_name, v| bits = self.bits_for(field_name) fields[bits] = v if bits.length > 0 end all_num = 0 none_num = 0 fields.each_pair { |field_bits, val| field_bits.each_with_index do |bit, i| if @options[:bool_caster].call(val[i]) all_num |= (1 << bit) else none_num |= (1 << bit) end end } [all_num, none_num] end |
#instead_of(*field_names) ⇒ Array<Integer> Also known as: without_any
Gives you an array of values where at least one of the bits of the field names list is set to false (ie without any of these bits as true).
Possible values are derived from the bits list during initialization.
Note: Order is not guaranteed. All numbers will be present, but there is no expectation that the numbers will be in the same order every time. If you need an ordered list, you can sort the result.
261 262 263 264 265 266 |
# File 'lib/bit_magic/bits_generator.rb', line 261 def instead_of(*field_names) [].tap do |list| none_num = any_of_number(*field_names) self.each_value { |num| list << num if (num & none_num) != none_num } end end |
#none_of(*field_names) ⇒ Array<Integer> Also known as: without_all
Gives you an array of values where all of these bits of the field names list is set to false (ie without all of these bits as true).
Possible values are derived from the bits list during initialization.
Note: Order is not guaranteed. All numbers will be present, but there is no expectation that the numbers will be in the same order every time. If you need an ordered list, you can sort the result.
233 234 235 236 237 238 |
# File 'lib/bit_magic/bits_generator.rb', line 233 def none_of(*field_names) [].tap do |list| lack_num = any_of_number(*field_names) self.each_value { |num| list << num if (num & lack_num) == 0 } end end |
#none_of_number(*field_names) ⇒ Integer Also known as: without_any_number, without_all_number
Get an integer with the given field names’ bits all unset. This number can be used in bitwise operations to test field conditionals.
Note: Because of ruby’s handling of two’s complement, this number is
almost always a negative number.
393 394 395 |
# File 'lib/bit_magic/bits_generator.rb', line 393 def none_of_number(*field_names) ~self.any_of_number(*field_names) end |