From: martinahansen Date: Tue, 3 Apr 2012 13:24:51 +0000 (+0000) Subject: upgraded bitmap.rb X-Git-Url: https://git.donarmstrong.com/?a=commitdiff_plain;h=7823a7b3da6c38523d1340427efd058c370b0acf;p=biopieces.git upgraded bitmap.rb git-svn-id: http://biopieces.googlecode.com/svn/trunk@1777 74ccb610-7750-0410-82ae-013aeee3265d --- diff --git a/code_ruby/lib/maasha/bitmap.rb b/code_ruby/lib/maasha/bitmap.rb index d1a1a12..be53acf 100644 --- a/code_ruby/lib/maasha/bitmap.rb +++ b/code_ruby/lib/maasha/bitmap.rb @@ -24,65 +24,179 @@ 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] = { @@ -109,6 +223,14 @@ class BitMap #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) } @@ -118,91 +240,166 @@ class BitMap } 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]; } } } @@ -211,13 +408,16 @@ class BitMap 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]); @@ -228,42 +428,39 @@ class BitMap 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