1 # Copyright (C) 2012 Martin A. Hansen.
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software
15 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # http://www.gnu.org/copyleft/gpl.html
19 # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
21 # This software is part of the Biopieces framework (www.biopieces.org).
23 # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
27 # Error class for all exceptions to do with BitMap.
28 class BitMapError < StandardError; end
32 # Bitmap - 13 columns, 3 rows:
38 # byte 0 byte 1 byte 2 byte 3 byte 4 byte 5
39 # +--------+--------+--------+--------+--------+--------+
40 # |10000000|00000000|00000010|00000000|00000000|00000001|
41 # +--------+--------+--------+--------+--------+--------+
45 # 10000000 00000xxx 00000010 00000xxx 00000000 00001xxx
47 attr_accessor :cols, :rows, :byte_array
49 # Method to initialize a BitMap.
50 def initialize(cols, rows)
54 size = ((rows * (cols + @pads)) / CHAR_BIT)
55 @byte_array = "\0" * size
58 # Method that set bits to 1 at specified columns, and rows.
61 # set([0, 4, 5], true)
64 # TODO: set([1 .. -2], true)
66 cols = cols_expand(cols)
67 rows = rows_expand(rows)
72 # Method that set bits to 0 at specified columns, and rows.
75 # unset([0, 4, 5], true)
76 # unset([0 .. 4], true)
77 # TODO: unset(-1, true)
78 # TODO: unset([1 .. -2], true)
80 cols = cols_expand(cols)
81 rows = rows_expand(rows)
86 # Method to get a slice from a bitmap from specified rows and columns,
87 # and return this slice as a new BitMap object.
90 # slice([0, 4, 5], true)
91 # slice([0 .. 4], true)
92 # TODO: slice(-1, true)
93 # TODO: slice([1 .. -2], true)
95 cols = cols_expand(cols)
96 rows = rows_expand(rows)
97 bm = BitMap.new(cols.size, rows.size)
99 slice_c(bm.byte_array, cols, rows)
104 # Method that performs bit wise and operation between:
105 # 1: an entire bit map.
107 # 3: a bit map column.
108 # The result is saved in self.
110 bm = BitMap.new(@cols, @rows)
112 if @rows == bit_map.rows and @cols == bit_map.cols
113 bitwise_and_c(bit_map.byte_array)
114 elsif @rows == bit_map.rows
115 bitwise_and_col_c(bit_map.byte_array)
116 elsif @cols == bit_map.cols
117 bitwise_and_row_c(self.byte_array, bm.byte_array, bit_map.byte_array, @cols, @rows)
119 raise BitMapError, "can't & uneven bit maps: #{@rows} x #{@cols} & #{bit_map.rows} x #{bit_map.cols}"
125 # Method to convert a bit array to a string.
131 (0 ... @rows).each do |r|
133 (0 ... @cols).each do |c|
134 pos = r * (@cols + @pads) + c
144 row = row[0 ... max_cols] + " ... (#{@cols})"
147 if rows.size > max_rows
162 (((@cols % CHAR_BIT) != 0) ? (CHAR_BIT - (@cols % CHAR_BIT)) : 0)
165 def rows_expand(rows)
169 rows = (0 ... @rows).to_a
170 elsif rows.is_a? Array and rows.first.is_a? Range
171 rows = rows.first.to_a
172 rows = [0] if rows.empty? # FIXME
175 raise BitMapError, "row outside bitmap: #{rows.first}" if rows.first < 0
176 raise BitMapError, "row outside bitmap: #{rows.last}" if rows.last >= @rows
181 def cols_expand(cols)
185 cols = (0 ... @cols).to_a
186 elsif cols.is_a? Array and cols.first.is_a? Range
187 cols = cols.first.to_a
190 raise BitMapError, "column outside bitmap: #{cols.first}" if cols.first < 0
191 raise BitMapError, "column outside bitmap: #{cols.last}" if cols.last >= @cols
197 builder.add_static "rows", 'rb_intern("@rows")', "ID"
198 builder.add_static "cols", 'rb_intern("@cols")', "ID"
199 builder.add_static "byte_array", 'rb_intern("@byte_array")', "ID"
202 unsigned char bit_count[256] = {
203 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
204 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
205 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
206 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
207 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
208 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
209 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
210 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
211 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
212 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
213 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
214 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
215 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
216 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
217 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
218 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
223 #define COUNT_BITS(c) (bit_count[c])
227 #define PADDING(cols) (((cols % CHAR_BIT) != 0) ? (CHAR_BIT - (cols % CHAR_BIT)) : 0)
231 #define BYTE_SIZE(rows, cols) (rows * (cols + PADDING(cols)) / CHAR_BIT)
235 #define BYTE_POS(pos) (pos / CHAR_BIT)
239 #define BIT_POS(pos) (1 << (CHAR_BIT - 1 - (pos % CHAR_BIT)))
245 VALUE _byte_array = rb_ivar_get(self, byte_array);
246 VALUE _rows = rb_ivar_get(self, rows);
247 VALUE _cols = rb_ivar_get(self, cols);
248 unsigned char* byte_array = StringValuePtr(_byte_array);
249 int rows = NUM2INT(_rows);
250 int cols = NUM2INT(_cols);
251 int size = BYTE_SIZE(rows, cols);
254 for (i = 0; i < size; i++) byte_array[i] = ~0;
263 VALUE _byte_array = rb_ivar_get(self, byte_array);
264 VALUE _rows = rb_ivar_get(self, rows);
265 VALUE _cols = rb_ivar_get(self, cols);
266 unsigned char* byte_array = StringValuePtr(_byte_array);
267 int rows = NUM2INT(_rows);
268 int cols = NUM2INT(_cols);
269 int size = BYTE_SIZE(rows, cols);
272 for (i = 0; i < size; i++) byte_array[i] = 0;
279 VALUE bit_test_c(VALUE _pos)
281 int pos = NUM2INT(_pos);
282 VALUE _byte_array = rb_ivar_get(self, byte_array);
283 unsigned char* byte_array = StringValuePtr(_byte_array);
285 return ((byte_array[BYTE_POS(pos)] & BIT_POS(pos)) != 0);
290 void set_c(VALUE _cols_array, VALUE _rows_array)
292 VALUE _byte_array = rb_ivar_get(self, byte_array);
293 VALUE _cols = rb_ivar_get(self, cols);
294 unsigned char* byte_array = StringValuePtr(_byte_array);
295 int cols = NUM2INT(_cols);
297 VALUE *cols_array = RARRAY_PTR(_cols_array);
298 VALUE *rows_array = RARRAY_PTR(_rows_array);
299 int col_size = (int) RARRAY_LEN(_cols_array);
300 int row_size = (int) RARRAY_LEN(_rows_array);
303 int pad = PADDING(cols);
306 for (r = 0; r < row_size; r++)
308 for (c = 0; c < col_size; c++)
310 pos = NUM2INT(rows_array[r]) * (cols + pad) + NUM2INT(cols_array[c]);
312 byte_array[BYTE_POS(pos)] |= BIT_POS(pos);
319 void unset_c(VALUE _cols_array, VALUE _rows_array)
321 VALUE _byte_array = rb_ivar_get(self, byte_array);
322 VALUE _cols = rb_ivar_get(self, cols);
323 unsigned char* byte_array = StringValuePtr(_byte_array);
324 int cols = NUM2INT(_cols);
326 VALUE *cols_array = RARRAY_PTR(_cols_array);
327 VALUE *rows_array = RARRAY_PTR(_rows_array);
328 int col_size = (int) RARRAY_LEN(_cols_array);
329 int row_size = (int) RARRAY_LEN(_rows_array);
332 int pad = PADDING(cols);
334 unsigned char mask = 0;
336 for (r = 0; r < row_size; r++)
338 for (c = 0; c < col_size; c++)
340 pos = NUM2INT(rows_array[r]) * (cols + pad) + NUM2INT(cols_array[c]);
345 byte_array[BYTE_POS(pos)] &= mask;
352 void slice_c(VALUE _new_array, VALUE _new_cols, VALUE _new_rows)
354 VALUE _old_array = rb_ivar_get(self, byte_array);
355 VALUE _old_cols = rb_ivar_get(self, cols);
356 VALUE _old_rows = rb_ivar_get(self, rows);
357 unsigned char* old_array = StringValuePtr(_old_array);
358 int old_cols = NUM2INT(_old_cols);
359 int old_rows = NUM2INT(_old_rows);
361 unsigned char* new_array = StringValuePtr(_new_array);
362 VALUE *cols_array = RARRAY_PTR(_new_cols);
363 VALUE *rows_array = RARRAY_PTR(_new_rows);
364 int col_count = (int) RARRAY_LEN(_new_cols);
365 int row_count = (int) RARRAY_LEN(_new_rows);
370 int old_pad = PADDING(old_cols);
371 int new_pad = PADDING(col_count);
373 for (r = 0; r < row_count; r++)
375 for (c = 0; c < col_count; c++)
377 old_pos = NUM2INT(rows_array[r]) * (old_cols + old_pad) + NUM2INT(cols_array[c]);
378 new_pos = r * (col_count + new_pad) + c;
380 if ((old_array[BYTE_POS(old_pos)] & BIT_POS(old_pos)) != 0)
381 new_array[BYTE_POS(new_pos)] |= BIT_POS(new_pos);
388 void bitwise_and_row_c(VALUE _old_array, VALUE _new_array, VALUE _row_array, VALUE _cols, VALUE _rows)
390 unsigned char* old_array = StringValuePtr(_old_array);
391 unsigned char* new_array = StringValuePtr(_new_array);
392 unsigned char* row_array = StringValuePtr(_row_array);
393 int cols = NUM2INT(_cols);
394 int rows = NUM2INT(_rows);
395 int col_bytes = (cols / CHAR_BIT) + ((cols % CHAR_BIT) ? 1: 0);
399 for (r = 0; r < rows; r++)
401 for (c = 0; c < col_bytes; c++) {
402 new_array[r * col_bytes + c] = old_array[r * col_bytes + c] & row_array[c];
411 VALUE _byte_array = rb_ivar_get(self, byte_array);
412 VALUE _rows = rb_ivar_get(self, rows);
413 VALUE _cols = rb_ivar_get(self, cols);
414 unsigned char* byte_array = StringValuePtr(_byte_array);
415 int rows = NUM2INT(_rows);
416 int cols = NUM2INT(_cols);
417 int size = BYTE_SIZE(rows, cols);
421 for (i = 0; i < size; i++)
422 sum += COUNT_BITS(byte_array[i]);
431 VALUE _byte_array = rb_ivar_get(self, byte_array);
432 VALUE _rows = rb_ivar_get(self, rows);
433 VALUE _cols = rb_ivar_get(self, cols);
434 unsigned char* byte_array = StringValuePtr(_byte_array);
435 int rows = NUM2INT(_rows);
436 int cols = NUM2INT(_cols);
437 int col_bytes = (cols / CHAR_BIT) + ((cols % CHAR_BIT) ? 1: 0);
441 VALUE rb_sums = rb_ary_new();
445 for (r = 0; r < rows; r++) sums[r] = 0;
447 for (r = 0; r < rows; r++)
449 for (c = 0; c < col_bytes; c++) {
450 sums[r] += COUNT_BITS(byte_array[r * col_bytes + c]);
454 for (r = 0; r < rows; r++) rb_ary_push(rb_sums, INT2FIX(sums[r]));
462 alias :sum_rows :sum_rows_c
463 alias :set? :bit_test_c
465 alias :empty! :empty_c