#include "ruby.h"
#include "string.h"

static VALUE cBitSet;
static ID id_new, id_first, id_end;

struct BitSet {
  int len;
  unsigned char *ptr;
};

int ZERO_TABLE[] = {
  8, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,
  4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0
};

int ONE_TABLE[] = {
  0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
  0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,
  0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
  0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6,
  0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
  0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,
  0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
  0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 7,
  0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
  0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,
  0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
  0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6,
  0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
  0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,
  0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,
  0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 8
};

int MAX_TABLE[] = {
  0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
  5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
  6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
  6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
  7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
  8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
  8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
  8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
  8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
  8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
  8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
  8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
  8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8
};

int COUNT_TABLE[] = {
  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,
};


void bs_resize(struct BitSet *bits, int len) {
  int arylen;
  int arybytes, arymod;
  int bytes, mod;
  arylen   = bits->len;
  arybytes = (arylen + 7) / 8;
  arymod   =  arylen      % 8;
  bytes    = (len + 7)    / 8;
  mod      =  len         % 8;

  if (len==arylen) {
    return;
  } else if (len<arylen) {
    bits->ptr = REALLOC_N(bits->ptr, unsigned char, bytes);
    bits->len = len;
    if (mod>0) {
      bits->ptr[bytes-1] &= (1<<mod)-1;
    }
  } else if (len>arylen) {
    bits->ptr = REALLOC_N(bits->ptr, unsigned char, bytes);
    memset(bits->ptr+arybytes, 0, bytes-arybytes);
    bits->len = len;
  }
}



int bs_get(struct BitSet *bits, int index) {
  int byte_offset;
  int bit_offset;
  unsigned char c;
  int bit;
  
  if (index >= bits->len) {
    bit = 0;
  } else {
    byte_offset = index / 8;
    bit_offset  = index % 8;
    
    c = bits->ptr[byte_offset];
    
    if (c & (1 << bit_offset)) {
      bit = 1;
    } else {
      bit = 0;
    }
  }
  
  return bit;
}


void bs_set(struct BitSet *bits, int index, int bit) {
  int byte_offset;
  int bit_offset;
  unsigned char c;
  
  if (index >= bits->len) {
    bs_resize(bits, index+1);
  }
  
  byte_offset = index / 8;
  bit_offset  = index % 8;
  
  c = bits->ptr[byte_offset];
  
  if (bit) {
    c |= (1 << bit_offset);
  } else {
    c &= ~(1 << bit_offset);
  }
  
  bits->ptr[byte_offset] = c;
  
}

void bs_sets(struct BitSet *bits, int first, int end, int bit) {
  int first_offset;
  int end_offset;
  int first_mod;
  int end_mod;
  int bytes;
  int i;
  unsigned char c;
  
  if (bits->len < end) {
    bs_resize(bits, end);
  }
  
  first_offset = first / 8;
  first_mod    = first % 8;
  end_offset   = end   / 8;
  end_mod      = end   % 8;
  bytes = end_offset - first_offset + 1;
  
  if (first_mod==0 && end_mod==7) {
    memset(bits->ptr+first_offset, bit ? 0xff : 0x00, bytes);
    return;
  }
  
  if (end-first < 16) {
    for(i=first; i<=end; i++) {
      bs_set(bits, i, bit);
    }
    return;
  }
  
  if (first_mod != 0) {
    c = bits->ptr[first_offset];
    if (bit) {
      c |= ~((1 << first_mod) - 1);
    } else {
      c &= ((1 << first_mod) - 1);
    }
    bits->ptr[first_offset] = c;
    first_offset++;
    bytes--;
  }
  
  if (end_mod != 7) {
    c = bits->ptr[end_offset];
    if (bit) {
      c |= ((2 << end_mod) - 1);
    } else {
      c &= ~((2 << end_mod) - 1);
    }
    bits->ptr[end_offset] = c;
    bytes--;
  }
  
  if (bytes>0) {
    memset(bits->ptr+first_offset, bit ? 0xff : 0x00, bytes);
  }
  
  return;
}


