/*		 
 * Copyright (C) 2004-2014 Sebastiano Vigna 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */



#if #keyclass(Byte)

// HORRIBLE kluges to work around bug #6478546

private final static int MAX_IO_LENGTH = 1024 * 1024;

private static int read( final InputStream is, final byte a[], final int offset, final int length ) throws IOException { 
	if ( length == 0 ) return 0;

	int read = 0, result;
	do {
		result = is.read( a, offset + read, Math.min( length - read, MAX_IO_LENGTH ) );
		if ( result < 0 ) return read;
		read += result;
	} while( read < length );

	return read;
}

private static void write( final OutputStream outputStream, final byte a[], final int offset, final int length ) throws IOException { 
	int written = 0;
	while( written < length ) { 
		outputStream.write( a, offset + written, Math.min( length - written, MAX_IO_LENGTH ) );
		written += Math.min( length - written, MAX_IO_LENGTH );
	}
}

private static void write( final DataOutput dataOutput, final byte a[], final int offset, final int length ) throws IOException { 
	int written = 0;
	while( written < length ) { 
		dataOutput.write( a, offset + written, Math.min( length - written, MAX_IO_LENGTH ) );
		written += Math.min( length - written, MAX_IO_LENGTH );
	}
}

// Additional read/write methods to work around the DataInput/DataOutput schizophrenia.

/** Loads bytes from a given input stream, storing them in a given array fragment.
 *
 * <p>Note that this method is going to be significantly faster than {@link #loadBytes(DataInput,byte[],int,int)}
 * as it uses {@link InputStream}'s bulk-read methods.
 *
 * @param inputStream an input stream.
 * @param array an array which will be filled with data from <code>inputStream</code>.
 * @param offset the index of the first element of <code>array</code> to be filled.
 * @param length the number of elements of <code>array</code> to be filled.
 * @return the number of elements actually read from <code>inputStream</code> (it might be less than <code>length</code> if <code>inputStream</code> ends).
 */
public static int LOAD_KEYS( final InputStream inputStream, final KEY_TYPE[] array, final int offset, final int length ) throws IOException {
	return read( inputStream, array, offset, length );
}

/** Loads bytes from a given input stream, storing them in a given array.
 *
 * <p>Note that this method is going to be significantly faster than {@link #loadBytes(DataInput,byte[])}
 * as it uses {@link InputStream}'s bulk-read methods.
 *
 * @param inputStream an input stream.
 * @param array an array which will be filled with data from <code>inputStream</code>.
 * @return the number of elements actually read from <code>inputStream</code> (it might be less than the array length if <code>inputStream</code> ends).
 */
public static int LOAD_KEYS( final InputStream inputStream, final KEY_TYPE[] array ) throws IOException {
	return read( inputStream, array, 0, array.length );
}

/** Stores an array fragment to a given output stream.
 *
 * <p>Note that this method is going to be significantly faster than {@link #storeBytes(byte[],int,int,DataOutput)}
 * as it uses {@link OutputStream}'s bulk-read methods.
 *
 * @param array an array whose elements will be written to <code>outputStream</code>.
 * @param offset the index of the first element of <code>array</code> to be written.
 * @param length the number of elements of <code>array</code> to be written.
 * @param outputStream an output stream.
 */
public static void STORE_KEYS( final KEY_TYPE array[], final int offset, final int length, final OutputStream outputStream ) throws IOException {
	write( outputStream, array, offset, length );
}

/** Stores an array to a given output stream.
 *
 * <p>Note that this method is going to be significantly faster than {@link #storeBytes(byte[],DataOutput)}
 * as it uses {@link OutputStream}'s bulk-read methods.
 *
 * @param array an array whose elements will be written to <code>outputStream</code>.
 * @param outputStream an output stream.
 */
public static void STORE_KEYS( final KEY_TYPE array[], final OutputStream outputStream ) throws IOException {
	write( outputStream, array, 0, array.length );
}


private static long read( final InputStream is, final byte a[][], final long offset, final long length ) throws IOException { 
	if ( length == 0 ) return 0;

	long read = 0;
	int segment = segment( offset );
	int displacement = displacement( offset );
	int result;
	do {
		result = is.read( a[ segment ], displacement, (int)Math.min( a[ segment ].length - displacement, Math.min( length - read, MAX_IO_LENGTH ) ) );
		if ( result < 0 ) return read;
		read += result;
		displacement += result;
		if ( displacement == a[ segment ].length ) {
			segment++;
			displacement = 0;
		}
	} while( read < length );

	return read;
}

