Ruby Bit Manipulation

delbetu

M Bellucci

Posted on December 18, 2019

Ruby Bit Manipulation

Some days ago as part of a recruiting process, I faced a code-challenge on Devskiller.
One of the challenges requires using an integer to represents the Groups that a User belongs to.
For example 101 means that the user belongs to Group0 and Group2 but not to Group1.

7 years ago I probably would pass this challenge but since this challenge is assessing the most underused feature of the language for a web-developer and it is time-constrained, I didn't pass :'( .

So if you want to succeed on this kind of challenges make sure you can unerstand this code:

# operators Description
# &   AND
# |   OR
# ^   XOR
# ~   NOT
# <<  left shift
# >>  right shift

# Calculating the binary version of the number (.to_s(2) doesn't work for negated values ~x)
def binary_string(number)
  31.downto(0).reduce('') {|result, index| result += number[index].to_s }
end
alias :bs :binary_string

# returns the value of the bit at the position index
# index 0 is the last bit
def get_bit(number, index)
  # since Ruby is awesome this also could be implemented as number[index]

  ith_one = 1 << index       # 0000000000100
  ith_bit = number & ith_one # 1101101101X01 & 0000000000100 => 0000000000X00
  ith_bit >> index           # 000000000000X
end

# puts a 1 in the index position of number starting from right to left
# doesn't change its parameter instead returns a new number
# index 0 is the less significant bit
def set_bit(number, index)
  ith_one = 1 << index # 00000000010000000
  number | ith_one
end

# puts a 0 in the index position of number starting on 0 from right to left
# index 0 is the less significant bit
def clear_bit(number, index)
  mask = ~(1 << index)
  number & mask
end

# param value must be 0 or 1
def update_bit(number, index, value)
  mask = ~(1 << index)
  (number & mask) | (value << i)
end

x = 5
zeroes = 0 # 000000000000000000000000000000
ones = ~0 # 111111111111111111111111111111

########################## XOR ##########################
p "#{ x ^ zeroes } == #{ x }"
p "#{ bs(x ^ ones) } == #{bs(~x)}" # using bs because to_s prints a negative binary
p "#{ x ^ x } == #{0}"

########################## OR ##########################
p "#{ x | zeroes } == #{ x }"
p "#{ x | ones } == #{ones}"
p "#{ x | x } == #{x}"

########################## AND ##########################
p "#{ x & zeroes } == #{ zeroes }"
p "#{ x & ones } == #{x}"
p "#{ x & x } == #{x}"

# Convert into array
0b01010101.to_s(2).split('')
# => ["1", "0", "1", "0", "1", "0", "1"]


################################## Sample Problem #################################################
# Insertion:
#   You are given two 32-bit numbers, N and M, and two bit positions, i and j.
#   Write a method to insert M into N such that M starts at bit j and ends at bit i.
#   You can assume that the bits j through i have enough space to fit all of M.
#
#   That is, if M = 10011, you can assume that there are at least 5 bits between j and i.
#   You would not, for example, have j = 3 and i = 2, because M could not fully fit between bit 3 and bit 2.
#
# EXAMPLE
# Input:
#   N = 10000000000, M = 10011, i = 2, j = 6
# Output:
#   N = 10001001100
###################################################################################################

def merge_bits_at(n, m, i, j)
  # m = M4 M3 M2 M1 M0
  # m1 = 0  0  M3 M2 M1 M0 0 0
  # n = N7 N6 N5 N4 N3 N2 N1 N0
  # n1 = N7 N6 0  0  0  0  N1 N0
  # n1 | m1 = N7 N6 M3  M2  M1  M0  N1 N0

  m1 = ( m << i )      # 0  0  M3 M2 M1 M0 0 0

  ones = ~0
  x1 = ones << i      # 1 1 1 1 1 1 0 0
  x2 = ~( ones << j ) # 0 0 1 1 1 1 1 1
  mask = x1 & x2      # 0 0 1 1 1 1 0 0
  mask = ~mask        # 1 1 0 0 0 0 1 1

  n1 = n & mask       # N7 N6 0  0  0  0  N1 N0

  m1 | n1
end


n, m, i, j = 0b10000000, 0b10011, 2, 6
puts "N = #{bs(n)}"
puts "M = #{bs(m)}"
puts "i = #{i}, j = #{j}"
puts 'Calling merge_bits_at(10000000, 10011, 2, 6)'
merged_bits = merge_bits_at(n, m, i, j)
puts "#{ bs(merged_bits) } should be 11001100"


#### Using string representation ####
( "101".to_i(2) & "110".to_i(2) ).to_s(2)

Enter fullscreen mode Exit fullscreen mode
💖 💪 🙅 🚩
delbetu
M Bellucci

Posted on December 18, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Ruby Bit Manipulation
ruby Ruby Bit Manipulation

December 18, 2019