void bs_fill(VALUE obj, struct BitSet *bits, int fill) {
  int i;
  VALUE vfirst, vend;
  int first, end;
  switch(TYPE(obj)) {
  case T_FIXNUM:
    bs_set(bits, FIX2INT(obj), fill);
    break;
  case T_ARRAY:
    for(i=0; i< (RARRAY(obj)->len); i++) {
      bs_fill( (RARRAY(obj)->ptr[i]), bits, fill);
    }
    break;
  default:
    if (rb_class_of(obj)==rb_cRange) {
      vfirst = rb_funcall(obj, id_first, 0);
      vend   = rb_funcall(obj, id_end, 0);
      Check_Type(vfirst, T_FIXNUM);
      Check_Type(vend, T_FIXNUM);
      first = FIX2INT(vfirst);
      end   = FIX2INT(vend);
      if (rb_funcall(obj, rb_intern("exclude_end?"), 0) == Qtrue) {
        end--;
      }
      if (first>end || first<0 || end<0) {
        rb_raise(rb_eRangeError, "not valid range");
      }
      bs_sets(bits, first, end, fill);
    } else {
      rb_raise(rb_eTypeError, "not valid index");
    }
  }
}

void bs_or(struct BitSet *bits, struct BitSet *x) {
  unsigned char *ptr, *xptr;
  int bytes;
  int i;
  
  if (bits->len < x->len) {
    bs_resize(bits, x->len);
  }
  
  bytes = (x->len+7) / 8;
  ptr   = bits->ptr;
  xptr  = x->ptr;
  for(i=0; i<bytes; i++) {
    *ptr++ |= *xptr++;
  }
  
}

void bs_not(struct BitSet *bits) {
  unsigned char *ptr;
  unsigned char c;
  int bytes;
  int mod;
  int i;
  
  bytes = (bits->len+7) / 8;
  mod   = bits->len % 8;
  ptr   = bits->ptr;
  for(i=0; i<bytes; i++) {
    c = *ptr;
    *ptr++ = ~c;
  }
  if (mod>0) {
    c = *--ptr;
    *ptr = c & ((1<<mod)-1);
  }
}

/*
  aaaA  & bbbbB = xxxX
  aaaaA & bbbB  = xxxX
*/
void bs_and(struct BitSet *sbits, struct BitSet *xbits) {
  unsigned char *sptr, *xptr;
  unsigned char sch, xch;
  int srest, xrest;
  int sbytes, xbytes;
  
  sbytes = (sbits->len + 7) / 8;
  srest  = sbits->len % 8;
  xbytes = (xbits->len + 7) / 8;
  xrest  = xbits->len % 8;
  
  sptr  = sbits->ptr;
  xptr  = xbits->ptr;
  while(sbytes && xbytes) {
    *sptr++ &= *xptr++;
    sbytes--;
    xbytes--;
  }
  
  while(sbytes) {
    if (sbytes) {
      sch = *sptr;
      if (--sbytes == 0) {
        sch &= (1<<srest)-1;
      }
    } else {
      sch = 0;
    }
    if (xbytes) {
      xch = *xptr++;
      if (--xbytes == 0) {
        xch &= (1<<xrest)-1;
      }
    } else {
      xch = 0;
    }
    *sptr++ = sch & xch;
  }
  
}

void bs_xor(struct BitSet *bits, struct BitSet *x) {
  unsigned char *ptr, *xptr;
  int bytes;
  int i;
  
  if (bits->len < x->len) {
    bs_resize(bits, x->len);
  }
  
  bytes = (x->len+7) / 8;
  ptr   = bits->ptr;
  xptr  = x->ptr;
  for(i=0; i<bytes; i++) {
    *ptr++ ^= *xptr++;
  }
  
}

int bs_max(struct BitSet *bits) {
  int bytes;
  int index;
  int pos;
  unsigned char *ptr, c;

  bytes = (bits->len+7) / 8;
  index = (bytes-1)     * 8;
  ptr  =  bits->ptr + bytes - 1;
  
  while(index>=0) {
    c = *ptr--;
    pos = MAX_TABLE[c];
    if (pos) {
      index = index + pos - 1;
      return index;
    }
    index -= 8;
  }
  
  return -1;
}