private static void write( final OutputStream outputStream, final byte a[][], final long offset, final long length ) throws IOException { 
	if ( length == 0 ) return;
	long written = 0;
	int toWrite;
	int segment = segment( offset );
	int displacement = displacement( offset );
	do {
		toWrite = (int)Math.min( a[ segment ].length - displacement, Math.min( length - written, MAX_IO_LENGTH ) );
		outputStream.write( a[ segment ], displacement, toWrite );
		written += toWrite;
		displacement += toWrite;
		if ( displacement == a[ segment ].length ) {
			segment++;
			displacement = 0;
		}
	} while( written < length );
}

private static void write( final DataOutput dataOutput, final byte a[][], final long offset, final long length ) throws IOException { 
	if ( length == 0 ) return;
	long written = 0;
	int toWrite;
	int segment = segment( offset );
	int displacement = displacement( offset );
	do {
		toWrite = (int)Math.min( a[ segment ].length - displacement, Math.min( length - written, MAX_IO_LENGTH ) );
		dataOutput.write( a[ segment ], displacement, toWrite );
		written += toWrite;
		displacement += toWrite;
		if ( displacement == a[ segment ].length ) {
			segment++;
			displacement = 0;
		}
	} while( written < length );
}

// Additional read/write methods to work around the DataInput/DataOutput schizophrenia.


/** Loads bytes from a given input stream, storing them in a given big-array fragment.
 *
 * <p>Note that this method is going to be significantly faster than {@link #loadBytes(DataInput,byte[][],long,long)}
 * as it uses {@link InputStream}'s bulk-read methods.
 *
 * @param inputStream an input stream.
 * @param array a big array which will be filled with data from <code>inputStream</code>.
 * @param offset the index of the first element of <code>array</code> to be filled.
 * @param length the number of elements of <code>array</code> to be filled.
 * @return the number of elements actually read from <code>inputStream</code> (it might be less than <code>length</code> if <code>inputStream</code> ends).
 */
public static long LOAD_KEYS( final InputStream inputStream, final KEY_TYPE[][] array, final long offset, final long length ) throws IOException {
	return read( inputStream, array, offset, length );
}

/** Loads bytes from a given input stream, storing them in a given big array.
 *
 * <p>Note that this method is going to be significantly faster than {@link #loadBytes(DataInput,byte[][])}
 * as it uses {@link InputStream}'s bulk-read methods.
 *
 * @param inputStream an input stream.
 * @param array a big array which will be filled with data from <code>inputStream</code>.
 * @return the number of elements actually read from <code>inputStream</code> (it might be less than the array length if <code>inputStream</code> ends).
 */
public static long LOAD_KEYS( final InputStream inputStream, final KEY_TYPE[][] array ) throws IOException {
	return read( inputStream, array, 0, BIG_ARRAYS.length( array ) );
}

/** Stores a big-array fragment to a given output stream.
 *
 * <p>Note that this method is going to be significantly faster than {@link #storeBytes(byte[][],long,long,DataOutput)}
 * as it uses {@link OutputStream}'s bulk-read methods.
 *
 * @param array a big array whose elements will be written to <code>outputStream</code>.
 * @param offset the index of the first element of <code>array</code> to be written.
 * @param length the number of elements of <code>array</code> to be written.
 * @param outputStream an output stream.
 */
public static void STORE_KEYS( final KEY_TYPE array[][], final long offset, final long length, final OutputStream outputStream ) throws IOException {
	write( outputStream, array, offset, length );
}

/** Stores a big array to a given output stream.
 *
 * <p>Note that this method is going to be significantly faster than {@link #storeBytes(byte[][],DataOutput)}
 * as it uses {@link OutputStream}'s bulk-read methods.
 *
 * @param array a big array whose elements will be written to <code>outputStream</code>.
 * @param outputStream an output stream.
 */
public static void STORE_KEYS( final KEY_TYPE array[][], final OutputStream outputStream ) throws IOException {
	write( outputStream, array, 0, BIG_ARRAYS.length( array ) );
}

