--- /dev/null
+# Copyright (C) 2012 Martin A. Hansen.
+
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+# http://www.gnu.org/copyleft/gpl.html
+
+# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+
+# This software is part of the Biopieces framework (www.biopieces.org).
+
+# >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+
+require 'inline'
+
+# Error class for all exceptions to do with Base36.
+class BitMapError < StandardError; end
+
+BitsInChar = 8
+
+class BitMap
+ attr_accessor :byte_array
+
+ def initialize(rows, cols, byte_array = nil)
+ @rows = rows
+ @cols = cols
+ @size = rows * cols
+
+ if byte_array.nil?
+ @byte_array_size = (rows * cols.to_f / BitsInChar).ceil
+ @byte_array = "\0" * @byte_array_size
+ else
+ @byte_array_size = (rows * cols.to_f / BitsInChar).ceil
+ @byte_array = byte_array
+ end
+ 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
+
+ new_byte_array_size = (rows.size * cols.size.to_f / BitsInChar).ceil
+ new_byte_array = "\0" * new_byte_array_size
+
+ slice_c(new_byte_array, rows, cols)
+
+ BitMap.new(rows.size, cols.size, new_byte_array)
+ end
+
+ def flip!
+ @rows, @cols = @cols, @rows
+ 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.gsub(/.{#{@cols}}/, "\\0#{$/}")
+ 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.prefix %s{
+ unsigned char bit_count[256] = {
+ 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+ 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
+ };
+ }
+
+ builder.prefix %{
+ #define COUNT_BITS(c) (bit_count[c])
+ }
+
+ builder.prefix %{
+ #define BYTE_POS(pos) (pos / CHAR_BIT)
+ }
+
+ builder.prefix %{
+ #define BIT_POS(pos) (1 << (CHAR_BIT - 1 - (pos % CHAR_BIT)))
+ }
+
+ builder.c %{
+ void fill_c()
+ {
+ VALUE _size = rb_ivar_get(self, byte_array_size);
+ VALUE _byte_array = rb_ivar_get(self, byte_array);
+ int size = NUM2INT(_size);
+ unsigned char* byte_array = StringValuePtr(_byte_array);
+ int i = 0;
+
+ for (i = 0; i < size; i++) byte_array[i] = ~0;
+ }
+ }
+
+ builder.c %{
+ void empty_c()
+ {
+ VALUE _size = rb_ivar_get(self, byte_array_size);
+ VALUE _byte_array = rb_ivar_get(self, byte_array);
+ int size = NUM2INT(_size);
+ unsigned char* byte_array = StringValuePtr(_byte_array);
+ int i = 0;
+
+ for (i = 0; i < size; i++) byte_array[i] = 0;
+ }
+ }
+
+ builder.c %{
+ void bit_set_c(int row, int col)
+ {
+ VALUE _rows = rb_ivar_get(self, rows);
+ VALUE _cols = rb_ivar_get(self, cols);
+ 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);
+
+ if (col >= cols)
+ rb_raise(rb_eval_string("BitMapError"), "col out of range: %d >= %d\\n", col, cols);
+
+ pos = row * cols + col;
+
+ byte_array[BYTE_POS(pos)] |= BIT_POS(pos);
+ }
+ }
+
+ builder.c %{
+ VALUE bit_test_c(int pos)
+ {
+ VALUE _byte_array = rb_ivar_get(self, byte_array);
+ unsigned char* byte_array = StringValuePtr(_byte_array);
+
+ return ((byte_array[BYTE_POS(pos)] & BIT_POS(pos)) != 0);
+ }
+ }
+
+ builder.c %{
+ void slice_c(char* new_array, VALUE _new_rows, VALUE _new_cols)
+ {
+ 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);
+ unsigned char* old_array = StringValuePtr(_old_array);
+ int old_rows = NUM2INT(_old_rows);
+ int old_cols = NUM2INT(_old_cols);
+
+ VALUE *rows_array = RARRAY_PTR(_new_rows);
+ VALUE *cols_array = RARRAY_PTR(_new_cols);
+ int row_count = (int) RARRAY_LEN(_new_rows);
+ int col_count = (int) RARRAY_LEN(_new_cols);
+ int r = 0;
+ int c = 0;
+ int old_pos = 0;
+ int new_pos = 0;
+
+ 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;
+
+ new_array[BYTE_POS(new_pos)] |= BIT_POS(new_pos) & (old_array[BYTE_POS(old_pos)] & BIT_POS(old_pos));
+ }
+ }
+ }
+ }
+
+ 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);
+ unsigned char* byte_array = StringValuePtr(_byte_array);
+ int i = 0;
+ int sum = 0;
+
+ for (i = 0; i < size; i++)
+ sum += COUNT_BITS(byte_array[i]);
+
+ return INT2NUM(sum);
+ }
+ }
+
+ builder.c %{
+ VALUE sum_rows_c()
+ {
+ VALUE _rows = rb_ivar_get(self, rows);
+ VALUE _cols = rb_ivar_get(self, cols);
+ 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 row = 0;
+ int col = 0;
+ int sums[rows];
+ VALUE rb_sums = rb_ary_new();
+
+ int pos = 0;
+
+ for (row = 0; row < rows; row++) sums[row] = 0;
+
+ for (row = 0; row < rows; row++)
+ {
+ for (col = 0; col < cols; col++)
+ {
+ pos = row * cols + col;
+ sums[row] += ((byte_array[BYTE_POS(pos)] & BIT_POS(pos)) != 0);
+ }
+ }
+
+ for (row = 0; row < rows; row++) rb_ary_push(rb_sums, INT2FIX(sums[row]));
+
+ return rb_sums;
+ }
+ }
+ end
+
+ 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
+end