int bs_min(struct BitSet *bits) {
  int index;
  int len;
  int pos;
  unsigned char *ptr, c;
  
  index = 0;
  ptr   = bits->ptr;
  len   = bits->len;
  while(index<len) {
    c = *ptr++;
    pos = ZERO_TABLE[c];
    if (pos<8) {
      index = index + pos;
      return index;
    }
    index += 8;
  }
  
  return -1;
}







int to_bit(VALUE obj) {
  int bit;
  
  switch(TYPE(obj)) {
  case T_NIL:
  case T_FALSE:
    bit = 0;
    break;
  case T_FIXNUM:
    if (FIX2INT(obj)==0) {
      bit = 0;
    } else {
      bit = 1;
    }
    break;
  default:
    bit = 1;
  }
  
  return bit;
}

void bits_free(struct BitSet *bits) {
  ruby_xfree(bits->ptr);
}

static VALUE bits_s_new(int argc, VALUE *argv, VALUE self) {
  struct BitSet *bits;
  VALUE arg;
  VALUE obj;
  int len;
  int bytes;
  
  obj = Data_Make_Struct(self, struct BitSet, NULL, bits_free, bits);
  
  if (argc>0) {
    arg = argv[0];
  } else {
    arg = INT2FIX(1);
  }
  
  switch(TYPE(arg)) {
  case T_FIXNUM:
    len = FIX2INT(arg);
    if (len<1) {
      rb_raise(rb_eArgError, "array size");
    }
    bytes = (len+7)/8;
    bits->len = len;
    bits->ptr = ALLOC_N(unsigned char, bytes);
    memset(bits->ptr, 0, bytes);
    break;
  case T_STRING:
    bytes = RSTRING(arg)->len;
    bits->len = bytes*8;
    bits->ptr = ALLOC_N(unsigned char, bytes);
    memcpy(bits->ptr, RSTRING(arg)->ptr, bytes);
    break;
  default:
    rb_raise(rb_eArgError, "not valid value");
  }
  
  return obj;
}

static VALUE bits_s_from_bin(VALUE self, VALUE arg) {
  struct BitSet *bits;
  VALUE retobj;
  unsigned char ch, *ptr, *bptr;
  int len;
  int bytes;
  int bitcnt;
  
  Check_Type(arg, T_STRING);
  
  len = RSTRING(arg)->len;
  if (len<1) {
    rb_raise(rb_eArgError, "array size");
  }
  bytes = (len+7)/8;
  
  retobj = Data_Make_Struct(self, struct BitSet, NULL, bits_free, bits);
  bits->len = len;
  bits->ptr = ALLOC_N(unsigned char, bytes);
  memset(bits->ptr, 0, bytes);
  
  ch = 0;
  bitcnt = 0;
  ptr = (unsigned char *)RSTRING(arg)->ptr;
  bptr = bits->ptr;
  while(len--) {
    switch(*ptr++) {
    case '0':
    case '-':
    case 'f':
    case 'F':
    case 'N':
      break;
    default:
      ch |= 1 << bitcnt;
    }
    bitcnt++;
    if (bitcnt==8) {
      *bptr++ = ch;
      ch = 0;
      bitcnt = 0;
    }
  }
  if (bitcnt) {
    *bptr++ = ch;
  }
  
  return retobj;
}


static VALUE bits_length(VALUE self) {
  struct BitSet *bits;
  
  Data_Get_Struct(self, struct BitSet, bits);
  
  return INT2FIX(bits->len);
}

static VALUE bits_bytes(VALUE self) {
  struct BitSet *bits;
  
  Data_Get_Struct(self, struct BitSet, bits);
  
  return INT2FIX((bits->len+7)/8);
}