#endif


/** Loads elements from a given data input, storing them in a given array fragment.
 *
 * @param dataInput a data input.
 * @param array an array which will be filled with data from <code>dataInput</code>.
 * @param offset the index of the first element of <code>array</code> to be filled.
 * @param length the number of elements of <code>array</code> to be filled.
 * @return the number of elements actually read from <code>dataInput</code> (it might be less than <code>length</code> if <code>dataInput</code> ends).
 */
public static int LOAD_KEYS( final DataInput dataInput, final KEY_TYPE[] array, final int offset, final int length ) throws IOException {
	PACKAGE.ARRAYS.ensureOffsetLength( array, offset, length );
	int i = 0;
	try {
		for( i = 0; i < length; i++ ) array[ i + offset ] = dataInput.READ_KEY();
	}
	catch( EOFException itsOk ) {}
	return i;
}

/** Loads elements from a given data input, storing them in a given array.
 *
 * @param dataInput a data input.
 * @param array an array which will be filled with data from <code>dataInput</code>.
 * @return the number of elements actually read from <code>dataInput</code> (it might be less than the array length if <code>dataInput</code> ends).
 */
public static int LOAD_KEYS( final DataInput dataInput, final KEY_TYPE[] array ) throws IOException {
	int i = 0;
	try {
		final int length = array.length;
		for( i = 0; i < length; i++ ) array[ i ] = dataInput.READ_KEY();
	}
	catch( EOFException itsOk ) {}
	return i;
}

/** Loads elements from a file given by a {@link File} object, storing them in a given array fragment.
 *
 * @param file a file.
 * @param array an array which will be filled with data from the specified file.
 * @param offset the index of the first element of <code>array</code> to be filled.
 * @param length the number of elements of <code>array</code> to be filled.
 * @return the number of elements actually read from the given file (it might be less than <code>length</code> if the file is too short).
 */
public static int LOAD_KEYS( final File file, final KEY_TYPE[] array, final int offset, final int length ) throws IOException {
	PACKAGE.ARRAYS.ensureOffsetLength( array, offset, length );

	final FileInputStream fis = new FileInputStream( file );
#if #keyclass(Byte)
	final int result = read( fis, array, offset, length );
	fis.close();
	return result;
#else
	final DataInputStream dis = new DataInputStream( new FastBufferedInputStream( fis ) );

	int i = 0;
	try {
		for( i = 0; i < length; i++ ) array[ i + offset ] = dis.READ_KEY();
	}
	catch( EOFException itsOk ) {}

	dis.close();
	return i;
#endif
}

/** Loads elements from a file given by a pathname, storing them in a given array fragment.
 *
 * @param filename a filename.
 * @param array an array which will be filled with data from the specified file.
 * @param offset the index of the first element of <code>array</code> to be filled.
 * @param length the number of elements of <code>array</code> to be filled.
 * @return the number of elements actually read from the given file (it might be less than <code>length</code> if the file is too short).
 */
public static int LOAD_KEYS( final CharSequence filename, final KEY_TYPE[] array, final int offset, final int length ) throws IOException {
	return LOAD_KEYS( new File( filename.toString() ), array, offset, length );
}

/** Loads elements from a file given by a {@link File} object, storing them in a given array.
 *
 * @param file a file.
 * @param array an array which will be filled with data from the specified file.
 * @return the number of elements actually read from the given file (it might be less than the array length if the file is too short).
 */
public static int LOAD_KEYS( final File file, final KEY_TYPE[] array ) throws IOException {
	final FileInputStream fis = new FileInputStream( file );
#if #keyclass(Byte)
	final int result = read( fis, array, 0, array.length );
	fis.close();
	return result;
#else
	final DataInputStream dis = new DataInputStream( new FastBufferedInputStream( fis ) );

	int i = 0;
	try {
		final int length = array.length;
		for( i = 0; i < length; i++ ) array[ i ] = dis.READ_KEY();
	}
	catch( EOFException itsOk ) {}

	dis.close();

	return i;
#endif
}

/** Loads elements from a file given by a pathname, storing them in a given array.
 *
 * @param filename a filename.
 * @param array an array which will be filled with data from the specified file.
 * @return the number of elements actually read from the given file (it might be less than the array length if the file is too short).
 */
