/*		 
 * 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. 
 */



/** Loads elements from a given fast buffered reader, storing them in a given array fragment.
 *
 * @param reader a buffered reader.
 * @param array an array which will be filled with data from <code>reader</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>reader</code> (it might be less than <code>length</code> if <code>reader</code> ends).
 */
public static int LOAD_KEYS( final BufferedReader reader, final KEY_TYPE[] array, final int offset, final int length ) throws IOException {
	PACKAGE.ARRAYS.ensureOffsetLength( array, offset, length );
	int i = 0;
	String s;
	try {
		for( i = 0; i < length; i++ ) 
			if ( ( s = reader.readLine() ) != null ) array[ i + offset ] = KEY_CLASS.PARSE_KEY( s.trim() );
			else break;
	}
	catch( EOFException itsOk ) {}
	return i;
}

/** Loads elements from a given buffered reader, storing them in a given array.
 *
 * @param reader a buffered reader.
 * @param array an array which will be filled with data from <code>reader</code>.
 * @return the number of elements actually read from <code>reader</code> (it might be less than the array length if <code>reader</code> ends).
 */
public static int LOAD_KEYS( final BufferedReader reader, final KEY_TYPE[] array ) throws IOException {
	return LOAD_KEYS( reader, array, 0, array.length );
}

/** 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 {
	final BufferedReader reader = new BufferedReader( new FileReader( file ) );
	final int result = LOAD_KEYS( reader, array, offset, length );
	reader.close();

	return result;
}

/** Loads elements from a file given by a filename, 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 {
	return LOAD_KEYS( file, array, 0, array.length );
}

/** Loads elements from a file given by a filename, 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( filename, array, 0, array.length );
}

/** Stores an array fragment to a given print stream.
 *
 * @param array an array whose elements will be written to <code>stream</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 stream a print stream.
 */
public static void STORE_KEYS( final KEY_TYPE array[], final int offset, final int length, final PrintStream stream ) {
	PACKAGE.ARRAYS.ensureOffsetLength( array, offset, length );
	for( int i = 0; i < length; i++ ) stream.println( array[ offset + i ] );
}

/** Stores an array to a given print stream.
 *
 * @param array an array whose elements will be written to <code>stream</code>.
 * @param stream a print stream.
 */
public static void STORE_KEYS( final KEY_TYPE array[], final PrintStream stream ) {
	STORE_KEYS( array, 0, array.length, stream );
}

/** 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 {
	final PrintStream stream = new PrintStream( new FastBufferedOutputStream( new FileOutputStream( file ) ) );
	STORE_KEYS( array, offset, length, stream );
	stream.close();
}

/** 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 {
	STORE_KEYS( array, 0, array.length, file );
}

/** 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, 0, array.length, filename );
}

/** Stores the element returned by an iterator to a given print stream.
 *
 * @param i an iterator whose output will be written to <code>stream</code>.
 * @param stream a print stream.
 */
public static void STORE_KEYS( final KEY_ITERATOR i, final PrintStream stream ) {
	while( i.hasNext() ) stream.println( 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 PrintStream stream = new PrintStream( new FastBufferedOutputStream( new FileOutputStream( file ) ) );
	STORE_KEYS( i, stream );
	stream.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() ) );
}


/** Loads elements from a given fast buffered reader, storing them in a given big-array fragment.
 *
 * @param reader a buffered reader.
 * @param array a big array which will be filled with data from <code>reader</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>reader</code> (it might be less than <code>length</code> if <code>reader</code> ends).
 */
public static long LOAD_KEYS( final BufferedReader reader, final KEY_TYPE[][] array, final long offset, final long length ) throws IOException {
	PACKAGE.BIG_ARRAYS.ensureOffsetLength( array, offset, length );
	long c = 0;
	String s;
	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++ ) {
				if ( ( s = reader.readLine() ) != null ) t[ d ] = KEY_CLASS.PARSE_KEY( s.trim() );
				else return c;
				c++;
			}
		}
	}
	catch( EOFException itsOk ) {}
	return c;
}

/** Loads elements from a given buffered reader, storing them in a given array.
 *
 * @param reader a buffered reader.
 * @param array a big array which will be filled with data from <code>reader</code>.
 * @return the number of elements actually read from <code>reader</code> (it might be less than the array length if <code>reader</code> ends).
 */
public static long LOAD_KEYS( final BufferedReader reader, final KEY_TYPE[][] array ) throws IOException {
	return LOAD_KEYS( reader, array, 0, PACKAGE.BIG_ARRAYS.length( array ) );
}

/** 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 {
	final BufferedReader reader = new BufferedReader( new FileReader( file ) );
	final long result = LOAD_KEYS( reader, array, offset, length );
	reader.close();

	return result;
}

/** Loads elements from a file given by a filename, storing them in a given big-array fragment.
 *
 * @param filename a filename.
 * @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 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 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 {
	return LOAD_KEYS( file, array, 0, PACKAGE.BIG_ARRAYS.length( array ) );
}

/** Loads elements from a file given by a filename, storing them in a given 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( filename, array, 0, PACKAGE.BIG_ARRAYS.length( array ) );
}

/** Stores a big-array fragment to a given print stream.
 *
 * @param array a big array whose elements will be written to <code>stream</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 stream a print stream.
 */
public static void STORE_KEYS( final KEY_TYPE array[][], final long offset, final long length, final PrintStream stream ) {
	PACKAGE.BIG_ARRAYS.ensureOffsetLength( array, offset, length );

	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++ ) stream.println( t[ d ] );
	}
}

/** Stores a big array to a given print stream.
 *
 * @param array a big array whose elements will be written to <code>stream</code>.
 * @param stream a print stream.
 */
public static void STORE_KEYS( final KEY_TYPE array[][], final PrintStream stream ) {
	STORE_KEYS( array, 0, PACKAGE.BIG_ARRAYS.length( array ), stream );
}

/** 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 {
	final PrintStream stream = new PrintStream( new FastBufferedOutputStream( new FileOutputStream( file ) ) );
	STORE_KEYS( array, offset, length, stream );
	stream.close();
}

/** 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 a big array to a file given by a {@link File} object.
 *
 * @param array a big 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 {
	STORE_KEYS( array, 0, PACKAGE.BIG_ARRAYS.length( array ), file );
}

/** 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, 0, PACKAGE.BIG_ARRAYS.length( array ), filename );
}


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

final private static class KEY_READER_WRAPPER extends KEY_ABSTRACT_ITERATOR {
	final private BufferedReader reader;
	private boolean toAdvance = true;
	private String s;
	private KEY_TYPE next;

	public KEY_READER_WRAPPER( final BufferedReader reader ) {
		this.reader = reader;
	}

	public boolean hasNext() {
		if ( ! toAdvance ) return s != null;

		toAdvance = false;

		try {
			s = reader.readLine();
		}
		catch( EOFException itsOk ) {}
		catch( IOException rethrow ) { throw new RuntimeException( rethrow ); }

		if ( s == null ) return false;

		next = KEY_CLASS.PARSE_KEY( s.trim() );
		return true;
	}

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



/** Wraps the given buffered reader into an iterator.
 *
 * @param reader a buffered reader.
 */
public static KEY_ITERATOR AS_KEY_ITERATOR( final BufferedReader reader ) {
	return new KEY_READER_WRAPPER( reader );
}

/** 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_READER_WRAPPER( new BufferedReader( new FileReader( 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 );
			}
		}
	};
}

