/**
 *  Bit-mask represented in a growable bit-array.
 *   @author  Josef.Pelikan@mff.cuni.cz
 *   @version 0.007 (2001/06/16)
 *   @since   0.007
 */
public class BitMask {

    /** Bit-mask array - grows on demand. */
    protected byte[] mask;

    /** Minimum represented value (stored in <code>mask[0] & 1</code>). */
    protected int min;

    /** First non-represented value (after the end of an array). */
    protected int max;

    public BitMask () {
        empty();
    }

    /**
     *  [Re-]sets one flag value.
     *   @param  value Value to be [re-]set.
     *   @param  flag Set/reset.
     */
    public void set ( int value, boolean flag ) {
        if ( flag ) {
            assert(value);
            value -= min;
            mask[value>>3] |= (1 << (value & 7));
            return;
            }
        if ( value < min || value >= max ) return;
        value -= min;
        mask[value>>3] &= ~(1 << (value & 7));
    }

    /**
     *  Sets one flag value.
     *   @param  value Value to be set.
     */
    public void on ( int value ) {
        assert(value);
        value -= min;
        mask[value>>3] |= (1 << (value & 7));
    }

    /**
     *  Resets one flag value.
     *   @param  value Value to be reset.
     */
    public void off ( int value ) {
        if ( value < min || value >= max ) return;
        value -= min;
        mask[value>>3] &= ~(1 << (value & 7));
    }

    /**
     *  Prepares the mask array to hold the given value.
     *   @param  value Value which space will be reserved for.
     */
    public void assert ( int value ) {
        int add;
        byte[] newMask;
        if ( mask == null )
            min = max = (value & -8);
        if ( value < min ) {
            add = (min - value + 7) >> 3;
            newMask = new byte[mask.length+add];
            System.arraycopy(mask,0,newMask,add,mask.length);
            mask = newMask;
            min -= add << 3;
            return;
            }
        if ( value >= max ) {
            add = (value - min + 8) >> 3;
            newMask = new byte[add];
            if ( mask != null ) System.arraycopy(mask,0,newMask,0,mask.length);
            mask = newMask;
            max = min + (add << 3);
            }
    }

    public final void setUnsafe ( int value, boolean flag ) {
        value -= min;
        if ( flag ) mask[value>>3] |=  (1 << (value & 7));
        else        mask[value>>3] &= ~(1 << (value & 7));
    }

    /**
     *  Sets the given range.
     *   @param  from the first index.
     *   @param  len Number of flags to be [re-]set.
     *   @param  flag [Re-]set flag.
     */
    public void range ( int from, int len, boolean flag ) {
        if ( flag ) {
            assert(from);
            assert(from+len-1);
            }
        else {
            if ( from < min ) {
                len -= min - from;
                from = min;
                }
            if ( from + len > max )
                len = max - from;
            }
        while ( len-- > 0 ) setUnsafe(from++,flag);
    }

    /**
     *  Retrieves one flag value.
     *   @param  value The asked value.
     */
    public final boolean get ( int value ) {
        if ( value < min || value >= max ) return false;
        value -= min;
        return( (mask[value>>3] & (1<<(value & 7))) != 0 );
    }

    public final boolean getUnsafe ( int value ) {
        value -= min;
        return( (mask[value>>3] & (1<<(value & 7))) != 0 );
    }

    /**
     *  [Re-]initialize the mask.
     */
    public void empty () {
        mask = null;
        min = max = 0;
    }

    /**
     *  Bit-mask cardinality.
     *   @return The cardinal number of the mask.
     */
    public int card () {
        if ( mask == null ) return 0;
        int count = 0;
        int i;
        for ( i = 0; i < mask.length; ) {
            int act = mask[i++];
            while ( act != 0 ) {
                count += (act & 1);
                act >>>= 1;
                }
            }
        return count;
    }

}