public static int LOAD_KEYS( final CharSequence filename, final KEY_TYPE[] array ) throws IOException {
	return LOAD_KEYS( new File( filename.toString() ), array );
}

/** Loads elements from a file given by a {@link File} object, storing them in a new array.
 *
 * <P>Note that the length of the returned array will be computed 
 * dividing the specified file size by the number of bytes used to
 * represent each element.
 *
 * @param file a file.
 * @return an array filled with the content of the specified file.
 */
public static KEY_TYPE[] LOAD_KEYS( final File file ) throws IOException {
	final FileInputStream fis = new FileInputStream( file );

#if #keyclass(Boolean)
	final long length = fis.getChannel().size();
#else
	final long length = fis.getChannel().size() / ( KEY_CLASS.SIZE / 8 );
#endif

	if ( length > Integer.MAX_VALUE ) {
		fis.close();
		throw new IllegalArgumentException( "File too long: " + fis.getChannel().size()+ " bytes (" + length + " elements)" ); 
	}

	final KEY_TYPE[] array = new KEY_TYPE[ (int)length ];

#if #keyclass(Byte)
	if ( read( fis, array, 0, (int)length ) < length ) throw new EOFException();
	fis.close();
#else
	final DataInputStream dis = new DataInputStream( new FastBufferedInputStream( fis ) );
	for( int i = 0; i < length; i++ ) array[ i ] = dis.READ_KEY();
	dis.close();
#endif
	return array;
}

/** Loads elements from a file given by a filename, storing them in a new array.
 *
 * <P>Note that the length of the returned array will be computed 
 * dividing the specified file size by the number of bytes used to
 * represent each element.
 *
 * @param filename a filename.
 * @return an array filled with the content of the specified file.
 */
public static KEY_TYPE[] LOAD_KEYS( final CharSequence filename ) throws IOException {
	return LOAD_KEYS( new File( filename.toString() ) );
}

/** Stores an array fragment to a given data output.
 *
 * @param array an array whose elements will be written to <code>dataOutput</code>.
 * @param offset the index of the first element of <code>array</code> to be written.
 * @param length the number of elements of <code>array</code> to be written.
 * @param dataOutput a data output.
 */
public static void STORE_KEYS( final KEY_TYPE array[], final int offset, final int length, final DataOutput dataOutput ) throws IOException {
	PACKAGE.ARRAYS.ensureOffsetLength( array, offset, length );
#if #keyclass(Byte)
	write( dataOutput, array, offset, length );
#else
	for( int i = 0; i < length; i++ ) dataOutput.WRITE_KEY( array[ offset + i ] );
#endif
}

/** Stores an array to a given data output.
 *
 * @param array an array whose elements will be written to <code>dataOutput</code>.
 * @param dataOutput a data output.
 */
public static void STORE_KEYS( final KEY_TYPE array[], final DataOutput dataOutput ) throws IOException {
#if #keyclass(Byte)
	write( dataOutput, array, 0, array.length );
#else
	final int length = array.length;
	for( int i = 0; i < length; i++ ) dataOutput.WRITE_KEY( array[ i ] );
#endif
}

/** Stores an array fragment to a file given by a {@link File} object.
 *
 * @param array an array whose elements will be written to <code>filename</code>.
 * @param offset the index of the first element of <code>array</code> to be written.
 * @param length the number of elements of <code>array</code> to be written.
 * @param file a file.
 */
public static void STORE_KEYS( final KEY_TYPE array[], final int offset, final int length, final File file ) throws IOException {
	PACKAGE.ARRAYS.ensureOffsetLength( array, offset, length );
#if #keyclass(Byte)
	final OutputStream os = new FastBufferedOutputStream( new FileOutputStream( file ) );
	write( os, array, offset, length );
	os.close();
#else
	final DataOutputStream dos = new DataOutputStream( new FastBufferedOutputStream( new FileOutputStream( file ) ) );
	for( int i = 0; i < length; i++ ) dos.WRITE_KEY( array[ offset + i ] );
	dos.close();
#endif
}

/** Stores an array fragment to a file given by a pathname.
 *
 * @param array an array whose elements will be written to <code>filename</code>.
 * @param offset the index of the first element of <code>array</code> to be written.
 * @param length the number of elements of <code>array</code> to be written.
 * @param filename a filename.
 */