static VALUE bits_resize(VALUE self, VALUE obj) {
  struct BitSet *bits;
  int len;
  
  Check_Type(obj, T_FIXNUM);
  len = FIX2INT(obj);
  if (len<1) {
    rb_raise(rb_eArgError, "array size");
  }
  
  Data_Get_Struct(self, struct BitSet, bits);
  bs_resize(bits, len);
  
  return self;
}

static VALUE bits_to_s(VALUE self) {
  struct BitSet *bits;
  unsigned char *ptr;
  int i;
  VALUE str;
  
  Data_Get_Struct(self, struct BitSet, bits);
  str = rb_str_new(0, bits->len);
  
  ptr = (unsigned char *)RSTRING(str)->ptr;
  for(i=0; i<bits->len; i++) {
    *ptr++ = bs_get(bits, i) ? '1' : '0';
  }
  
  return str;
}

static VALUE bits_dup(VALUE self) {
  struct BitSet *orig;
  struct BitSet *dups;
  VALUE obj;
  int bytes;
  
  Data_Get_Struct(self, struct BitSet, orig);
  
  obj = Data_Make_Struct(CLASS_OF(self), struct BitSet, NULL, bits_free, dups);
  dups->len = orig->len;
  bytes = (dups->len+7)/8;
  dups->ptr = ALLOC_N(unsigned char, bytes);
  memcpy(dups->ptr, orig->ptr, bytes);
  
  return obj;
}

static VALUE bits_clear(VALUE self) {
  struct BitSet *bits;
  
  Data_Get_Struct(self, struct BitSet, bits);
  memset(bits->ptr, 0, (bits->len+7)/8);
  
  return self;
}

static VALUE bits_get(VALUE self, VALUE vidx) {
  struct BitSet *bits;
  int index;
  int bit;
  
  Data_Get_Struct(self, struct BitSet, bits);
  
  Check_Type(vidx, T_FIXNUM);
  
  index = FIX2INT(vidx);
  if (index<0) {
    rb_raise(rb_eRangeError, "index range");
  }
  
  bit = bs_get(bits, index);
  
  return INT2FIX(bit);
}


static VALUE bits_set(VALUE self, VALUE vidx, VALUE vobj) {
  struct BitSet *bits;
  int index;
  int bit;
  
  Data_Get_Struct(self, struct BitSet, bits);
  
  Check_Type(vidx, T_FIXNUM);
  
  index = FIX2INT(vidx);
  if (index<0) {
    rb_raise(rb_eRangeError, "index range");
  }
  
  bit = to_bit(vobj);
  
  bs_set(bits, index, bit);
  
  return self;
}

static VALUE bits_on(int argc, VALUE *argv, VALUE self) {
  struct BitSet *bits;
  int i;
  Data_Get_Struct(self, struct BitSet, bits);

  for(i=0; i<argc; i++) {
    bs_fill(argv[i], bits, 1);
  }
  
  return self;
}

static VALUE bits_off(int argc, VALUE *argv, VALUE self) {
  struct BitSet *bits;
  int i;
  
  Data_Get_Struct(self, struct BitSet, bits);
  
  for(i=0; i<argc; i++) {
    bs_fill(argv[i], bits, 0);
  }
  
  return self;
}

static VALUE bits_or(VALUE self, VALUE other) {
  VALUE obj;
  struct BitSet *bits, *rbits, *xbits;
  int bytes;
  
  Data_Get_Struct(self,  struct BitSet, bits);
  
  obj = Data_Make_Struct(CLASS_OF(self), struct BitSet, NULL, bits_free, rbits);
  rbits->len = bits->len;
  bytes      = (rbits->len+7)/8;
  rbits->ptr = ALLOC_N(unsigned char, bytes);
  memcpy(rbits->ptr, bits->ptr, bytes);
  
  Data_Get_Struct(other, struct BitSet, xbits);
  bs_or(rbits, xbits);
  
  return obj;
}

