it.unimi.dsi.bits.LongArrayBitVector Maven / Gradle / Ivy
Show all versions of dsi-utils Show documentation
package it.unimi.dsi.bits;
/*
* DSI utilities
*
* Copyright (C) 2007-2009 Sebastiano Vigna
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library 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 Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
import it.unimi.dsi.fastutil.longs.LongArrays;
import it.unimi.dsi.util.LongBigList;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
/** A bit vector implementation based on arrays of longs.
*
* The main goal of this class is to be fast and flexible. It implements a lightweight,
* fast, open, optimized, reuse-oriented version of bit vectors. Instances of this class
* represent a bit vector an array of longs that is enlarged as needed when new entries
* are created (by dividing the current length by the golden ratio), but is
* never made smaller (even on a {@link #clear()}). Use
* {@link #trim()} for that purpose.
*
*
Besides usual methods for setting and getting bits, this class provides views
* that make it possible to access comfortably the bit vector in different ways: for instance,
* {@link #asLongBigList(int)} provide access as a list of longs, whereas
* {@link #asLongSet()} provides access in setwise form.
*
*
When enlarging the underlying array (e.g., for {@link #append(long, int)} operations or
* add operations on the {@linkplain #asLongBigList(int) big list view}), or when
* invoking {@link #ensureCapacity(long)}, this class calls
* {@link LongArrays#grow(long[], int, int)}, which could enlarge the array more than
* expected. On the contrary, {@link #length(long)} (and the corresponding method in the
* {@linkplain #asLongBigList(int) big list view}) size the underlying array in an exact manner.
*
*
Bit numbering follows the right-to-left convention: bit k (counted from the
* right) of word w is bit 64w + k of the overall bit vector.
*
*
If {@link #CHECKS} is true at compile time, boundary checks for all bit operations
* will be compiled in. For maximum speed, you may want to recompile this class with {@link #CHECKS}
* set to false. {@link #CHECKS} is public, so you can check from your code whether you're
* being provided a version with checks or not.
*
*
Warning: A few optional methods have still to be implemented (e.g.,
* adding an element at an arbitrary position using the list view).
*
*
Warning: In some cases, you might want to cache locally the result
* of {@link #bits()} to speed up computations on immutable bit vectors (this is what happens, for instance,
* in static ranking structures). This class, however, does its own serialisation
* of the bit vector: as a result, all cached references to the result of {@link #bits()}
* must be marked as transient and rebuilt at deserialisation
* time, or you will end up saving the bits twice.
*/
// TODO: take care that unused bits are zeroes
// TODO: implement subVector properly
public class LongArrayBitVector extends AbstractBitVector implements Cloneable, Serializable {
private static final long serialVersionUID = 1L;
public final static int LOG2_BITS_PER_WORD = 6;
public final static int BITS_PER_WORD = 1 << LOG2_BITS_PER_WORD;
public final static int WORD_MASK = BITS_PER_WORD - 1;
public final static int LAST_BIT = BITS_PER_WORD - 1;
public final static long ALL_ONES = 0xFFFFFFFFFFFFFFFFL;
public final static long LAST_BIT_MASK = 1L << LAST_BIT;
/** Whether this class has been compiled with index checks or not. */
public final static boolean CHECKS = true;
private static final boolean ASSERTS = false;
/** The number of bits in this vector. */
protected long length;
/** The backing array of this vector. Bit 0 of the first element contains bit 0 of the bit vector,
* bit 1 of the second element contains bit {@link #BITS_PER_WORD} of the bit vector and so on. */
protected transient long[] bits;
/** Returns the number of words that are necessary to hold the given number of bits.
*
* @param size a number of bits.
* @return the number of words that are necessary to hold the given number of bits.
*/
protected final static int numWords( final long size ) {
if ( ASSERTS ) assert ( size + WORD_MASK ) >>> LOG2_BITS_PER_WORD <= Integer.MAX_VALUE;
return (int)( ( size + WORD_MASK ) >>> LOG2_BITS_PER_WORD );
}
/** Return the index of the word that holds a bit of specified index.
*
* @param index the index of a bit, or -1.
* @return the index of the word that holds the bit of given index, or -1
* if index
is -1.
*/
protected final static int word( final long index ) {
if ( ASSERTS ) assert index >>> LOG2_BITS_PER_WORD <= Integer.MAX_VALUE;
return (int)( index >> LOG2_BITS_PER_WORD );
}
/** Returns the inside-word index of the bit that would hold the bit of specified index.
*
*
Note that bit 0 is positioned in word 0, index 0, bit 1 in word 0, index 1, …,
* bit {@link #BITS_PER_WORD} in word 0, index 0, bit {@link #BITS_PER_WORD} + 1 in word 1, index 1,
* and so on.
*
* @param index the index of a bit.
* @return the inside-word index of the bit that would hold the bit of specified index.
*/
protected final static int bit( final long index ) {
return (int)( index & WORD_MASK );
}
/** Returns a mask having a 1 exactly at the bit {@link #bit(long) bit(index)}.
*
* @param index the index of a bit
* @return a mask having a 1 exactly at the bit {@link #bit(long) bit(index)}.
*/
protected final static long mask( final long index ) {
return 1L << ( index & WORD_MASK );
}
protected LongArrayBitVector( final long capacity ) {
this.bits = capacity > 0 ? new long[ numWords( capacity ) ] : LongArrays.EMPTY_ARRAY;
}
/** Creates a new empty bit vector of given capacity. The
* resulting vector will be able to contain capacity
* bits without reallocations of the backing array.
*
*
Note that this constructor creates an empty bit vector.
* If you want a cleared bit vector of a specified size, please
* use the {@link #ofLength(long)} factory method.
*
* @param capacity the capacity (in bits) of the new bit vector.
* @return a new bit vector of given capacity.
*/
public static LongArrayBitVector getInstance( final long capacity ) {
return new LongArrayBitVector( capacity );
}
/** Creates a new empty bit vector. No allocation is actually performed.
* @return a new bit vector with no capacity.
*/
public static LongArrayBitVector getInstance() {
return new LongArrayBitVector( 0 );
}
/** Creates a new empty bit vector of given length.
*
* @param length the size (in bits) of the new bit vector.
*/
public static LongArrayBitVector ofLength( final long length ) {
return new LongArrayBitVector( length ).length( length );
}
/** Creates a new bit vector with given bits.
*
* @param bit a list of bits that will be set in the newly created bit vector.
*/
public static LongArrayBitVector of( final int... bit ) {
final LongArrayBitVector bitVector = new LongArrayBitVector( bit.length );
for( int b : bit ) bitVector.add( b );
return bitVector;
}
public long[] bits() {
return bits;
}
public long length() {
return length;
}
/** Ensures that this bit vector can hold the specified number of bits.
*
*
This method uses {@link LongArrays#grow(long[], int, int)} to
* ensure that there is enough space for the given number of bits. As a
* consequence, the actual length of the long array allocated might be
* larger than expected.
*
* @param numBits the number of bits that this vector must be able to contain.
* @return this bit vector.
*/
public LongArrayBitVector ensureCapacity( final long numBits ) {
bits = LongArrays.grow( bits, numWords( numBits ), numWords( length ) );
return this;
}
public LongArrayBitVector length( final long newLength ) {
bits = LongArrays.ensureCapacity( bits, numWords( newLength ), numWords( length ) );
final long oldLength = length;
if ( newLength < oldLength ) fill( newLength, oldLength, false );
length = newLength;
return this;
}
@Override
public void fill( final boolean value ) {
final int fullWords = (int)( length / Long.SIZE );
LongArrays.fill( bits, 0, fullWords, value ? 0xFFFFFFFFFFFFFFFFL : 0L );
if ( length % Long.SIZE != 0 ) {
if ( value ) bits[ fullWords ] = ( 1L << length % Long.SIZE ) - 1;
else bits[ fullWords ] = 0;
}
}
@Override
public void fill( final long from, final long to, final boolean value ) {
if ( to / Long.SIZE == from / Long.SIZE ) {
if ( value ) bits[ (int)( from / Long.SIZE ) ] |= ( 1L << to - from ) - 1 << from;
else bits[ (int)( from / Long.SIZE ) ] &= ~( ( 1L << to - from ) - 1 << from );
return;
}
LongArrays.fill( bits, (int)( ( from + Long.SIZE - 1 ) / Long.SIZE ), (int)( to / Long.SIZE ), value ? -1L : 0L );
if ( from % Long.SIZE != 0 ) {
if ( value ) bits[ (int)( from / Long.SIZE ) ] |= -1L << from % Long.SIZE;
else bits[ (int)( from / Long.SIZE ) ] &= ( 1L << from % Long.SIZE ) - 1;
}
if ( to % Long.SIZE != 0 ) {
if ( value ) bits[ (int)( to / Long.SIZE ) ] |= ( 1L << to % Long.SIZE ) - 1;
else bits[ (int)( to / Long.SIZE ) ] &= -1L << to % Long.SIZE;
}
}
@Override
public void flip() {
final int fullWords = (int)( length / Long.SIZE );
for( int i = fullWords; i-- != 0; ) bits[ i ] ^= 0xFFFFFFFFFFFFFFFFL;
if ( length % Long.SIZE != 0 ) bits[ fullWords ] ^= ( 1L << length % Long.SIZE ) - 1;
}
/** Reduces as must as possible the size of the backing array.
*
* @return true if some trimming was actually necessary.
*/
public boolean trim() {
if ( bits.length == numWords( length ) ) return false;
bits = LongArrays.setLength( bits, numWords( length ) );
return true;
}
/** Sets the size of this bit vector to 0.
*
Note that this method does not try to reallocate that backing array.
* If you want to force that behaviour, call {@link #trim()} afterwards.
*/
public void clear() {
LongArrays.fill( bits, 0, word( length - 1 ) + 1, 0 );
length = 0;
}
public LongArrayBitVector copy( final long from, final long to ) {
BitVectors.ensureFromTo( length, from, to );
final LongArrayBitVector copy = new LongArrayBitVector( to - from );
if ( ( copy.length = to - from ) == 0 ) return copy;
final int numWords = numWords( to - from );
final int startWord = word( from );
final int startBit = bit( from );
if ( startBit == 0 ) {
// If we're copying from the first bit, we just copy the array.
System.arraycopy( bits, startWord, copy.bits, 0, numWords );
final int endBit = bit( to );
if ( endBit > 0 ) copy.bits[ numWords - 1 ] &= ( 1L << endBit ) - 1;
}
else if ( startWord == word( to - 1 ) ) {
// Same word, startBit > 0
copy.bits[ 0 ] = bits[ startWord ] >>> startBit & ( ( 1L << to - from ) - 1 );
}
else {
final int bitsPerWordMinusStartBit = BITS_PER_WORD - startBit;
final long[] bits = this.bits;
final long[] copyBits = copy.bits;
copyBits[ 0 ] = bits[ startWord ] >>> startBit;
for( int word = 1; word < numWords; word++ ) {
copyBits[ word - 1 ] |= bits[ word + startWord ] << bitsPerWordMinusStartBit;
copyBits[ word ] = bits[ word + startWord ] >>> startBit;
}
final int endBit = bit( to - from );
if ( endBit == 0 ) copyBits[ numWords - 1 ] |= bits[ numWords + startWord ] << bitsPerWordMinusStartBit;
else {
if ( endBit > bitsPerWordMinusStartBit ) copyBits[ numWords - 1 ] |= bits[ numWords + startWord ] << bitsPerWordMinusStartBit;
copyBits[ numWords - 1 ] &= ( 1L << endBit ) - 1;
}
}
return copy;
}
public LongArrayBitVector copy() {
LongArrayBitVector copy = new LongArrayBitVector( length );
copy.length = length;
System.arraycopy( bits, 0, copy.bits, 0, numWords( length ) );
return copy;
}
/** Returns this bit vector.
*
* @return this bit vector.
*/
public LongArrayBitVector fast() {
return this;
}
/** Returns a copy of the given bit vector.
*
*
This method uses {@link BitVector#getLong(long, long)} on {@link Long#SIZE} boundaries to copy at high speed.
*
* @param bv a bit vector.
* @return an instance of this class containing a copy of the given vector.
*/
public static LongArrayBitVector copy( final BitVector bv ) {
final long length = bv.length();
final LongArrayBitVector copy = new LongArrayBitVector( length );
final long fullBits = length - length % Long.SIZE;
for( long i = 0; i < fullBits; i += Long.SIZE ) copy.bits[ (int)( i / Long.SIZE ) ] = bv.getLong( i, i + Long.SIZE );
if ( length % Long.SIZE != 0 ) copy.bits[ (int)( fullBits / Long.SIZE ) ] = bv.getLong( fullBits, length );
copy.length = length;
return copy;
}
public boolean getBoolean( final long index ) {
if ( CHECKS ) ensureRestrictedIndex( index );
return ( bits[ word( index ) ] & mask( index ) ) != 0;
}
public boolean set( final long index, final boolean value ) {
if ( CHECKS ) ensureRestrictedIndex( index );
final int word = word( index );
final long mask = mask( index );
final boolean oldValue = ( bits[ word ] & mask ) != 0;
if ( value ) bits[ word ] |= mask;
else bits[ word ] &= ~mask;
return oldValue != value;
}
public void set( final long index ) {
if ( CHECKS ) ensureRestrictedIndex( index );
bits[ word( index ) ] |= mask( index );
}
public void clear( final long index ) {
if ( CHECKS ) ensureRestrictedIndex( index );
bits[ word( index ) ] &= ~mask( index );
}
public void add( final long index, final boolean value ) {
if ( CHECKS ) ensureIndex( index );
if ( length == bits.length << LOG2_BITS_PER_WORD ) bits = LongArrays.grow( bits, numWords( length + 1 ) );
length++;
if ( index == length - 1 ) set( index, value );
else {
final int word = word( index );
final int bit = bit( index );
boolean carry = ( bits[ word ] & LAST_BIT_MASK ) != 0, nextCarry;
long t = bits[ word ];
if ( bit == LAST_BIT ) t &= ~LAST_BIT_MASK;
else t = ( t & - ( 1L << bit ) ) << 1 | t & ( 1L << bit ) - 1;
if ( value ) t |= 1L << bit;
bits[ word ] = t;
final int numWords = numWords( length );
for( int i = word + 1; i < numWords; i++ ) {
nextCarry = ( bits[ i ] & LAST_BIT_MASK ) != 0;
bits[ i ] <<= 1;
if ( carry ) bits[ i ] |= 1;
carry = nextCarry;
}
}
return;
}
public boolean removeBoolean( final long index ) {
if ( CHECKS ) ensureRestrictedIndex( index );
final boolean oldValue = getBoolean( index );
final long[] bits = this.bits;
final int word = word( index );
final int bit = bit( index );
bits[ word ] = ( bits[ word ] & - ( 1L << bit ) << 1 ) >>> 1 | bits[ word ] & ( 1L << bit ) - 1;
final int numWords = numWords( length-- );
for( int i = word + 1; i < numWords; i++ ) {
if ( ( bits[ i ] & 1 ) != 0 ) bits[ i - 1 ] |= LAST_BIT_MASK;
bits[ i ] >>>= 1;
}
return oldValue;
}
public LongArrayBitVector append( long value, int width ) {
if ( width == 0 ) return this;
if ( CHECKS && width < Long.SIZE && ( value & -1L << width ) != 0 ) throw new IllegalArgumentException( "The specified value (" + value + ") is larger than the maximum value for the given width (" + width + ")" );
final long length = this.length;
final int startWord = word( length );
final int startBit = bit( length );
ensureCapacity( length + width );
if ( startBit + width <= Long.SIZE ) bits[ startWord ] |= value << startBit;
else {
bits[ startWord ] |= value << startBit;
bits[ startWord + 1 ] = value >>> BITS_PER_WORD - startBit;
}
this.length += width;
return this;
}
public long getLong( long from, long to ) {
if ( CHECKS ) BitVectors.ensureFromTo( length, from, to );
final long l = Long.SIZE - ( to - from );
final int startWord = word( from );
final int startBit = bit( from );
if ( l == Long.SIZE ) return 0;
if ( startBit <= l ) return bits[ startWord ] << l - startBit >>> l;
return bits[ startWord ] >>> startBit | bits[ startWord + 1 ] << Long.SIZE + l - startBit >>> l;
}
public long count() {
long c = 0;
for( int i = numWords( length ); i-- != 0; ) c += Fast.count( bits[ i ] );
return c;
}
public long nextOne( final long index ) {
if ( index >= length ) return -1;
final long[] bits = this.bits;
final long words = numWords( length );
final int from = word( index );
final long maskedFirstWord = bits[ from ] & -( 1L << bit( index ) );
if ( maskedFirstWord != 0 ) return from * BITS_PER_WORD + Fast.leastSignificantBit( maskedFirstWord );
for ( int i = from + 1; i < words; i++ )
if ( bits[ i ] != 0 ) return i * BITS_PER_WORD + Fast.leastSignificantBit( bits[ i ] );
return -1;
}
public long previousOne( final long index ) {
if ( index == 0 ) return -1;
final long[] bits = this.bits;
final int from = word( index - 1 );
final long mask = 1L << bit( index - 1 );
final long maskedFirstWord = bits[ from ] & ( mask | mask - 1 );
if ( maskedFirstWord != 0 ) return from * BITS_PER_WORD + Fast.mostSignificantBit( maskedFirstWord );
for ( int i = from; i-- != 0; )
if ( bits[ i ] != 0 ) return i * BITS_PER_WORD + Fast.mostSignificantBit( bits[ i ] );
return -1;
}
public long nextZero( final long index ) {
if ( index >= length ) return -1;
final long[] bits = this.bits;
final long words = numWords( length );
final int from = word( index );
long maskedFirstWord = bits[ from ] | ( 1L << bit( index ) ) - 1;
if ( maskedFirstWord != 0xFFFFFFFFFFFFFFFFL ) {
final long result = from * BITS_PER_WORD + Fast.leastSignificantBit( ~maskedFirstWord );
return result >= length ? -1 : result;
}
for ( int i = from + 1; i < words; i++ )
if ( bits[ i ] != 0xFFFFFFFFFFFFFFFFL ) {
final long result = i * BITS_PER_WORD + Fast.leastSignificantBit( ~bits[ i ] );
return result >= length ? -1 : result;
}
return -1;
}
public long previousZero( final long index ) {
if ( index == 0 ) return -1;
final long[] bits = this.bits;
final int from = word( index - 1 );
long maskedFirstWord = bits[ from ] | -1L << bit( index );
if ( from == word( length - 1 ) ) maskedFirstWord |= -1L << length % Long.SIZE;
if ( maskedFirstWord != 0xFFFFFFFFFFFFFFFFL )
return from * BITS_PER_WORD + Fast.mostSignificantBit( ~maskedFirstWord );
for ( int i = from; i-- != 0; )
if ( bits[ i ] != 0xFFFFFFFFFFFFFFFFL )
return i * BITS_PER_WORD + Fast.mostSignificantBit( ~bits[ i ] );
return -1;
}
public long longestCommonPrefixLength( final BitVector v ) {
if ( v instanceof LongArrayBitVector ) return longestCommonPrefixLength( (LongArrayBitVector)v );
return super.longestCommonPrefixLength( v );
}
public long longestCommonPrefixLength( final LongArrayBitVector v ) {
final long minLength = Math.min( v.length(), length() );
final long words = ( minLength + BITS_PER_WORD - 1 ) >> LOG2_BITS_PER_WORD;
final long[] bits = this.bits;
final long[] vBits = v.bits;
for ( int i = 0; i < words; i++ )
if ( bits[ i ] != vBits[ i ] )
return Math.min( minLength, i * BITS_PER_WORD + Fast.leastSignificantBit( bits[ i ] ^ vBits[ i ] ) );
return minLength;
}
public BitVector and( final BitVector v ) {
if ( v instanceof LongArrayBitVector ) {
LongArrayBitVector l = (LongArrayBitVector)v;
int words = Math.min( numWords( length() ), numWords( l.length() ) );
while( words-- != 0 ) bits[ words ] &= l.bits[ words ];
}
else super.and( v );
return this;
}
public BitVector or( final BitVector v ) {
if ( v instanceof LongArrayBitVector ) {
LongArrayBitVector l = (LongArrayBitVector)v;
int words = Math.min( numWords( length() ), numWords( l.length() ) );
while( words-- != 0 ) bits[ words ] |= l.bits[ words ];
}
else super.or( v );
return this;
}
public BitVector xor( final BitVector v ) {
if ( v instanceof LongArrayBitVector ) {
LongArrayBitVector l = (LongArrayBitVector)v;
int words = Math.min( numWords( length() ), numWords( l.length() ) );
while( words-- != 0 ) bits[ words ] ^= l.bits[ words ];
}
else super.xor( v );
return this;
}
/** Wraps the given array of longs in a bit vector for the given number of bits.
*
*
Note that all bits in array
beyond that of index
* size
must be unset, or an exception will be thrown.
*
* @param array an array of longs.
* @param size the number of bits of the newly created bit vector.
* @return a bit vector of size size
using array
as backing array.
*/
public static LongArrayBitVector wrap( final long[] array, final long size ) {
if ( size > array.length << LOG2_BITS_PER_WORD ) throw new IllegalArgumentException( "The provided array is too short (" + array.length + " elements) for the given size (" + size + ")" );
final LongArrayBitVector result = new LongArrayBitVector( 0 );
result.length = size;
result.bits = array;
final int arrayLength = array.length;
final int lastWord = (int)( size / Long.SIZE );
if ( lastWord < arrayLength && ( array[ lastWord ] & ~ ( ( 1L << size % Long.SIZE ) - 1 ) ) != 0 ) throw new IllegalArgumentException( "Garbage beyond size in bit array" );
for( int i = lastWord + 1; i < arrayLength; i++ ) if ( array[ i ] != 0 ) throw new IllegalArgumentException( "Garbage beyond size in bit array" );
return result;
}
/** Wraps the given array of longs in a bit vector.
*
* @param array an array of longs.
* @return a bit vector of size array.length * Long.SIZE
using array
as backing array.
*/
public static LongArrayBitVector wrap( final long[] array ) {
return wrap( array, array.length * Long.SIZE );
}
/** Returns a cloned copy of this bit vector.
*
*
This method is functionally equivalent to {@link #copy()},
* except that {@link #copy()} trims the backing array.
*
* @return a copy of this bit vector.
*/
public LongArrayBitVector clone() throws CloneNotSupportedException {
LongArrayBitVector copy = (LongArrayBitVector)super.clone();
copy.bits = bits.clone();
return copy;
}
public LongArrayBitVector replace( final LongArrayBitVector bv ) {
ensureCapacity( bv.length );
final long[] bits = this.bits;
final long[] bvBits = bv.bits;
final int bvFirstFreeWord = word( bv.length - 1 ) + 1;
for( int i = bvFirstFreeWord; i-- != 0; ) bits[ i ] = bvBits[ i ];
final int thisFirstFreeWord = word( length - 1 ) + 1;
if ( bvFirstFreeWord < thisFirstFreeWord ) LongArrays.fill( this.bits, bvFirstFreeWord, thisFirstFreeWord, 0 );
this.length = bv.length;
return this;
}
@Override
public LongArrayBitVector replace( final BitVector bv ) {
final long bvLength = bv.length();
ensureCapacity( bvLength );
final long[] bits = this.bits;
final long fullBits = bvLength - bvLength % Long.SIZE;
for( long i = 0; i < fullBits; i += Long.SIZE ) bits[ (int)( i / Long.SIZE ) ] = bv.getLong( i, i + Long.SIZE );
final int bvFirstFreeWord = word( bvLength - 1 ) + 1;
final int thisFirstFreeWord = word( length - 1 ) + 1;
if ( bvLength % Long.SIZE != 0 ) bits[ (int)( fullBits / Long.SIZE ) ] = bv.getLong( fullBits, bvLength );
if ( bvFirstFreeWord < thisFirstFreeWord ) LongArrays.fill( this.bits, bvFirstFreeWord, thisFirstFreeWord, 0 );
this.length = bvLength;
return this;
}
public boolean equals( final Object o ) {
if ( o instanceof LongArrayBitVector ) return equals( (LongArrayBitVector) o );
return super.equals( o );
}
public boolean equals( final LongArrayBitVector v ) {
if ( length != v.length() ) return false;
int i = numWords( length );
while( i-- != 0 ) if ( bits[ i ] != v.bits[ i ] ) return false;
return true;
}
public int hashCode() {
long h = 0x9e3779b97f4a7c13L ^ length;
final int numWords = numWords( length );
for( int i = 0; i < numWords; i++ ) h ^= ( h << 5 ) + bits[ i ] + ( h >>> 2 );
if ( ASSERTS ) assert (int)( ( h >>> 32 ) ^ h ) == super.hashCode();
return (int)( ( h >>> 32 ) ^ h );
}
/** A list-of-integers view of a bit vector.
*
*
This class implements in the obvious way a view
* of a bit vector as a list of integers of given width. The vector is enlarged as needed (i.e., when
* adding new elements), but it is never shrunk.
*/
protected static class LongBigListView extends AbstractBitVector.LongBigListView {
private static final long serialVersionUID = 1L;
@SuppressWarnings("hiding")
final private LongArrayBitVector bitVector;
public LongBigListView( final LongArrayBitVector bitVector, final int width ) {
super( bitVector, width );
this.bitVector = bitVector;
}
@Override
public boolean add( long value ) {
bitVector.append( value, width );
return true;
}
@Override
public long getLong( long index ) {
final long start = index * width;
return bitVector.getLong( start, start + width );
}
@Override
public void clear() {
bitVector.clear();
}
@Override
public long set( long index, long value ) {
if ( width == 0 ) return 0;
if ( width != Long.SIZE && value > fullMask ) throw new IllegalArgumentException( "Value too large: " + value );
final long bits[] = bitVector.bits;
final long start = index * width;
final int startWord = word( start );
final int endWord = word( start + width - 1 );
final int startBit = bit( start );
final long oldValue;
if ( startWord == endWord ) {
oldValue = bits[ startWord ] >>> startBit & fullMask;
bits[ startWord ] &= ~ ( fullMask << startBit );
bits[ startWord ] |= value << startBit;
if ( ASSERTS ) assert value == ( bits[ startWord ] >>> startBit & fullMask );
}
else {
// Here startBit > 0.
oldValue = bits[ startWord ] >>> startBit | bits[ endWord ] << ( BITS_PER_WORD - startBit ) & fullMask;
bits[ startWord ] &= ( 1L << startBit ) - 1;
bits[ startWord ] |= value << startBit;
bits[ endWord ] &= - ( 1L << width - BITS_PER_WORD + startBit );
bits[ endWord ] |= value >>> BITS_PER_WORD - startBit;
if ( ASSERTS ) assert value == ( bits[ startWord ] >>> startBit | bits[ endWord ] << ( BITS_PER_WORD - startBit ) & fullMask );
}
return oldValue;
}
}
@Override
public LongBigList asLongBigList( final int width ) {
return new LongBigListView( this, width );
}
private void writeObject( final ObjectOutputStream s ) throws IOException {
s.defaultWriteObject();
final int numWords = numWords( length );
for( int i = 0; i < numWords; i++ ) s.writeLong( bits[ i ] );
}
private void readObject( final ObjectInputStream s ) throws IOException, ClassNotFoundException {
s.defaultReadObject();
final int numWords = numWords( length );
bits = new long[ numWords ];
for( int i = 0; i < numWords; i++ ) bits[ i ] = s.readLong();
}
}