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.io.OfflineIterable;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;


/** A class providing static methods and objects that do useful things with bit vectors.
 * 
 * @see BitVector
 */

public class BitVectors {
	
	private BitVectors() {}	
	
	@Deprecated
	public static <T extends BitVector> TransformationStrategy<T> identity() {
		return TransformationStrategies.identity();
	}
	
    public static void ensureFromTo( final long bitVectorLength, final long from, final long to ) {
        if ( from < 0 ) throw new ArrayIndexOutOfBoundsException( "Start index (" + from + ") is negative" );
        if ( from > to ) throw new IllegalArgumentException( "Start index (" + from + ") is greater than end index (" + to + ")" );
        if ( to > bitVectorLength ) throw new ArrayIndexOutOfBoundsException( "End index (" + to + ") is greater than bit vector length (" + bitVectorLength + ")" );
    }
 	
	/** An immutable, singleton empty bit vector. */ 
	public final static BitVector EMPTY_VECTOR = new AbstractBitVector() {
		public final long length() { return 0; }
		public final BitVector copy( final long from, final long to ) { 
			ensureFromTo( 0, from, to );
			return EMPTY_VECTOR; 
		}
		public final boolean getBoolean( final long index ) { throw new IndexOutOfBoundsException(); }
		public BitVector copy() {
			return this;
		}
		public Object readResolve() {
			return EMPTY_VECTOR;
		}
	};

	/** An immutable bit vector of length one containing a zero. */ 
	public final static BitVector ZERO = new AbstractBitVector() {
		public final long length() { return 1; }
		public final BitVector copy( final long from, final long to ) { 
			ensureFromTo( 1, from, to );
			return from == to ? EMPTY_VECTOR : this; 
		}
		public final boolean getBoolean( final long index ) { if ( index > 0 ) throw new IndexOutOfBoundsException(); else return false; } 
		public BitVector copy() {
			return this;
		}
		public Object readResolve() {
			return ZERO;
		}
	};

	/** An immutable bit vector of length one containing a one. */ 
	public final static BitVector ONE = new AbstractBitVector() {
		public final long length() { return 1; }
		public final BitVector copy( final long from, final long to ) { 
			ensureFromTo( 1, from, to );
			return from == to ? EMPTY_VECTOR : this; 
		}
		public final boolean getBoolean( final long index ) { if ( index > 0 ) throw new IndexOutOfBoundsException(); else return true; } 
		public BitVector copy() {
			return this;
		}
		public Object readResolve() {
			return ONE;
		}
	};
	
	/** Writes quickly a bit vector to a {@link DataOutputStream}. 
	 * 
	 * <p>This method writes a bit vector in a simple format: first, a long representing the length.
	 * Then, as many longs as necessary to write the bits in the bit vectors (i.e.,
	 * {@link LongArrayBitVector#numWords(long)} of the bit vector length), obtained via {@link BitVector#getLong(long, long)}.
	 * 
	 * <p>The main purpose of this function is to support {@link OfflineIterable} (see {@link #OFFLINE_SERIALIZER}).
	 * 
	 * @param v a bit vector.
	 * @param dos a data output stream.
	 */
	
	public static void writeFast( final BitVector v, final DataOutputStream dos ) throws IOException {
		final long length = v.length();
		final long l = length - length % Long.SIZE;
		dos.writeLong( length );
		long i;
		for( i = 0; i < l; i += Long.SIZE ) dos.writeLong( v.getLong( i, i + Long.SIZE ) );
		if ( i < length ) dos.writeLong( v.getLong( i, length ) );
	}
	
	/** Reads quickly a bit vector from a {@link DataInputStream}.
	 * 
	 * <p>This method is the dual of {@link #writeFast(BitVector, DataOutputStream)}. If you
	 * need to avoid creating a bit vector at each call, please have a look at {@link #readFast(DataInputStream, LongArrayBitVector)}.
	 * 
	 * @param dis a data input stream.
	 * @return the next bit vector in the stream, as saved by {@link  #writeFast(BitVector, DataOutputStream)}.
	 * @see #writeFast(BitVector, DataOutputStream)
	 * @see #readFast(DataInputStream, LongArrayBitVector)
	 */
	
	public static LongArrayBitVector readFast( DataInputStream dis ) throws IOException {
		final long length = dis.readLong();
		final long bits[] = new long[ LongArrayBitVector.numWords( length ) ];
		final int l = bits.length;
		for( int i = 0; i < l; i++ ) bits[ i ] = dis.readLong();
		return LongArrayBitVector.wrap( bits, length );
	}
	
	/** Reads quickly a bit vector from a {@link DataInputStream}.
	 * 
	 * <p>This method is similar in purpose to {@link #readFast(DataInputStream)}, but it allows reuse of the bit vector.
	 * 
	 * @param dis a data input stream.
	 * @param bv a long-array bit vector.
	 * @return <code>bv</code>, filled with the next bit vector in the stream, as saved by {@link  #writeFast(BitVector, DataOutputStream)}.
	 * @see #writeFast(BitVector, DataOutputStream)
	 * @see #readFast(DataInputStream)
	 */
	public static LongArrayBitVector readFast( DataInputStream dis, final LongArrayBitVector bv ) throws IOException {
		final long length = dis.readLong();
		bv.ensureCapacity( length );
		final int l = LongArrayBitVector.numWords( length );
		for( int i = 0; i < l; i++ ) bv.bits[ i ] = dis.readLong();
		bv.length( length );
		return bv;
	}

	private static class BitVectorOfflineSerializer implements OfflineIterable.Serializer<BitVector,LongArrayBitVector> {
		public void write( BitVector bv, DataOutputStream dos ) throws IOException {
			writeFast( bv, dos );
		}
		
		public void read( DataInputStream dis, LongArrayBitVector bv ) throws IOException {
			readFast( dis, bv );
		}
	}

	/** A serializer for {@link LongArrayBitVector} instances that can be used with {@link it.unimi.dsi.io.OfflineIterable}. It can serialize
	 * any implementation of {@link BitVector}, and requires at construction time an instance of {@link LongArrayBitVector} that
	 * will be used to return deserialized elements. */
	public static BitVectorOfflineSerializer OFFLINE_SERIALIZER = new BitVectorOfflineSerializer();
}