static VALUE bits_not(VALUE self) {
  VALUE obj;
  struct BitSet *bits, *rbits;
  int bytes;
  
  Data_Get_Struct(self,  struct BitSet, bits);
  
  obj = Data_Make_Struct(CLASS_OF(self), struct BitSet, NULL, bits_free, rbits);
  rbits->len = bits->len;
  bytes      = (rbits->len+7)/8;
  rbits->ptr = ALLOC_N(unsigned char, bytes);
  memcpy(rbits->ptr, bits->ptr, bytes);
  
  bs_not(rbits);
  
  return obj;
}

static VALUE bits_and(VALUE self, VALUE other) {
  VALUE obj;
  struct BitSet *bits, *rbits, *xbits;
  int bytes;
  
  Data_Get_Struct(self,  struct BitSet, bits);
  
  obj = Data_Make_Struct(CLASS_OF(self), struct BitSet, NULL, bits_free, rbits);
  rbits->len = bits->len;
  bytes      = (rbits->len+7)/8;
  rbits->ptr = ALLOC_N(unsigned char, bytes);
  memcpy(rbits->ptr, bits->ptr, bytes);
  
  Data_Get_Struct(other, struct BitSet, xbits);
  bs_and(rbits, xbits);
  
  return obj;
}

static VALUE bits_xor(VALUE self, VALUE other) {
  VALUE obj;
  struct BitSet *bits, *rbits, *xbits;
  int bytes;
  
  Data_Get_Struct(self,  struct BitSet, bits);
  
  obj = Data_Make_Struct(CLASS_OF(self), struct BitSet, NULL, bits_free, rbits);
  rbits->len = bits->len;
  bytes      = (rbits->len+7)/8;
  rbits->ptr = ALLOC_N(unsigned char, bytes);
  memcpy(rbits->ptr, bits->ptr, bytes);
  
  Data_Get_Struct(other, struct BitSet, xbits);
  bs_xor(rbits, xbits);
  
  return obj;
}

static VALUE bits_minus(VALUE self, VALUE other) {
  VALUE obj;
  struct BitSet y;
  struct BitSet *bits, *rbits, *xbits, *ybits;
  int bytes;
  
  Data_Get_Struct(self,  struct BitSet, bits);
  Data_Get_Struct(other, struct BitSet, xbits);
  
  obj = Data_Make_Struct(CLASS_OF(self), struct BitSet, NULL, bits_free, rbits);
  rbits->len = bits->len;
  bytes      = (rbits->len+7)/8;
  rbits->ptr = ALLOC_N(unsigned char, bytes);
  memcpy(rbits->ptr, bits->ptr, bytes);

  ybits = &y;
  ybits->len = xbits->len;
  bytes      = (xbits->len+7)/8;
  ybits->ptr = ALLOC_N(unsigned char, bytes);
  memcpy(ybits->ptr, xbits->ptr, bytes);
  if (ybits->len < rbits->len) {
    bs_resize(ybits, rbits->len);
  }
  
  bs_not(ybits);
  bs_and(rbits, ybits);
  
  return obj;
}

static VALUE bits_cmp(VALUE self, VALUE other) {
  struct BitSet *sbits, *obits;
  int smax,omax,bytes;
  unsigned char *sptr,*optr, sc, oc;
  
  Data_Get_Struct(self,  struct BitSet, sbits);
  Data_Get_Struct(other, struct BitSet, obits);

  smax = bs_max(sbits);
  omax = bs_max(obits);
  if (smax < omax) {
    return INT2FIX(-1);
  } else if (smax > omax) {
    return INT2FIX(1);
  }
  
  if (smax < 0) {
    return INT2FIX(0);
  }
  
  bytes = (smax+7)/8;
  sptr = sbits->ptr + bytes - 1;
  optr = obits->ptr + bytes - 1;
  while(bytes--) {
    sc = *sptr--;
    oc = *optr--;
    if (sc<oc) {
      return INT2FIX(-1);
    } else if (sc > oc) {
      return INT2FIX(1);
    }
  }
  
  return INT2FIX(0);
}


static VALUE bits_zero(VALUE self) {
  struct BitSet *bits;
  unsigned char *ptr;
  int bytes;
  int i;
  
  Data_Get_Struct(self,  struct BitSet, bits);
  bytes      = (bits->len+7)/8;

  
  ptr = bits->ptr;
  for(i=0 ; i<bytes; i++) {
    if (*ptr++ != 0) {
      return Qfalse;
    }
  }
  
  return Qtrue;
}