public static void STORE_KEYS( final KEY_TYPE array[], final int offset, final int length, final CharSequence filename ) throws IOException {
	STORE_KEYS( array, offset, length, new File( filename.toString() ) );
}

/** Stores an array to a file given by a {@link File} object.
 *
 * @param array an array whose elements will be written to <code>filename</code>.
 * @param file a file.
 */
public static void STORE_KEYS( final KEY_TYPE array[], final File file ) throws IOException {
#if #keyclass(Byte)
	final OutputStream os = new FastBufferedOutputStream( new FileOutputStream( file ) );
	write( os, array, 0, array.length );
	os.close();
#else
	final int length = array.length;
	final DataOutputStream dos = new DataOutputStream( new FastBufferedOutputStream( new FileOutputStream( file ) ) );
	for( int i = 0; i < length; i++ ) dos.WRITE_KEY( array[ i ] );
	dos.close();
#endif
}

/** Stores an array to a file given by a pathname.
 *
 * @param array an array whose elements will be written to <code>filename</code>.
 * @param filename a filename.
 */
public static void STORE_KEYS( final KEY_TYPE array[], final CharSequence filename ) throws IOException {
	STORE_KEYS( array, new File( filename.toString() ) );
}





/** Loads elements from a given data input, storing them in a given big-array fragment.
 *
 * @param dataInput a data input.
 * @param array a big array which will be filled with data from <code>dataInput</code>.
 * @param offset the index of the first element of <code>bigArray</code> to be filled.
 * @param length the number of elements of <code>bigArray</code> to be filled.
 * @return the number of elements actually read from <code>dataInput</code> (it might be less than <code>length</code> if <code>dataInput</code> ends).
 */
public static long LOAD_KEYS( final DataInput dataInput, final KEY_TYPE[][] array, final long offset, final long length ) throws IOException {
	PACKAGE.BIG_ARRAYS.ensureOffsetLength( array, offset, length );
	long c = 0;
	try {
		for( int i = segment( offset ); i < segment( offset + length + SEGMENT_MASK ); i++ ) {
			final KEY_TYPE[] t = array[ i ];
			final int l = (int)Math.min( t.length, offset + length - start( i ) );
			for( int d = (int)Math.max( 0, offset - start( i ) ); d < l; d++ ) {
				t[ d ] = dataInput.READ_KEY();
				c++;
			}
		}
	}
	catch( EOFException itsOk ) {}
	return c;
}

/** Loads elements from a given data input, storing them in a given big array.
 *
 * @param dataInput a data input.
 * @param array a big array which will be filled with data from <code>dataInput</code>.
 * @return the number of elements actually read from <code>dataInput</code> (it might be less than the array length if <code>dataInput</code> ends).
 */
public static long LOAD_KEYS( final DataInput dataInput, final KEY_TYPE[][] array ) throws IOException {
	long c = 0;
	try {
		for( int i = 0; i < array.length; i++ ) {
			final KEY_TYPE[] t = array[ i ];
			final int l = t.length;
			for( int d = 0; d < l; d++ ) {
				t[ d ] = dataInput.READ_KEY();
				c++;
			}
		}
	}
	catch( EOFException itsOk ) {}
	return c;
}

/** Loads elements from a file given by a {@link File} object, storing them in a given big-array fragment.
 *
 * @param file a file.
 * @param array a big array which will be filled with data from the specified file.
 * @param offset the index of the first element of <code>array</code> to be filled.
 * @param length the number of elements of <code>array</code> to be filled.
 * @return the number of elements actually read from the given file (it might be less than <code>length</code> if the file is too short).
 */
public static long LOAD_KEYS( final File file, final KEY_TYPE[][] array, final long offset, final long length ) throws IOException {
	PACKAGE.BIG_ARRAYS.ensureOffsetLength( array, offset, length );

	final FileInputStream fis = new FileInputStream( file );
#if #keyclass(Byte)
	final long result = read( fis, array, offset, length );
	fis.close();
	return result;
#else
	final DataInputStream dis = new DataInputStream( new FastBufferedInputStream( fis ) );

	long c = 0;
	try {
		for( int i = segment( offset ); i < segment( offset + length + SEGMENT_MASK ); i++ ) {
			final KEY_TYPE[] t = array[ i ];
			final int l = (int)Math.min( t.length, offset + length - start( i ) );
			for( int d = (int)Math.max( 0, offset - start( i ) ); d < l; d++ ) {
				t[ d ] = dis.READ_KEY();
				c++;
			}
		}
	}
	catch( EOFException itsOk ) {}
	dis.close();
	return c;
#endif
}

