package it.unimi.dsi.bits; import it.unimi.dsi.bits.BitVector; import it.unimi.dsi.bits.BooleanListBitVector; import it.unimi.dsi.bits.LongArrayBitVector; import it.unimi.dsi.fastutil.io.BinIO; import it.unimi.dsi.fastutil.longs.LongBidirectionalIterator; import it.unimi.dsi.fastutil.longs.LongListIterator; import it.unimi.dsi.fastutil.longs.LongSortedSet; import it.unimi.dsi.util.LongBigList; import java.io.File; import java.io.IOException; import java.util.Arrays; import java.util.Random; import junit.framework.TestCase; public abstract class BitVectorTestCase extends TestCase { public static void testSetClearFlip( final BitVector v ) { final int size = v.size(); for( int i = size; i-- != 0; ) { v.set( i ); assertTrue( v.getBoolean( i ) ); } for( int i = size; i-- != 0; ) { v.clear( i ); assertFalse( v.getBoolean( i ) ); } for( int i = size; i-- != 0; ) { v.set( i ); v.flip( i ); assertFalse( v.getBoolean( i ) ); v.flip( i ); assertTrue( v.getBoolean( i ) ); } } public static void testRemove( final BitVector v ) { v.clear(); v.size( 100 ); v.set( 0, true ); assertTrue( v.removeBoolean( 0 ) ); assertFalse( v.getBoolean( 0 ) ); v.clear(); v.size( 100 ); v.set( 63, true ); v.set( 64, true ); assertTrue( v.removeBoolean( 63 ) ); assertTrue( v.getBoolean( 63 ) ); assertFalse( v.getBoolean( 64 ) ); assertFalse( v.getBoolean( 0 ) ); } public static void testAdd( final BitVector v ) { v.clear(); v.size( 100 ); v.add( 0, true ); assertTrue( v.getBoolean( 0 ) ); v.add( 0, true ); assertTrue( v.getBoolean( 0 ) ); assertTrue( v.getBoolean( 1 ) ); v.add( false ); assertTrue( v.getBoolean( 0 ) ); assertTrue( v.getBoolean( 1 ) ); assertFalse( v.getBoolean( 2 ) ); v.set( 1, false ); assertTrue( v.getBoolean( 0 ) ); assertFalse( v.getBoolean( 1 ) ); assertFalse( v.getBoolean( 2 ) ); v.set( 1, true ); assertTrue( v.getBoolean( 0 ) ); assertTrue( v.getBoolean( 1 ) ); assertFalse( v.getBoolean( 2 ) ); v.clear(); v.size( 100 ); v.add( 0, 1 ); assertEquals( 1, v.getInt( 0 ) ); v.add( 0, 2 ); assertEquals( 1, v.getInt( 0 ) ); assertEquals( 1, v.getInt( 1 ) ); v.add( 0, 0 ); assertEquals( 0, v.getInt( 0 ) ); assertEquals( 1, v.getInt( 1 ) ); assertEquals( 1, v.getInt( 2 ) ); v.add( 0 ); assertEquals( 0, v.getInt( 0 ) ); assertEquals( 1, v.getInt( 1 ) ); assertEquals( 1, v.getInt( 2 ) ); assertEquals( 0, v.getInt( 3 ) ); v.set( 1, 0 ); assertEquals( 0, v.getInt( 0 ) ); assertEquals( 0, v.getInt( 1 ) ); assertEquals( 1, v.getInt( 2 ) ); assertEquals( 0, v.getInt( 3 ) ); v.set( 1, 1 ); assertEquals( 0, v.getInt( 0 ) ); assertEquals( 1, v.getInt( 1 ) ); assertEquals( 1, v.getInt( 2 ) ); assertEquals( 0, v.getInt( 3 ) ); v.clear(); v.append( 1, 2 ); v.append( 1, 2 ); v.append( 3, 2 ); assertEquals( LongArrayBitVector.of( 1, 0, 1, 0, 1, 1 ), v ); v.clear(); for( int i = 0; i < 80; i++ ) v.add( 0, true ); for( int i = 0; i < 80; i++ ) assertTrue( v.getBoolean( i ) ); v.clear(); for( int i = 0; i < 80; i++ ) v.add( 0, false ); for( int i = 0; i < 80; i++ ) assertFalse( v.getBoolean( i ) ); } public static void testFillFlip( final BitVector v ) { v.clear(); v.size( 100 ); v.fill( true ); for( int i = v.size(); i-- != 0; ) assertTrue( v.getBoolean( i ) ); v.fill( false ); for( int i = v.size(); i-- != 0; ) assertFalse( v.getBoolean( i ) ); v.flip(); for( int i = v.size(); i-- != 0; ) assertTrue( v.getBoolean( i ) ); v.clear(); v.size( 100 ); v.fill( 1 ); for( int i = v.size(); i-- != 0; ) assertTrue( v.getBoolean( i ) ); v.fill( 0 ); for( int i = v.size(); i-- != 0; ) assertFalse( v.getBoolean( i ) ); v.clear(); v.size( 100 ); v.fill( 5, 70, true ); for( int i = v.size(); i-- != 0; ) assertEquals( Integer.toString( i ), i >= 5 && i < 70, v.getBoolean( i ) ); v.fill( true ); v.fill( 5, 70, false ); for( int i = v.size(); i-- != 0; ) assertEquals( Integer.toString( i ), i < 5 || i >= 70, v.getBoolean( i ) ); v.clear(); v.size( 100 ); v.fill( 5, 70, 1 ); for( int i = v.size(); i-- != 0; ) assertEquals( Integer.toString( i ), i >= 5 && i < 70, v.getBoolean( i ) ); v.fill( true ); v.fill( 5, 70, 0 ); for( int i = v.size(); i-- != 0; ) assertEquals( i < 5 || i >= 70, v.getBoolean( i ) ); v.clear(); v.size( 100 ); v.flip( 5, 70 ); for( int i = v.size(); i-- != 0; ) assertEquals( Integer.toString( i ), i >= 5 && i < 70, v.getBoolean( i ) ); v.fill( true ); v.flip( 5, 70 ); for( int i = v.size(); i-- != 0; ) assertEquals( i < 5 || i >= 70, v.getBoolean( i ) ); } public static void testCopy( final BitVector v ) { v.clear(); v.size( 100 ); v.fill( 5, 80, true ); BitVector w = v.copy( 0, 85 ); assertEquals( w, v.subVector( 0, 85 ).copy() ); for( int i = w.size(); i-- != 0; ) assertEquals( i >= 5 && i < 80, w.getBoolean( i ) ); w = v.copy( 5, 85 ); assertEquals( w, v.subVector( 5, 85 ).copy() ); for( int i = w.size(); i-- != 0; ) assertEquals( i < 75, w.getBoolean( i ) ); v.clear(); int[] bits = { 0,0,0,0,1,1,1,0,0,0,0,1,1,0,0 }; for( int i = 0; i < bits.length; i++ ) v.add( bits[ i ] ); LongArrayBitVector c = LongArrayBitVector.getInstance(); for( int i = 5; i < bits.length; i++ ) c.add( bits[ i ] ); assertEquals( c, v.copy( 5, 15 ) ); assertEquals( c, v.subVector( 5, 15 ).copy() ); v.clear(); bits = new int[] { 0,0,0,0,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,0,0,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,0,0,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,0,0,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,0,0,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,0,0,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,0,0,0,0,0,0,1,1,1,0,0,0,0,1,1,0,0 }; for( int i = 0; i < bits.length; i++ ) v.add( bits[ i ] ); c = LongArrayBitVector.getInstance(); for( int i = 5; i < bits.length - 2; i++ ) c.add( bits[ i ] ); assertEquals( c, v.copy( 5, bits.length - 2 ) ); assertEquals( v, v.copy() ); long[] words = new long[] { 0xDEADBEEFDEADF00DL, 0xDEADBEEFDEADF00DL, 0xDEADBEEFDEADF00DL, }; long[] copyWords16 = new long[] { 0xF00DDEADBEEFDEADL, 0xF00DDEADBEEFDEADL, 0xBEEFDEADL }; long[] copyWords32 = new long[] { 0xDEADF00DDEADBEEFL, 0xDEADF00DDEADBEEFL }; LongArrayBitVector.wrap( copyWords16, 64 * 2 + 32 ).equals( LongArrayBitVector.wrap( words, 64 * 3 ).copy( 16, 16 + 64 * 2 + 32 ) ); assertEquals( LongArrayBitVector.wrap( copyWords16, 64 * 2 + 32 ), LongArrayBitVector.wrap( words, 64 * 3 ).copy( 16, 16 + 64 * 2 + 32 ) ); copyWords16[ 2 ] &= 0xFFFF; assertEquals( LongArrayBitVector.wrap( copyWords16, 64 * 2 + 16 ), LongArrayBitVector.wrap( words, 64 * 3 ).copy( 16, 16 + 64 * 2 + 16 ) ); copyWords16[ 2 ] &= 0xFF; assertEquals( LongArrayBitVector.wrap( copyWords16, 64 * 2 + 8 ), LongArrayBitVector.wrap( words, 64 * 3 ).copy( 16, 16 + 64 * 2 + 8 ) ); copyWords16[ 2 ] &= 0x1F; assertEquals( LongArrayBitVector.wrap( copyWords16, 64 * 2 + 5 ), LongArrayBitVector.wrap( words, 64 * 3 ).copy( 16, 16 + 64 * 2 + 5 ) ); copyWords16[ 2 ] &= 0x1; assertEquals( LongArrayBitVector.wrap( copyWords16, 64 * 2 + 1 ), LongArrayBitVector.wrap( words, 64 * 3 ).copy( 16, 16 + 64 * 2 + 1 ) ); copyWords32[ 1 ] &= 0xFFFFFFFFFFFFL; assertEquals( LongArrayBitVector.wrap( copyWords32, 64 * 1 + 32 + 16 ), LongArrayBitVector.wrap( words, 64 * 3 ).copy( 32, 32 + 64 + 32 + 16 ) ); copyWords32[ 1 ] &= 0xFFFFFFFFFFL; assertEquals( LongArrayBitVector.wrap( copyWords32, 64 * 1 + 32 + 8 ), LongArrayBitVector.wrap( words, 64 * 3 ).copy( 32, 32 + 64 + 32 + 8 ) ); copyWords32[ 1 ] &= 0x1FFFFFFFFFL; assertEquals( LongArrayBitVector.wrap( copyWords32, 64 * 1 + 32 + 5 ), LongArrayBitVector.wrap( words, 64 * 3 ).copy( 32, 32 + 64 + 32 + 5 ) ); copyWords32[ 1 ] &= 0x1FFFFFFFFL; assertEquals( LongArrayBitVector.wrap( copyWords32, 64 * 1 + 32 + 1 ), LongArrayBitVector.wrap( words, 64 * 3 ).copy( 32, 32 + 64 + 32 + 1 ) ); } public static void its( BitVector b ) { for( int i = 0; i < 100; i++ ) b.add( i % 2 ); assertTrue( LongArrayBitVector.wrap( b.bits(), b.length() ).toString(), Arrays.equals( new long[] { 0xAAAAAAAAAAAAAAAAL, 0x0000000AAAAAAAAAL }, b.bits() ) ); } public static void testLongBigListView( BitVector b ) { LongBigList list = b.asLongBigList( 10 ); for( int i = 0; i < 100; i++ ) list.add( i ); for( int i = 0; i < 100; i++ ) assertEquals( i, list.getLong( i ) ); assertTrue( b.getBoolean( 10 ) ); assertTrue( b.getBoolean( 21 ) ); for( int i = 0; i < 100; i++ ) list.add( i ); for( int i = 0; i < 100; i++ ) { assertEquals( i, list.set( i, i + 1 ) ); for( int j = i + 1; j < 100; j++ ) assertEquals( "" + i , j, list.getLong( j ) ); } for( int i = 0; i < 100; i++ ) assertEquals( i + 1, list.getLong( i ) ); assertTrue( b.getBoolean( 0 ) ); assertTrue( b.getBoolean( 11 ) ); list.size( 100 ); int k = 0; LongListIterator i = list.listIterator(); while( i.hasNext() ) { assertEquals( k, i.nextIndex() ); assertEquals( ++k, i.nextLong() ); } while( i.hasPrevious() ) { assertEquals( k - 1, i.previousIndex() ); assertEquals( k--, i.previousLong() ); } } public static void testLongSetView( BitVector b ) { LongSortedSet s = b.asLongSet(); assertNull( s.comparator() ); for( int i = 0; i < 100; i++ ) s.add( i * 2 ); for( int i = 0; i < 100; i++ ) assertTrue( s.contains( i * 2 ) ); for( int i = 0; i < 100; i++ ) assertFalse( s.contains( i * 2 + 1 ) ); LongBidirectionalIterator iterator = s.iterator(); for( int i = 0; i < 100; i++ ) assertEquals( i * 2, iterator.nextLong() ); assertFalse( iterator.hasNext() ); for( int i = 100; i-- != 0; ) assertEquals( i * 2, iterator.previousLong() ); assertFalse( iterator.hasPrevious() ); assertEquals( 100, s.size() ); assertEquals( false, s.remove( 1 ) ); assertEquals( 100, s.size() ); assertEquals( true, s.remove( 0 ) ); assertEquals( 99, s.size() ); assertEquals( true, s.remove( 60 ) ); assertEquals( 98, s.size() ); assertEquals( false, s.remove( 1000 ) ); assertEquals( 98, s.size() ); assertEquals( false, s.add( 2 ) ); assertEquals( 98, s.size() ); assertEquals( 2, s.firstLong() ); assertEquals( 198, s.lastLong() ); assertEquals( 18, s.subSet( 3, 40 ).size()); assertEquals( 19, s.headSet( 40 ).size()); assertEquals( 5, s.tailSet( 190 ).size()); iterator = s.iterator(); assertEquals( 2, iterator.nextLong() ); iterator.remove(); assertFalse( s.contains( 2 ) ); s.clear(); assertNull( s.comparator() ); for( int i = 0; i < 100; i++ ) s.add( i ); for( int i = 0; i < 100; i++ ) assertTrue( s.contains( i ) ); iterator = s.iterator(); for( int i = 0; i < 100; i++ ) assertEquals( i, iterator.nextLong() ); assertFalse( iterator.hasNext() ); for( int i = 100; i-- != 0; ) assertEquals( i, iterator.previousLong() ); assertFalse( iterator.hasPrevious() ); } public static void testFirstLastPrefix( BitVector b ) { b.clear(); b.length( 60 ); assertEquals( -1, b.firstOne() ); assertEquals( -1, b.lastOne() ); b.flip(); assertEquals( -1, b.firstZero() ); assertEquals( -1, b.lastZero() ); b.flip(); b.set( 4, true ); assertEquals( 4, b.firstOne() ); assertEquals( 4, b.lastOne() ); b.flip(); assertEquals( 4, b.firstZero() ); assertEquals( 4, b.lastZero() ); b.flip(); b.set( 50, true ); assertEquals( 4, b.firstOne() ); assertEquals( 50, b.nextOne( 5 ) ); assertEquals( 50, b.lastOne() ); assertEquals( 4, b.previousOne( 50 ) ); b.flip(); assertEquals( 4, b.firstZero() ); assertEquals( 50, b.nextZero( 5 ) ); assertEquals( 50, b.lastZero() ); assertEquals( 4, b.previousZero( 50 ) ); b.flip(); b.set( 20, true ); assertEquals( 4, b.firstOne() ); assertEquals( 20, b.nextOne( 5 ) ); assertEquals( 50, b.nextOne( 21 ) ); assertEquals( 50, b.lastOne() ); assertEquals( 20, b.previousOne( 50 ) ); assertEquals( 4, b.previousOne( 20 ) ); b.flip(); assertEquals( 4, b.firstZero() ); assertEquals( 20, b.nextZero( 5 ) ); assertEquals( 50, b.nextZero( 21 ) ); assertEquals( 50, b.lastZero() ); assertEquals( 20, b.previousZero( 50 ) ); assertEquals( 4, b.previousZero( 20 ) ); b.flip(); b.length( 100 ); b.set( 90, true ); assertEquals( 4, b.firstOne() ); assertEquals( 90, b.lastOne() ); b.flip(); assertEquals( 4, b.firstZero() ); assertEquals( 90, b.lastZero() ); b.flip(); b.clear(); b.length( 100 ); assertEquals( -1, b.firstOne() ); assertEquals( -1, b.lastOne() ); b.flip(); assertEquals( -1, b.firstZero() ); assertEquals( -1, b.lastZero() ); b.flip(); b.set( 4, true ); assertEquals( 4, b.firstOne() ); assertEquals( 4, b.lastOne() ); b.flip(); assertEquals( 4, b.firstZero() ); assertEquals( 4, b.lastZero() ); b.flip(); b.set( 90, true ); assertEquals( 4, b.firstOne() ); assertEquals( 90, b.lastOne() ); b.flip(); assertEquals( 4, b.firstZero() ); assertEquals( 90, b.lastZero() ); b.flip(); b.length( 60 ); BitVector c = b.copy(); c.length( 40 ); assertEquals( c.length(), b.longestCommonPrefixLength( c ) ); c.flip( 20 ); assertEquals( 20, b.longestCommonPrefixLength( c ) ); c.flip( 0 ); assertEquals( 0, b.longestCommonPrefixLength( c ) ); b.length( 128 ).fill( false ); b.set( 127 ); c.length( 65 ).fill( false ); assertEquals( 65, b.longestCommonPrefixLength( c ) ); b.length( 100 ); c = b.copy(); c.length( 80 ); assertEquals( c.length(), b.longestCommonPrefixLength( c ) ); assertEquals( c.length(), b.longestCommonPrefixLength( BooleanListBitVector.wrap( c ) ) ); c.flip( 20 ); assertEquals( 20, b.longestCommonPrefixLength( c ) ); assertEquals( 20, b.longestCommonPrefixLength( BooleanListBitVector.wrap( c ) ) ); c.flip( 0 ); assertEquals( 0, b.longestCommonPrefixLength( c ) ); assertEquals( 0, b.longestCommonPrefixLength( BooleanListBitVector.wrap( c ) ) ); c.clear(); c.length( 2 ); c.set( 0, 0 ); assertFalse( c.getBoolean( 0 ) ); c.set( 0, 1 ); assertTrue( c.getBoolean( 0 ) ); c.set( 0, 2 ); assertTrue( c.getBoolean( 0 ) ); c.set( 0, 0 ); assertFalse( c.getBoolean( 0 ) ); c.add( 0, 0 ); assertFalse( c.getBoolean( 0 ) ); c.add( 0, 1 ); assertTrue( c.getBoolean( 0 ) ); } public static void testLogicOperators( BitVector b ) { b.clear(); b.length( 100 ); BitVector c = b.copy(); for( int i = 0; i < 50; i++ ) b.set( 2 * i ); for( int i = 0; i < 50; i++ ) c.set( 2 * i + 1 ); BitVector r; r = b.copy().and( c ); for( int i = 0; i < 100; i++ ) assertFalse( r.getBoolean( i ) ); r = b.copy().or( c ); for( int i = 0; i < 100; i++ ) assertTrue( r.getBoolean( i ) ); r = b.copy().xor( c ); for( int i = 0; i < 100; i++ ) assertTrue( r.getBoolean( i ) ); r.xor( r ); for( int i = 0; i < 100; i++ ) assertFalse( r.getBoolean( i ) ); r = b.copy().and( BooleanListBitVector.wrap( c ) ); for( int i = 0; i < 100; i++ ) assertFalse( r.getBoolean( i ) ); r = b.copy().or( BooleanListBitVector.wrap( c ) ); for( int i = 0; i < 100; i++ ) assertTrue( r.getBoolean( i ) ); r = b.copy().xor( BooleanListBitVector.wrap( c ) ); for( int i = 0; i < 100; i++ ) assertTrue( r.getBoolean( i ) ); r.xor( BooleanListBitVector.wrap( r ) ); for( int i = 0; i < 100; i++ ) assertFalse( r.getBoolean( i ) ); } public static void testCount( BitVector b ) { b.clear(); b.length( 100 ); for( int i = 0; i < 50; i++ ) b.set( i * 2 ); assertEquals( 50, b.count() ); } public static void testSerialisation( BitVector b ) throws IOException, ClassNotFoundException { final File file = File.createTempFile( BitVectorTestCase.class.getSimpleName(), "test" ); file.deleteOnExit(); b.clear(); BinIO.storeObject( b, file ); assertEquals( b, BinIO.loadObject( file ) ); b.length( 1000 ); BinIO.storeObject( b, file ); assertEquals( b, BinIO.loadObject( file ) ); b.fill( true ); BinIO.storeObject( b, file ); assertEquals( b, BinIO.loadObject( file ) ); } public static void testReplace( BitVector b ) { Random r = new Random( 1 ); LongArrayBitVector bv = LongArrayBitVector.getInstance( 200 ); for( int i = 0; i < 100; i++ ) bv.add( r.nextBoolean() ); assertEquals( b.replace( bv ), bv ); bv = LongArrayBitVector.getInstance( 256 ); for( int i = 0; i < 256; i++ ) bv.add( r.nextBoolean() ); assertEquals( b.replace( bv ), bv ); bv = LongArrayBitVector.getInstance( 10 ); for( int i = 0; i < 10; i++ ) bv.add( r.nextBoolean() ); assertEquals( b.replace( bv ), bv ); } public static void testAppend( BitVector b ) { b.clear(); LongArrayBitVector v = LongArrayBitVector.ofLength( 200 ); for( int i = 0; i < 60; i++ ) v.set( i * 3 ); b.append( v ); assertEquals( b, v ); b.clear(); b.add( true ); b.append( v ); LongArrayBitVector w = LongArrayBitVector.ofLength( 201 ); for( int i = 0; i < 60; i++ ) w.set( i * 3 + 1 ); w.set( 0 ); assertEquals( w, b ); } }