]> git.donarmstrong.com Git - biopieces.git/commitdiff
upgraded bitmap.rb
authormartinahansen <martinahansen@74ccb610-7750-0410-82ae-013aeee3265d>
Tue, 3 Apr 2012 13:24:51 +0000 (13:24 +0000)
committermartinahansen <martinahansen@74ccb610-7750-0410-82ae-013aeee3265d>
Tue, 3 Apr 2012 13:24:51 +0000 (13:24 +0000)
git-svn-id: http://biopieces.googlecode.com/svn/trunk@1777 74ccb610-7750-0410-82ae-013aeee3265d

code_ruby/lib/maasha/bitmap.rb

index d1a1a125501d8db335467c1c3909b61b9e394629..be53acfcebd9c147bd13ba4524a149ba5adfc353 100644 (file)
 
 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