package edu.northwestern.at.utils; import java.io.Serializable; import java.util.Enumeration; import java.util.Set; /** A set of bits. The set automatically grows as more bits are needed. * * @version 1.00, 25 Jul 1999 * @author C. Scott Ananian * * <p> * Modications by Philip R. "Pib" Burns to add missing BitSet methods. * </p> */ public class SparseBitSet implements XCloneable, Serializable { /** Sorted array of bit-block offsets. */ protected int offs[]; /** Array of bit-blocks; each holding BITS bits. */ protected long bits[]; /** Number of blocks currently in use. */ protected int size; /** log base 2 of BITS, for the identity: x/BITS == x >> LG_BITS */ protected static final int LG_BITS = 6; /** Number of bits in a block. */ protected static final int BITS = ( 1 << LG_BITS ); /** BITS-1, using the identity: x % BITS == x & (BITS-1) */ protected static final int BITS_M1 = BITS - 1; /** Create an empty set. */ public SparseBitSet() { bits = new long[ 4 ]; offs = new int[ 4 ]; size = 0; } /** Create an empty set with the specified size. * * @param nbits The size of the set. */ public SparseBitSet( int nbits ) { this(); } /** Create an empty set with the same size as the given set. */ public SparseBitSet( SparseBitSet set ) { bits = new long[ set.size ]; offs = new int[ set.size ]; size = 0; } protected void new_block( int bnum ) { new_block( bsearch( bnum ) , bnum ); } protected void new_block( int idx , int bnum ) { if ( size == bits.length ) { long[] nbits = new long[ size * 3 ]; int[] noffs = new int[ size * 3 ]; System.arraycopy( bits , 0 , nbits , 0 , size ); System.arraycopy( offs , 0 , noffs , 0 , size ); bits = nbits; offs = noffs; } insert_block( idx , bnum ); } protected void insert_block( int idx , int bnum ) { System.arraycopy( bits , idx , bits , idx + 1 , size - idx ); System.arraycopy( offs , idx , offs , idx + 1 , size - idx ); offs[ idx ] = bnum; bits[ idx ] = 0; size++; } protected int bsearch( int bnum ) { int l = 0 , r = size; // search interval is [l, r) while ( l < r ) { int p = ( l + r ) / 2; if ( bnum < offs[ p ] ) r = p; else if ( bnum > offs[ p ] ) l = p + 1; else return p; } return l; // index at which the bnum *should* be, if it's not. } /** Set a bit. * * @param bit The bit to set. */ public void set( int bit ) { int bnum = ( bit >> LG_BITS ); int idx = bsearch( bnum ); if ( ( idx >= size ) || ( offs[ idx ] != bnum ) ) { new_block( idx , bnum ); } bits[ idx ] |= ( 1L << ( bit & BITS_M1 ) ); } /** Clear a bit. * * @param bit The bit to be cleared. */ public void clear( int bit ) { int bnum = ( bit >> LG_BITS ); int idx = bsearch( bnum ); if ( ( idx >= size ) || ( offs[ idx ] != bnum ) ) { new_block( idx , bnum ); } bits[ idx ] &= ~( 1L << ( bit & BITS_M1 ) ); } /** Clear all bits. */ public void clear() { size = 0; } /** Clears a range of bits. * * @param fromIndex Index of first bit to be cleared. * @param toIndex Index + 1 of last bit to be cleared. */ public void clear( int fromIndex , int toIndex ) { for ( int bit = fromIndex ; bit < toIndex ; bit++ ) { clear( bit ); } } /** Flip a bit value. * * @param bit Bit to flip. */ public void flip( int bit ) { if ( get( bit ) ) { clear( bit ); } else { set( bit ); } } /** Flips a range of bits. * * @param fromIndex Index of first bit to be flipped. * @param toIndex Index + 1 of last bit to be flipped. */ public void flip( int fromIndex , int toIndex ) { for ( int bit = fromIndex ; bit < toIndex ; bit++ ) { flip( bit ); } } /** Get bit value. * * @param bit Bit whose value is to be retrieved. * * @return true if bit set, false otherwise. */ public boolean get( int bit ) { int bnum = ( bit >> LG_BITS ); int idx = bsearch( bnum ); if ( ( idx >= size ) || ( offs[ idx ] != bnum ) ) { return false; } return ( 0 != ( bits[ idx ] & ( 1L << ( bit & BITS_M1 ) ) ) ); } /** Logically AND this bit set with another. * * @param otherSet The other bit set to AND against. */ public void and( SparseBitSet otherSet ) { binop( this , otherSet , AND ); } /** Logically OR this bit set with another. * * @param otherSet The other bit set to be ORed against. */ public void or( SparseBitSet otherSet ) { binop( this , otherSet , OR ); } /** Logically XOR this bit set with another. * * @param otherSet The other bit set to be XORed against. */ public void xor( SparseBitSet otherSet ) { binop( this , otherSet , XOR ); } /** Logically ANDNOT this bit set with another. * * @param otherSet The other bit set to be ANDNOTed against. */ public void andNot( SparseBitSet otherSet ) { binop( this , otherSet , ANDNOT ); } // BINARY OPERATION MACHINERY protected static interface BinOp { public long op( long a , long b ); } protected static final BinOp AND = new BinOp() { public final long op( long a , long b ) { return a & b; } }; protected static final BinOp OR = new BinOp() { public final long op(long a, long b) { return a | b; } }; protected static final BinOp XOR = new BinOp() { public final long op(long a, long b) { return a ^ b; } }; protected static final BinOp ANDNOT = new BinOp() { public final long op( long a , long b ) { return a & ~b; } }; protected static final void binop ( SparseBitSet a , SparseBitSet b , BinOp op ) { int nsize = a.size + b.size; long[] nbits; int[] noffs; int a_zero, a_size; // Be very clever and avoid allocating // more memory if we can. if (a.bits.length < nsize) { // Need to make working space. nbits = new long[ nsize ]; noffs = new int[ nsize ]; a_zero = 0; a_size = a.size; } else { // Reduce, reuse, recycle! nbits = a.bits; noffs = a.offs; a_zero = a.bits.length - a.size; a_size = a.bits.length; System.arraycopy( a.bits , 0 , a.bits , a_zero , a.size ); System.arraycopy( a.offs , 0 , a.offs , a_zero , a.size ); } // Crunch through and binop those sets! nsize = 0; for ( int i = a_zero , j = 0 ; i < a_size || j < b.size ; ) { long nb; int no; if ( ( i < a_size ) && ( ( j >= b.size ) || ( a.offs[ i ] < b.offs[ j ] ) ) ) { nb = op.op( a.bits[i], 0); no = a.offs[i]; i++; } else if ( ( j < b.size ) && ( ( i >= a_size ) || ( a.offs[ i ] > b.offs[ j ] ) ) ) { nb = op.op( 0 , b.bits[ j ] ); no = b.offs[ j ]; j++; } else { // Equal keys; merge. nb = op.op( a.bits[ i ] , b.bits[ j ] ); no = a.offs[ i ]; i++; j++; } if ( nb != 0 ) { nbits[ nsize ] = nb; noffs[ nsize ] = no; nsize++; } } a.bits = nbits; a.offs = noffs; a.size = nsize; } /** Return hash code for set. * * @return The hash code. */ public int hashCode() { long hash = 1234; for ( int i = 0 ; i < size ; i++ ) { hash ^= bits[ i ] * offs[ i ]; } return (int)( ( hash >> 32) ^ hash ); } /** Return set's size. */ public int size() { return ( size == 0 ) ? 0 : ( ( 1 + offs[ size - 1 ] ) << LG_BITS ); } /** Compare this set against another. * * @param object The object to compare against. * * @return true the objects are equal, false otherwise. */ public boolean equals( Object object ) { boolean result = false; if ( (object != null ) && ( object instanceof SparseBitSet ) ) { result = equals( this , (SparseBitSet)object ); } return result; } /** Compare two SparseBitSets for equality. * * @return true if sets are equal, false otherwise. */ public static boolean equals( SparseBitSet a , SparseBitSet b ) { for ( int i = 0 , j = 0 ; ( ( i < a.size ) || ( j < b.size ) ) ; ) { if ( ( i < a.size ) && ( ( j >= b.size ) || ( a.offs[ i ] < b.offs[ j ] ) ) ) { if ( a.bits[ i++ ] != 0 ) return false; } else if ( ( j < b.size ) && ( ( i >= a.size ) || ( a.offs[ i ] > b.offs[ j ] ) ) ) { if ( b.bits[ j++ ] != 0 ) return false; } else { if ( a.bits[ i++ ] != b.bits[ j++ ] ) return false; } } return true; } /** Return cardinality (# of bits set on) in this bit set. * * @return Cardinality of this bit set. */ public int cardinality() { int result = 0; Enumeration enumeration = elements(); while ( enumeration.hasMoreElements() ) { enumeration.nextElement(); result++; } return result; } /** Check if this set and another share at least one set bit. * * @param otherSet The other set to check for intersection * * @return true If this set and the other intersect. */ public boolean intersects( SparseBitSet otherSet ) { boolean result = false; int i = Math.min( size , otherSet.size ); while ( ( i > 0 ) && !result ) { result = ( ( bits[ i ] & otherSet.bits[ i ] ) != 0 ); i--; } return result; } /** Returns index of highest set bit in this set plus one. * * @return The logical size of this set. */ public int length() { int result = size(); while ( !get( result ) && ( result >= 0 ) ) { result--; } return result + 1; } /** Returns index of first bit set to true that occurs on or after specified starting index. * * @param fromIndex Index to start checking from (inclusive). * * @return Index of next bit set. * * @throws IndexOutOfBoundsException if specified index * is negative. * * <p> * If no such bit exists then -1 is returned. * To iterate over the true bits in a BitSet, use the following loop: * </p> * * <p> * <code> * for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) * { * // operate on index i here * } * </code> * </p> */ public int nextSetBit( int fromIndex ) { if ( fromIndex < 0 ) { throw new IndexOutOfBoundsException( "Index is negative" ); } int result = -1; int l = length(); for ( int i = fromIndex + 1 ; i < l ; i++ ) { if ( get( i ) ) { return i; } } return -1; } /** Returns index of first bit set to false that occurs on or after specified starting index. * * @param fromIndex Index to start checking from (inclusive). * * @return Index of next clear bit. * * @throws IndexOutOfBoundsException if specified index * is negative. * * <p> * If no such bit exists then -1 is returned. * To iterate over the false bits in a BitSet, use the following loop: * </p> * * <p> * <code> * for (int i = bs.nextClearBit(0); i >= 0; i = bs.nextClearBit(i+1)) * { * // operate on index i here * } * </code> * </p> */ public int nextClearBit( int fromIndex ) { if ( fromIndex < 0 ) { throw new IndexOutOfBoundsException( "Index is negative" ); } int result = -1; int l = length(); for ( int i = fromIndex + 1 ; i < l ; i++ ) { if ( !get( i ) ) { return i; } } return -1; } /** Clone this set. * * @return Clone of this bit set. */ public Object clone() { SparseBitSet set = new SparseBitSet(); set.bits = (long[])bits.clone(); set.offs = (int[])offs.clone(); set.size = size; return set; } /** Return an <code>Enumeration</code> of <code>Integer</code>s * indices of bits set on in this SparseBitSet. */ public Enumeration elements() { return new Enumeration() { int idx = -1, bit = BITS; { advance(); } public boolean hasMoreElements() { return ( idx < size ); } public Object nextElement() { int r = bit + ( offs[ idx ] << LG_BITS ); advance(); return new Integer( r ); } protected void advance() { while ( idx < size ) { while ( ++bit < BITS ) { if ( 0 != (bits[ idx ] & ( 1L << bit ) ) ) { return; } } idx++; bit = -1; } } }; } /** Converts the SparseBitSet to a String. */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append( '{' ); for ( Enumeration e = elements() ; e.hasMoreElements(); ) { if ( sb.length() > 1 ) sb.append( ", " ); sb.append( e.nextElement() ); } sb.append( '}' ); return sb.toString(); } /** Check validity. */ protected boolean isValid() { if ( bits.length != offs.length ) return false; if ( size > bits.length ) return false; if ( ( size != 0 ) && ( 0 <= offs[ 0 ] ) ) return false; for ( int i = 1 ; i < size ; i++ ) { if ( offs[ i ] < offs[ i - 1 ] ) { return false; } } return true; } }