static VALUE bits_nonzero(VALUE self) {
  struct BitSet *bits;
  unsigned char *ptr;
  int bytes;
  int i;
  
  Data_Get_Struct(self,  struct BitSet, bits);
  bytes      = (bits->len+7)/8;
  
  ptr = bits->ptr;
  for(i=0 ; i<bytes; i++) {
    if (*ptr++ != 0) {
      return Qtrue;
    }
  }
  
  return Qfalse;
}

static VALUE bits_max(VALUE self) {
  struct BitSet *bits;
  int index;
  
  Data_Get_Struct(self,  struct BitSet, bits);
  
  index = bs_max(bits);

  if (index>=0) {
    return INT2FIX(index);
  } else {
    return Qnil;
  }
}

static VALUE bits_min(VALUE self) {
  struct BitSet *bits;
  int index;
  
  Data_Get_Struct(self,  struct BitSet, bits);
  
  index = bs_min(bits);
  
  if (index>=0) {
    return INT2FIX(index);
  } else {
    return Qnil;
  }
}

static VALUE bits_norm(VALUE self) {
  VALUE obj;
  struct BitSet *bits;
  int index;
  
  obj = bits_dup(self);
  Data_Get_Struct(obj,  struct BitSet, bits);
  
  index = bs_max(bits);
  if (index<0) {
    index = 1;
  } else {
    index = index + 1;
  }
  bs_resize(bits, index);
  
  return obj;
}

static VALUE bits_normx(VALUE self) {
  struct BitSet *bits;
  int index;
  
  Data_Get_Struct(self,  struct BitSet, bits);
  
  index = bs_max(bits);
  
  index = bs_max(bits);
  if (index<0) {
    index = 1;
  } else {
    index = index + 1;
  }
  bs_resize(bits, index);
  
  return self;
}


static VALUE bits_to_ary(VALUE self) {
  VALUE ary;
  struct BitSet *bits;
  unsigned char *ptr, c;
  int bytes;
  int from, to, rest, index, len, cnt;
  
  Data_Get_Struct(self,  struct BitSet, bits);
  bytes = (bits->len+7)/8;
  
  
  ary = rb_ary_new();
  
  ptr = bits->ptr;
  index = 0;
  len   = bits->len;
  from  = -1;
  to    = -1;
  rest  = 0;
  while(index<len) {
    if (rest==0) {
      c = *ptr++;
      rest = 8;
    }
    /* printf("%6d(%d): %02x %d\n", index, rest, c, from); */
    if (from < 0) {
      cnt = ZERO_TABLE[c];
      if (cnt>rest) {
        cnt = rest;
      }
      rest -= cnt;
      index += cnt;
      if (rest>0) {
        c = c >> cnt;
        from = index;
      }
    } else {
      cnt = ONE_TABLE[c];
      rest -= cnt;
      index += cnt;
      if (rest>0) {
        c = c >> cnt;
        to = index - 1;
        if (from==to) {
          rb_ary_push(ary, INT2FIX(from));
        } else {
          rb_ary_push(ary, rb_funcall(rb_cRange, id_new, 2, INT2FIX(from), INT2FIX(to)));
        }
        from = -1;
        to   = -1;
      }
    }
  }
  if (from >= 0) {
    to = index - 1;
    if (from==to) {
      rb_ary_push(ary, INT2FIX(from));
    } else {
      rb_ary_push(ary, rb_funcall(rb_cRange, id_new, 2, INT2FIX(from), INT2FIX(to)));
    }
  }
  
  return ary;
}

static VALUE bits_to_bytes(VALUE self) {
  struct BitSet *bits;
  int bytes;
  
  Data_Get_Struct(self,  struct BitSet, bits);
  bytes = (bits->len+7)/8;
  
  return rb_str_new((char *)bits->ptr,bytes);
}

