--- /dev/null
+BitsInChar = 8
+
+# Error class for all exceptions to do with BitArray.
+class BitArrayError < StandardError; end
+
+# Class containing methods for creating and manipulating a bit array.
+class BitArray
+ attr_reader :size, :byte_array
+
+ # Method to initialize a new bit array of a given size.
+ def initialize(size)
+ @size = size
+ @byte_array = init_byte_array
+ @count_table = init_count_table
+ end
+
+ # Method to set a bit to 1 at a given position in the bit array.
+ def bit_set(pos)
+ raise BitArrayError, "Position #{pos} must be an integer." unless pos.is_a? Fixnum
+ raise BitArrayError, "Position #{pos} outside of range: 0 ... #{@size}" unless (0 ... @size ).include? pos
+
+ @byte_array[byte_pos(pos)] = (@byte_array[byte_pos(pos)].ord | bit_pos(pos)).chr
+ end
+
+ # Method to check if a bit at a given position in the bit array is set.
+ def bit_set?(pos)
+ raise BitArrayError, "Position #{pos} must be an integer." unless pos.is_a? Fixnum
+ raise BitArrayError, "Position #{pos} outside of range: 0 ... #{@size}" unless (0 ... @size ).include? pos
+
+ (@byte_array[byte_pos(pos)].ord & bit_pos(pos)) != 0
+ end
+
+ # Method that returns the number of bits set "on" in a bit array.
+ def bits_on1
+ bits_on = 0
+
+ (0 ... @size).each do |pos|
+ bits_on += 1 if bit_set?(pos)
+ end
+
+ bits_on
+ end
+
+ # Method that returns the number of bits set "on" in a bit array.
+ def bits_on
+ bits_on = 0
+
+ (0 ... self.byte_array.size).each do |byte|
+ bits_on += @count_table[byte]
+ end
+
+ bits_on
+ end
+
+ # Method that returns the number of bits set "off" in a bit array.
+ def bits_off
+ @size - bits_on
+ end
+
+ # Method to run bitwise AND (&) on two bit arrays and return the
+ # result in a new bit array. Bits are copied if they exists in BOTH operands.
+ # 00111100 & 00001101 = 00001100
+ def &(ba)
+ raise BitArrayError, "uneven size of bit arrays: #{self.size} vs #{ba.size}" if self.size != ba.size
+
+ result = BitArray.new(ba.size)
+
+ (0 ... ba.byte_array.size).each do |byte|
+ result.byte_array[byte] = (self.byte_array[byte].ord & ba.byte_array[byte].ord).chr
+ end
+
+ result
+ end
+
+ # Method to run bitwise OR (|) on two bit arrays and return the
+ # result in a new bit array. Bits are copied if they exists in EITHER operands.
+ # 00111100 | 00001101 = 00111101
+ def |(ba)
+ raise BitArrayError, "uneven size of bit arrays: #{self.size} vs #{ba.size}" if self.size != ba.size
+
+ result = BitArray.new(ba.size)
+
+ (0 ... ba.byte_array.size).each do |byte|
+ result.byte_array[byte] = (self.byte_array[byte].ord | ba.byte_array[byte].ord).chr
+ end
+
+ result
+ end
+
+ # Method to run bitwise XOR (^) on two bit arrays and return the
+ # result in a new bit array. Bits are copied if they exists in ONE BUT NOT BOTH operands.
+ # 00111100 ^ 00001101 = 00110001
+ def ^(ba)
+ raise BitArrayError, "uneven size of bit arrays: #{self.size} vs #{ba.size}" if self.size != ba.size
+
+ result = BitArray.new(ba.size)
+
+ (0 ... ba.byte_array.size).each do |byte|
+ result.byte_array[byte] = (self.byte_array[byte].ord ^ ba.byte_array[byte].ord).chr
+ end
+
+ result
+ end
+
+ # Method to convert a bit array to a string.
+ def to_s
+ string = ""
+
+ (0 ... @size).each do |pos|
+ if self.bit_set? pos
+ string << "1"
+ else
+ string << "0"
+ end
+ end
+
+ string
+ end
+
+ private
+
+ # Method to initialize the byte array (string) that constitutes the bit array.
+ def init_byte_array
+ raise BitArrayError, "Size must be an integer, not #{@size}" unless @size.is_a? Fixnum
+ raise BitArrayError, "Size must be positive, not #{@size}" unless @size > 0
+
+ byte_array = ""
+ byte_array << 0.chr * (((@size - 1) / BitsInChar) + 1)
+
+ byte_array
+ end
+
+ # Method that creates a table with number of bits set.
+ def init_count_table
+ count_table = []
+
+ bits = 0
+
+ (0 ... BitsInChar).each do |i|
+ (0 ... BitsInChar).each do |c|
+ bits += 1
+ end
+ end
+
+ puts bits
+ end
+
+ # Method that returns the byte position in the byte array for a given bit position.
+ def byte_pos(pos)
+ pos / BitsInChar
+ end
+
+ # Method that returns the bit position in a byte.
+ def bit_pos(pos)
+ 1 << (BitsInChar - 1 - (pos % BitsInChar))
+ end
+end
+
--- /dev/null
+#!/usr/bin/env ruby
+
+require 'bitarray'
+require 'test/unit'
+require 'pp'
+
+class TestBitArray < Test::Unit::TestCase
+ def setup
+ @ba = BitArray.new(10)
+ end
+
+ def test_BitArray_initialize_raises_on_bad_sizes
+ [ -1, 0, 1.1, "a" ].each do |size|
+ assert_raise(BitArrayError) { BitArray.new(size) }
+ end
+ end
+
+ def test_BitArray_initialize_dont_raise_on_ok_sizes
+ [ 1, 10, 1000 ].each do |size|
+ assert_nothing_raised { BitArray.new(size) }
+ end
+ end
+
+ def test_BitArray_size_returns_correctly
+ assert_equal(10, @ba.size)
+ end
+
+ def test_BitArray_to_s_returns_correctly
+ assert_equal("0000000000", @ba.to_s)
+ end
+
+ def test_BitArray_bit_set_with_bad_pos_raises
+ [-1, 10, 1.1].each do |pos|
+ assert_raise(BitArrayError) { @ba.bit_set(pos) }
+ end
+ end
+
+ def test_BitArray_bit_set
+ str = "0000000000"
+
+ (0.upto 9).each do |pos|
+ @ba.bit_set(pos)
+ str[pos] = "1"
+ assert_equal(str, @ba.to_s)
+ end
+ end
+
+ def test_BitArray_bit_set_questionmark_with_bad_pos_raises
+ [-1, 10, 1.1].each do |pos|
+ assert_raise(BitArrayError) { @ba.bit_set?(pos) }
+ end
+ end
+
+ def test_BitArray_bit_set_questionmark
+ (0.upto 9).each do |pos|
+ @ba.bit_set(pos)
+ assert_equal(true, @ba.bit_set?(pos))
+ end
+ end
+
+ def test_BitArray_bits_on_returns_correctly
+ @ba.bit_set(4)
+ assert_equal(1, @ba.bits_on)
+ end
+
+ def test_BitArray_bits_off_returns_correctly
+ @ba.bit_set(4)
+ assert_equal(9, @ba.bits_off)
+ end
+
+ def test_BitArray_and_with_uneven_sizes_raises
+ ba = BitArray.new(11)
+ assert_raise(BitArrayError) { @ba & ba }
+ end
+
+ def test_BitArray_AND_returns_correctly
+ ba = BitArray.new(10)
+ @ba.bit_set(4)
+ @ba.bit_set(5)
+ ba.bit_set(5)
+ ba.bit_set(6)
+ assert_equal( "0000010000", (@ba & ba).to_s)
+ end
+
+ def test_BitArray_AND_EQUAL_returns_correctly
+ ba = BitArray.new(10)
+ @ba.bit_set(4)
+ @ba.bit_set(5)
+ ba.bit_set(5)
+ ba.bit_set(6)
+ @ba &= ba
+ assert_equal( "0000010000", @ba.to_s)
+ end
+
+ def test_BitArray_OR_returns_correctly
+ ba = BitArray.new(10)
+ @ba.bit_set(4)
+ @ba.bit_set(5)
+ ba.bit_set(5)
+ ba.bit_set(6)
+ assert_equal( "0000111000", (@ba | ba).to_s)
+ end
+
+ def test_BitArray_XOR_returns_correctly
+ ba = BitArray.new(10)
+ @ba.bit_set(4)
+ @ba.bit_set(5)
+ ba.bit_set(5)
+ ba.bit_set(6)
+ assert_equal( "0000101000", (@ba ^ ba).to_s)
+ end
+end
+
+
+__END__
+