/** Loads elements from a file given by a pathname, storing them in a given big-array fragment.
 *
 * @param filename a filename.
 * @param array an array which will be filled with data from the specified file.
 * @param offset the index of the first element of <code>array</code> to be filled.
 * @param length the number of elements of <code>array</code> to be filled.
 * @return the number of elements actually read from the given file (it might be less than <code>length</code> if the file is too short).
 */
public static long LOAD_KEYS( final CharSequence filename, final KEY_TYPE[][] array, final long offset, final long length ) throws IOException {
	return LOAD_KEYS( new File( filename.toString() ), array, offset, length );
}

/** Loads elements from a file given by a {@link File} object, storing them in a given big array.
 *
 * @param file a file.
 * @param array a big array which will be filled with data from the specified file.
 * @return the number of elements actually read from the given file (it might be less than the array length if the file is too short).
 */
public static long LOAD_KEYS( final File file, final KEY_TYPE[][] array ) throws IOException {
	final FileInputStream fis = new FileInputStream( file );
#if #keyclass(Byte)
	final long result = read( fis, array, 0, BIG_ARRAYS.length( array ) );
	fis.close();
	return result;
#else
	final DataInputStream dis = new DataInputStream( new FastBufferedInputStream( fis ) );

	long c = 0;
	try {
		for( int i = 0; i < array.length; i++ ) {
			final KEY_TYPE[] t = array[ i ];
			final int l = t.length;
			for( int d = 0; d < l; d++ ) {
				t[ d ] = dis.READ_KEY();
				c++;
			}
		}
	}
	catch( EOFException itsOk ) {}

	dis.close();

	return c;
#endif
}

/** Loads elements from a file given by a pathname, storing them in a given big array.
 *
 * @param filename a filename.
 * @param array a big array which will be filled with data from the specified file.
 * @return the number of elements actually read from the given file (it might be less than the array length if the file is too short).
 */
public static long LOAD_KEYS( final CharSequence filename, final KEY_TYPE[][] array ) throws IOException {
	return LOAD_KEYS( new File( filename.toString() ), array );
}

/** Loads elements from a file given by a {@link File} object, storing them in a new big array.
 *
 * <P>Note that the length of the returned big array will be computed 
 * dividing the specified file size by the number of bytes used to
 * represent each element.
 *
 * @param file a file.
 * @return a big array filled with the content of the specified file.
 */
public static KEY_TYPE[][] LOAD_KEYS_BIG( final File file ) throws IOException {
	final FileInputStream fis = new FileInputStream( file );

#if #keyclass(Boolean)
	final long length = fis.getChannel().size();
#else
	final long length = fis.getChannel().size() / ( KEY_CLASS.SIZE / 8 );
#endif

	final KEY_TYPE[][] array = BIG_ARRAYS.newBigArray( length );

#if #keyclass(Byte)
	if ( read( fis, array, 0, length ) < length ) throw new EOFException();
	fis.close();
#else
	final DataInputStream dis = new DataInputStream( new FastBufferedInputStream( fis ) );

	for( int i = 0; i < array.length; i++ ) {
		final KEY_TYPE[] t = array[ i ];
		final int l = t.length;
		for( int d = 0; d < l; d++ ) t[ d ] = dis.READ_KEY();
	}

	dis.close();
#endif
	return array;
}

/** Loads elements from a file given by a filename, storing them in a new big array.
 *
 * <P>Note that the length of the returned big array will be computed 
 * dividing the specified file size by the number of bytes used to
 * represent each element.
 *
 * @param filename a filename.
 * @return a big array filled with the content of the specified file.
 */
public static KEY_TYPE[][] LOAD_KEYS_BIG( final CharSequence filename ) throws IOException {
	return LOAD_KEYS_BIG( new File( filename.toString() ) );
}