static VALUE bits_each(VALUE self) {
  struct BitSet *bits;
  int idx,len;
  
  Data_Get_Struct(self,  struct BitSet, bits);
  
  len = bits->len;
  for(idx=0; idx<len; idx++) {
    if (bs_get(bits, idx)) {
      rb_yield(INT2FIX(idx));
    }
  }
  
  return self;
}

static VALUE bits_count(VALUE self) {
  struct BitSet *bits;
  unsigned char *ptr, ch;
  int count;
  int bytes;
  int i;
  
  Data_Get_Struct(self,  struct BitSet, bits);
  bytes      = (bits->len+7)/8;
  
  count = 0;
  ptr = bits->ptr;
  for(i=0 ; i<bytes; i++) {
    ch = *ptr++;
    count += COUNT_TABLE[ch];
  }
  
  return INT2FIX(count);
}

void Init_bitset() {
  cBitSet =  rb_define_class("BitSet", rb_cObject);
  rb_include_module(cBitSet, rb_mComparable);
  
  rb_define_singleton_method(cBitSet, "new", bits_s_new, -1 ); /* tested */
  rb_define_singleton_method(cBitSet, "from_bin", bits_s_from_bin, 1 ); /* tested */
  
  rb_define_method(cBitSet, "size",       bits_length,    0); /* tested */
  rb_define_method(cBitSet, "size=",      bits_resize,    1); /* tested */
  rb_define_method(cBitSet, "length",     bits_length,    0); /* tested */
  rb_define_method(cBitSet, "bytes",      bits_bytes,     0); /* tested */
  rb_define_method(cBitSet, "to_s",       bits_to_s,      0); /* tested */
  rb_define_method(cBitSet, "dup",        bits_dup,       0); /* tested */
  rb_define_method(cBitSet, "clone",      bits_dup,       0); /* tested */
  rb_define_method(cBitSet, "clear",      bits_clear,     0); /* tested */
  rb_define_method(cBitSet, "get",        bits_get,       1); /* tested */
  rb_define_method(cBitSet, "set",        bits_set,       2); /* tested */
  rb_define_method(cBitSet, "on",         bits_on,       -1); /* tested */
  rb_define_method(cBitSet, "off",        bits_off,      -1); /* tested */
  
  rb_define_method(cBitSet, "|",          bits_or,        1); /* tested */
  rb_define_method(cBitSet, "~",          bits_not,       0); /* tested */
  rb_define_method(cBitSet, "&",          bits_and,       1); /* tested */
  rb_define_method(cBitSet, "^",          bits_xor,       1); /* tested */
  rb_define_method(cBitSet, "+",          bits_or,        1); /* tested */
  rb_define_method(cBitSet, "-",          bits_minus,     1); /* tested */
  rb_define_method(cBitSet, "*",          bits_and,       1); /* tested */
  rb_define_method(cBitSet, "<=>",        bits_cmp,       1); /* tested */
  
  rb_define_method(cBitSet, "zero?",      bits_zero,      0); /* tested */
  rb_define_method(cBitSet, "nonzero?",   bits_nonzero,   0); /* tested */
  rb_define_method(cBitSet, "max",        bits_max,       0); /* tested */
  rb_define_method(cBitSet, "min",        bits_min,       0); /* tested */
  rb_define_method(cBitSet, "normalize",  bits_norm,      0); /* tested */
  rb_define_method(cBitSet, "normalize!", bits_normx,     0); /* tested */
  
  rb_define_method(cBitSet, "to_ary",     bits_to_ary,    0); /* tested */
  rb_define_method(cBitSet, "to_bytes",   bits_to_bytes,  0); /* tested */
  rb_define_method(cBitSet, "count",      bits_count,     0); /* tested */

  rb_define_method(cBitSet, "each",       bits_each,      0); /* tested */
  /* rb_include_module(cBitSet, rb_mEnumerable); */
  
  id_new   = rb_intern("new");
  id_first = rb_intern("first");
  id_end   = rb_intern("end");
}



syntax highlighted by Code2HTML, v. 0.9.1