require 'inline'
-# Error class for all exceptions to do with Base36.
+# Error class for all exceptions to do with BitMap.
class BitMapError < StandardError; end
-BitsInChar = 8
-
+CHAR_BIT = 8
+
+# Bitmap - 13 columns, 3 rows:
+# 1000000000000
+# 0000001000000
+# 0000000000001
+#
+# Bytestring:
+# byte 0 byte 1 byte 2 byte 3 byte 4 byte 5
+# +--------+--------+--------+--------+--------+--------+
+# |10000000|00000000|00000010|00000000|00000000|00000001|
+# +--------+--------+--------+--------+--------+--------+
+#
+# Padding:
+# row 0 row 1 row 2
+# 10000000 00000xxx 00000010 00000xxx 00000000 00001xxx
class BitMap
- attr_accessor :byte_array
+ attr_accessor :cols, :rows, :byte_array
+
+ # Method to initialize a BitMap.
+ def initialize(cols, rows)
+ @cols = cols
+ @rows = rows
+ @pads = padding
+ size = ((rows * (cols + @pads)) / CHAR_BIT)
+ @byte_array = "\0" * size
+ end
+
+ # Method that set bits to 1 at specified columns, and rows.
+ # Usage: set(1, 4)
+ # set(true, 4)
+ # set([0, 4, 5], true)
+ # set([0 .. 4], true)
+ # TODO: set(-1, true)
+ # TODO: set([1 .. -2], true)
+ def set(cols, rows)
+ cols = cols_expand(cols)
+ rows = rows_expand(rows)
+
+ set_c(cols, rows)
+ end
- def initialize(rows, cols, byte_array = nil)
- @rows = rows
- @cols = cols
- @size = rows * cols
+ # Method that set bits to 0 at specified columns, and rows.
+ # Usage: unset(1, 4)
+ # unset(true, 4)
+ # unset([0, 4, 5], true)
+ # unset([0 .. 4], true)
+ # TODO: unset(-1, true)
+ # TODO: unset([1 .. -2], true)
+ def unset(cols, rows)
+ cols = cols_expand(cols)
+ rows = rows_expand(rows)
+
+ unset_c(cols, rows)
+ end
+
+ # Method to get a slice from a bitmap from specified rows and columns,
+ # and return this slice as a new BitMap object.
+ # Usage: slice(1, 4)
+ # slice(true, 4)
+ # slice([0, 4, 5], true)
+ # slice([0 .. 4], true)
+ # TODO: slice(-1, true)
+ # TODO: slice([1 .. -2], true)
+ def slice(cols, rows)
+ cols = cols_expand(cols)
+ rows = rows_expand(rows)
+ bm = BitMap.new(cols.size, rows.size)
+
+ slice_c(bm.byte_array, cols, rows)
+
+ bm
+ end
- if byte_array.nil?
- @byte_array_size = (rows * cols.to_f / BitsInChar).ceil
- @byte_array = "\0" * @byte_array_size
+ # Method that performs bit wise and operation between:
+ # 1: an entire bit map.
+ # 2: a bit map row.
+ # 3: a bit map column.
+ # The result is saved in self.
+ def &(bit_map)
+ bm = BitMap.new(@cols, @rows)
+
+ if @rows == bit_map.rows and @cols == bit_map.cols
+ bitwise_and_c(bit_map.byte_array)
+ elsif @rows == bit_map.rows
+ bitwise_and_col_c(bit_map.byte_array)
+ elsif @cols == bit_map.cols
+ bitwise_and_row_c(self.byte_array, bm.byte_array, bit_map.byte_array, @cols, @rows)
else
- @byte_array_size = (rows * cols.to_f / BitsInChar).ceil
- @byte_array = byte_array
+ raise BitMapError, "can't & uneven bit maps: #{@rows} x #{@cols} & #{bit_map.rows} x #{bit_map.cols}"
end
+
+ bm
end
- def slice(rows, cols)
- rows = rows.first.to_a if rows.first.respond_to? :to_a
- cols = cols.first.to_a if cols.first.respond_to? :to_a
+ # Method to convert a bit array to a string.
+ def to_s
+ max_rows = 20
+ max_cols = 60
+ rows = []
+
+ (0 ... @rows).each do |r|
+ row = ""
+ (0 ... @cols).each do |c|
+ pos = r * (@cols + @pads) + c
+
+ if self.set? pos
+ row << "1"
+ else
+ row << "0"
+ end
+ end
- new_byte_array_size = (rows.size * cols.size.to_f / BitsInChar).ceil
- new_byte_array = "\0" * new_byte_array_size
+ if @cols > max_cols
+ row = row[0 ... max_cols] + " ... (#{@cols})"
+ end
- slice_c(new_byte_array, rows, cols)
+ if rows.size > max_rows
+ rows << "..."
+ rows << "(#{@rows})"
+ break
+ end
- BitMap.new(rows.size, cols.size, new_byte_array)
+ rows << row
+ end
+
+ rows.join($/)
end
- def flip!
- @rows, @cols = @cols, @rows
+ private
+
+ def padding
+ (((@cols % CHAR_BIT) != 0) ? (CHAR_BIT - (@cols % CHAR_BIT)) : 0)
end
- # Method to convert a bit array to a string.
- def to_s
- string = ""
+ def rows_expand(rows)
+ if rows.is_a? Fixnum
+ rows = [rows]
+ elsif rows == true
+ rows = (0 ... @rows).to_a
+ elsif rows.is_a? Array and rows.first.is_a? Range
+ rows = rows.first.to_a
+ rows = [0] if rows.empty? # FIXME
+ end
- (0 ... @size).each do |pos|
- if self.bit_set? pos
- string << "1"
- else
- string << "0"
- end
+ raise BitMapError, "row outside bitmap: #{rows.first}" if rows.first < 0
+ raise BitMapError, "row outside bitmap: #{rows.last}" if rows.last >= @rows
+
+ rows
+ end
+
+ def cols_expand(cols)
+ if cols.is_a? Fixnum
+ cols = [cols]
+ elsif cols == true
+ cols = (0 ... @cols).to_a
+ elsif cols.is_a? Array and cols.first.is_a? Range
+ cols = cols.first.to_a
end
- string.gsub(/.{#{@cols}}/, "\\0#{$/}")
+ raise BitMapError, "column outside bitmap: #{cols.first}" if cols.first < 0
+ raise BitMapError, "column outside bitmap: #{cols.last}" if cols.last >= @cols
+
+ cols
end
inline do |builder|
- builder.add_static "rows", 'rb_intern("@rows")', "ID"
- builder.add_static "cols", 'rb_intern("@cols")', "ID"
- builder.add_static "size", 'rb_intern("@size")', "ID"
- builder.add_static "byte_array_size", 'rb_intern("@byte_array_size")', "ID"
- builder.add_static "byte_array", 'rb_intern("@byte_array")', "ID"
+ builder.add_static "rows", 'rb_intern("@rows")', "ID"
+ builder.add_static "cols", 'rb_intern("@cols")', "ID"
+ builder.add_static "byte_array", 'rb_intern("@byte_array")', "ID"
builder.prefix %s{
unsigned char bit_count[256] = {
#define COUNT_BITS(c) (bit_count[c])
}
+ builder.prefix %{
+ #define PADDING(cols) (((cols % CHAR_BIT) != 0) ? (CHAR_BIT - (cols % CHAR_BIT)) : 0)
+ }
+
+ builder.prefix %{
+ #define BYTE_SIZE(rows, cols) (rows * (cols + PADDING(cols)) / CHAR_BIT)
+ }
+
builder.prefix %{
#define BYTE_POS(pos) (pos / CHAR_BIT)
}
}
builder.c %{
- void fill_c()
+ VALUE fill_c()
{
- VALUE _size = rb_ivar_get(self, byte_array_size);
VALUE _byte_array = rb_ivar_get(self, byte_array);
- int size = NUM2INT(_size);
+ VALUE _rows = rb_ivar_get(self, rows);
+ VALUE _cols = rb_ivar_get(self, cols);
unsigned char* byte_array = StringValuePtr(_byte_array);
+ int rows = NUM2INT(_rows);
+ int cols = NUM2INT(_cols);
+ int size = BYTE_SIZE(rows, cols);
int i = 0;
for (i = 0; i < size; i++) byte_array[i] = ~0;
+
+ return self;
}
}
builder.c %{
- void empty_c()
+ VALUE empty_c()
{
- VALUE _size = rb_ivar_get(self, byte_array_size);
VALUE _byte_array = rb_ivar_get(self, byte_array);
- int size = NUM2INT(_size);
+ VALUE _rows = rb_ivar_get(self, rows);
+ VALUE _cols = rb_ivar_get(self, cols);
unsigned char* byte_array = StringValuePtr(_byte_array);
+ int rows = NUM2INT(_rows);
+ int cols = NUM2INT(_cols);
+ int size = BYTE_SIZE(rows, cols);
int i = 0;
for (i = 0; i < size; i++) byte_array[i] = 0;
+
+ return self;
}
}
builder.c %{
- void bit_set_c(int row, int col)
+ VALUE bit_test_c(VALUE _pos)
{
- VALUE _rows = rb_ivar_get(self, rows);
- VALUE _cols = rb_ivar_get(self, cols);
+ int pos = NUM2INT(_pos);
VALUE _byte_array = rb_ivar_get(self, byte_array);
- int rows = NUM2INT(_rows);
- int cols = NUM2INT(_cols);
unsigned char* byte_array = StringValuePtr(_byte_array);
- int pos = 0;
- if (row >= rows)
- rb_raise(rb_eval_string("BitMapError"), "row out of range: %d >= %d\\n", row, rows);
+ return ((byte_array[BYTE_POS(pos)] & BIT_POS(pos)) != 0);
+ }
+ }
+
+ builder.c %{
+ void set_c(VALUE _cols_array, VALUE _rows_array)
+ {
+ VALUE _byte_array = rb_ivar_get(self, byte_array);
+ VALUE _cols = rb_ivar_get(self, cols);
+ unsigned char* byte_array = StringValuePtr(_byte_array);
+ int cols = NUM2INT(_cols);
- if (col >= cols)
- rb_raise(rb_eval_string("BitMapError"), "col out of range: %d >= %d\\n", col, cols);
+ VALUE *cols_array = RARRAY_PTR(_cols_array);
+ VALUE *rows_array = RARRAY_PTR(_rows_array);
+ int col_size = (int) RARRAY_LEN(_cols_array);
+ int row_size = (int) RARRAY_LEN(_rows_array);
+ int c = 0;
+ int r = 0;
+ int pad = PADDING(cols);
+ int pos = 0;
- pos = row * cols + col;
+ for (r = 0; r < row_size; r++)
+ {
+ for (c = 0; c < col_size; c++)
+ {
+ pos = NUM2INT(rows_array[r]) * (cols + pad) + NUM2INT(cols_array[c]);
- byte_array[BYTE_POS(pos)] |= BIT_POS(pos);
+ byte_array[BYTE_POS(pos)] |= BIT_POS(pos);
+ }
+ }
}
}
builder.c %{
- VALUE bit_test_c(int pos)
+ void unset_c(VALUE _cols_array, VALUE _rows_array)
{
VALUE _byte_array = rb_ivar_get(self, byte_array);
+ VALUE _cols = rb_ivar_get(self, cols);
unsigned char* byte_array = StringValuePtr(_byte_array);
+ int cols = NUM2INT(_cols);
- return ((byte_array[BYTE_POS(pos)] & BIT_POS(pos)) != 0);
+ VALUE *cols_array = RARRAY_PTR(_cols_array);
+ VALUE *rows_array = RARRAY_PTR(_rows_array);
+ int col_size = (int) RARRAY_LEN(_cols_array);
+ int row_size = (int) RARRAY_LEN(_rows_array);
+ int c = 0;
+ int r = 0;
+ int pad = PADDING(cols);
+ int pos = 0;
+ unsigned char mask = 0;
+
+ for (r = 0; r < row_size; r++)
+ {
+ for (c = 0; c < col_size; c++)
+ {
+ pos = NUM2INT(rows_array[r]) * (cols + pad) + NUM2INT(cols_array[c]);
+
+ mask = BIT_POS(pos);
+ mask = ~mask;
+
+ byte_array[BYTE_POS(pos)] &= mask;
+ }
+ }
}
}
builder.c %{
- void slice_c(char* new_array, VALUE _new_rows, VALUE _new_cols)
+ void slice_c(VALUE _new_array, VALUE _new_cols, VALUE _new_rows)
{
VALUE _old_array = rb_ivar_get(self, byte_array);
- VALUE _old_rows = rb_ivar_get(self, rows);
VALUE _old_cols = rb_ivar_get(self, cols);
+ VALUE _old_rows = rb_ivar_get(self, rows);
unsigned char* old_array = StringValuePtr(_old_array);
- int old_rows = NUM2INT(_old_rows);
int old_cols = NUM2INT(_old_cols);
+ int old_rows = NUM2INT(_old_rows);
- VALUE *rows_array = RARRAY_PTR(_new_rows);
+ unsigned char* new_array = StringValuePtr(_new_array);
VALUE *cols_array = RARRAY_PTR(_new_cols);
- int row_count = (int) RARRAY_LEN(_new_rows);
+ VALUE *rows_array = RARRAY_PTR(_new_rows);
int col_count = (int) RARRAY_LEN(_new_cols);
- int r = 0;
+ int row_count = (int) RARRAY_LEN(_new_rows);
int c = 0;
+ int r = 0;
int old_pos = 0;
int new_pos = 0;
+ int old_pad = PADDING(old_cols);
+ int new_pad = PADDING(col_count);
for (r = 0; r < row_count; r++)
{
for (c = 0; c < col_count; c++)
{
- old_pos = NUM2INT(rows_array[r]) * old_cols + NUM2INT(cols_array[c]);
- new_pos = r * col_count + c;
+ old_pos = NUM2INT(rows_array[r]) * (old_cols + old_pad) + NUM2INT(cols_array[c]);
+ new_pos = r * (col_count + new_pad) + c;
- new_array[BYTE_POS(new_pos)] |= BIT_POS(new_pos) & (old_array[BYTE_POS(old_pos)] & BIT_POS(old_pos));
+ if ((old_array[BYTE_POS(old_pos)] & BIT_POS(old_pos)) != 0)
+ new_array[BYTE_POS(new_pos)] |= BIT_POS(new_pos);
+ }
+ }
+ }
+ }
+
+ builder.c %{
+ void bitwise_and_row_c(VALUE _old_array, VALUE _new_array, VALUE _row_array, VALUE _cols, VALUE _rows)
+ {
+ unsigned char* old_array = StringValuePtr(_old_array);
+ unsigned char* new_array = StringValuePtr(_new_array);
+ unsigned char* row_array = StringValuePtr(_row_array);
+ int cols = NUM2INT(_cols);
+ int rows = NUM2INT(_rows);
+ int col_bytes = (cols / CHAR_BIT) + ((cols % CHAR_BIT) ? 1: 0);
+ int c = 0;
+ int r = 0;
+
+ for (r = 0; r < rows; r++)
+ {
+ for (c = 0; c < col_bytes; c++) {
+ new_array[r * col_bytes + c] = old_array[r * col_bytes + c] & row_array[c];
}
}
}
builder.c %{
VALUE sum_c()
{
- VALUE _size = rb_ivar_get(self, byte_array_size);
VALUE _byte_array = rb_ivar_get(self, byte_array);
- int size = NUM2INT(_size);
+ VALUE _rows = rb_ivar_get(self, rows);
+ VALUE _cols = rb_ivar_get(self, cols);
unsigned char* byte_array = StringValuePtr(_byte_array);
+ int rows = NUM2INT(_rows);
+ int cols = NUM2INT(_cols);
+ int size = BYTE_SIZE(rows, cols);
int i = 0;
int sum = 0;
-
+
for (i = 0; i < size; i++)
sum += COUNT_BITS(byte_array[i]);
builder.c %{
VALUE sum_rows_c()
{
+ VALUE _byte_array = rb_ivar_get(self, byte_array);
VALUE _rows = rb_ivar_get(self, rows);
VALUE _cols = rb_ivar_get(self, cols);
- VALUE _byte_array = rb_ivar_get(self, byte_array);
+ unsigned char* byte_array = StringValuePtr(_byte_array);
int rows = NUM2INT(_rows);
int cols = NUM2INT(_cols);
- unsigned char* byte_array = StringValuePtr(_byte_array);
- int row = 0;
- int col = 0;
+ int col_bytes = (cols / CHAR_BIT) + ((cols % CHAR_BIT) ? 1: 0);
+ int r = 0;
+ int c = 0;
int sums[rows];
VALUE rb_sums = rb_ary_new();
int pos = 0;
- for (row = 0; row < rows; row++) sums[row] = 0;
+ for (r = 0; r < rows; r++) sums[r] = 0;
- for (row = 0; row < rows; row++)
+ for (r = 0; r < rows; r++)
{
- for (col = 0; col < cols; col++)
- {
- pos = row * cols + col;
- sums[row] += ((byte_array[BYTE_POS(pos)] & BIT_POS(pos)) != 0);
+ for (c = 0; c < col_bytes; c++) {
+ sums[r] += COUNT_BITS(byte_array[r * col_bytes + c]);
}
}
- for (row = 0; row < rows; row++) rb_ary_push(rb_sums, INT2FIX(sums[row]));
+ for (r = 0; r < rows; r++) rb_ary_push(rb_sums, INT2FIX(sums[r]));
return rb_sums;
}
}
end
- alias :sum :sum_c
+ alias :sum :sum_c
alias :sum_rows :sum_rows_c
- alias :bit_set :bit_set_c
- alias :bit_set? :bit_test_c
- alias :fill! :fill_c
- alias :empty! :empty_c
- alias :zero! :empty_c
+ alias :set? :bit_test_c
+ alias :fill! :fill_c
+ alias :empty! :empty_c
end