/** Stores an array fragment to a given data output.
 *
 * @param array an array whose elements will be written to <code>dataOutput</code>.
 * @param offset the index of the first element of <code>array</code> to be written.
 * @param length the number of elements of <code>array</code> to be written.
 * @param dataOutput a data output.
 */
public static void STORE_KEYS( final KEY_TYPE array[][], final long offset, final long length, final DataOutput dataOutput ) throws IOException {
	PACKAGE.BIG_ARRAYS.ensureOffsetLength( array, offset, length );
#if #keyclass(Byte)
	write( dataOutput, array, offset, length );
#else
	for( int i = segment( offset ); i < segment( offset + length + SEGMENT_MASK ); i++ ) {
		final KEY_TYPE[] t = array[ i ];
		final int l = (int)Math.min( t.length, offset + length - start( i ) );
		for( int d = (int)Math.max( 0, offset - start( i ) ); d < l; d++ ) dataOutput.WRITE_KEY( t[ d ] );
	}
#endif
}

/** Stores a big array to a given data output.
 *
 * @param array a big array whose elements will be written to <code>dataOutput</code>.
 * @param dataOutput a data output.
 */
public static void STORE_KEYS( final KEY_TYPE array[][], final DataOutput dataOutput ) throws IOException {
#if #keyclass(Byte)
	write( dataOutput, array, 0, BIG_ARRAYS.length( array ) );
#else
	for( int i = 0; i < array.length; i++ ) {
		final KEY_TYPE[] t = array[ i ];
		final int l = t.length;
		for( int d = 0; d < l; d++ ) dataOutput.WRITE_KEY( t[ d ] );
	}
#endif
}

/** Stores a big-array fragment to a file given by a {@link File} object.
 *
 * @param array a big array whose elements will be written to <code>filename</code>.
 * @param offset the index of the first element of <code>array</code> to be written.
 * @param length the number of elements of <code>array</code> to be written.
 * @param file a file.
 */
public static void STORE_KEYS( final KEY_TYPE array[][], final long offset, final long length, final File file ) throws IOException {
	PACKAGE.BIG_ARRAYS.ensureOffsetLength( array, offset, length );
#if #keyclass(Byte)
	final OutputStream os = new FastBufferedOutputStream( new FileOutputStream( file ) );
	write( os, array, offset, length );
	os.close();
#else
	final DataOutputStream dos = new DataOutputStream( new FastBufferedOutputStream( new FileOutputStream( file ) ) );
	for( int i = segment( offset ); i < segment( offset + length + SEGMENT_MASK ); i++ ) {
		final KEY_TYPE[] t = array[ i ];
		final int l = (int)Math.min( t.length, offset + length - start( i ) );
		for( int d = (int)Math.max( 0, offset - start( i ) ); d < l; d++ ) dos.WRITE_KEY( t[ d ] );
	}
	dos.close();
	
#endif
}

/** Stores a big-array fragment to a file given by a pathname.
 *
 * @param array a big array whose elements will be written to <code>filename</code>.
 * @param offset the index of the first element of <code>array</code> to be written.
 * @param length the number of elements of <code>array</code> to be written.
 * @param filename a filename.
 */
public static void STORE_KEYS( final KEY_TYPE array[][], final long offset, final long length, final CharSequence filename ) throws IOException {
	STORE_KEYS( array, offset, length, new File( filename.toString() ) );
}

/** Stores an array to a file given by a {@link File} object.
 *
 * @param array an array whose elements will be written to <code>filename</code>.
 * @param file a file.
 */
public static void STORE_KEYS( final KEY_TYPE array[][], final File file ) throws IOException {
#if #keyclass(Byte)
	final OutputStream os = new FastBufferedOutputStream( new FileOutputStream( file ) );
	write( os, array, 0, BIG_ARRAYS.length( array ) );
	os.close();
#else
	final DataOutputStream dos = new DataOutputStream( new FastBufferedOutputStream( new FileOutputStream( file ) ) );
	for( int i = 0; i < array.length; i++ ) {
		final KEY_TYPE[] t = array[ i ];
		final int l = t.length;
		for( int d = 0; d < l; d++ ) dos.WRITE_KEY( t[ d ] );
	}
	dos.close();
#endif
}

/** Stores a big array to a file given by a pathname.
 *
 * @param array a big array whose elements will be written to <code>filename</code>.
 * @param filename a filename.
 */
public static void STORE_KEYS( final KEY_TYPE array[][], final CharSequence filename ) throws IOException {
	STORE_KEYS( array, new File( filename.toString() ) );
}




/** Stores the element returned by an iterator to a given data output.
 *
 * @param i an iterator whose output will be written to <code>dataOutput</code>.
 * @param dataOutput a filename.
 */
public static void STORE_KEYS( final KEY_ITERATOR i, final DataOutput dataOutput ) throws IOException {
	while( i.hasNext() ) dataOutput.WRITE_KEY( i.NEXT_KEY() );
}

/** Stores the element returned by an iterator to a file given by a {@link File} object.
 *
 * @param i an iterator whose output will be written to <code>filename</code>.
 * @param file a file.
 */
public static void STORE_KEYS( final KEY_ITERATOR i, final File file ) throws IOException {
	final DataOutputStream dos = new DataOutputStream( new FastBufferedOutputStream( new FileOutputStream( file ) ) );
	while( i.hasNext() ) dos.WRITE_KEY( i.NEXT_KEY() );
	dos.close();
}

/** Stores the element returned by an iterator to a file given by a pathname.
 *
 * @param i an iterator whose output will be written to <code>filename</code>.
 * @param filename a filename.
 */
public static void STORE_KEYS( final KEY_ITERATOR i, final CharSequence filename ) throws IOException {
	STORE_KEYS( i, new File( filename.toString() ) );
}

/** A wrapper that exhibits the content of a data input stream as a type-specific iterator. */

final private static class KEY_DATA_INPUT_WRAPPER extends KEY_ABSTRACT_ITERATOR {
	final private DataInput dataInput;
	private boolean toAdvance = true;
	private boolean endOfProcess = false;
	private KEY_TYPE next;

	public KEY_DATA_INPUT_WRAPPER( final DataInput dataInput ) {
		this.dataInput = dataInput;
	}

	public boolean hasNext() {
		if ( ! toAdvance ) return ! endOfProcess;

		toAdvance = false;

		try {
			next = dataInput.READ_KEY();
		}
		catch( EOFException eof ) {
			endOfProcess = true;
		}
		catch( IOException rethrow ) { throw new RuntimeException( rethrow ); }

		return ! endOfProcess;
	}

	public KEY_TYPE NEXT_KEY() {
		if (! hasNext()) throw new NoSuchElementException();
		toAdvance = true;
		return next;
	}
}



/** Wraps the given data input stream into an iterator.
 *
 * @param dataInput a data input.
 */
public static KEY_ITERATOR AS_KEY_ITERATOR( final DataInput dataInput ) {
	return new KEY_DATA_INPUT_WRAPPER( dataInput );
}

/** Wraps a file given by a {@link File} object into an iterator.
 *
 * @param file a file.
 */
public static KEY_ITERATOR AS_KEY_ITERATOR( final File file ) throws IOException {
	return new KEY_DATA_INPUT_WRAPPER( new DataInputStream( new FastBufferedInputStream( new FileInputStream( file ) ) ) );
}

/** Wraps a file given by a pathname into an iterator.
 *
 * @param filename a filename.
 */
public static KEY_ITERATOR AS_KEY_ITERATOR( final CharSequence filename ) throws IOException {
	return AS_KEY_ITERATOR( new File( filename.toString() ) );
}

/** Wraps a file given by a {@link File} object into an iterable object.
 *
 * @param file a file.
 */
public static KEY_ITERABLE AS_KEY_ITERABLE( final File file ) {
	return new KEY_ITERABLE() {
		public KEY_ITERATOR iterator() {
			try {
				return AS_KEY_ITERATOR( file );
			}
			catch( IOException e ) {
				throw new RuntimeException( e );
			}
		}
	};
}

/** Wraps a file given by a pathname into an iterable object.
 *
 * @param filename a filename.
 */
public static KEY_ITERABLE AS_KEY_ITERABLE( final CharSequence filename ) {
	return new KEY_ITERABLE() {
		public KEY_ITERATOR iterator() {
			try {
				return AS_KEY_ITERATOR( filename );
			}
			catch( IOException e ) {
				throw new RuntimeException( e );
			}
		}
	};
}

