package oneLinersForAlgoTests;

import java.io.*;
import java.util.Vector;

/** One-Line reading and writing of 2D matrix of values to/from an ascii file.
 * 
 * For reading and writing matricies into ascii files.
 * For really large things this is slow, use BinaryMatrxFile or BinaryMatrixWriter instead
 * 
 * TODO: Write this as an extrenal library or something more efficient than java string processing!
 */
public class algoAsciiMatrixFile{
	/**
	 * mustLoad - simply calls load() but catches any IOException thrown. It simple re-throws it as a RuntimeException
	 * Try not to use this for final programs but it saves a lot of error catching in the meantime.
	 * @param fileName The file to read
	 * @param swapDims True to load into arrays as [column][row], false for [row][column]
	 * @returns 2D array of the data in the file
	 * @throws IOException 
	 */	
	public static double [][] mustLoad(String fileName, boolean swapDims){
		try{
			return load(fileName, swapDims);
		}
		catch(IOException err){
			throw new RuntimeException("AsciiMatrixFile.mustLoad() caught the following error from load():\n" + err.toString());
		}		
	}
	
	/**
	 * load - Loads a file, attempts to interpret it as a fixed size 2D matrix of doubles with cols seperated by \t and rows by \n 
	 * @params fileName The file to read
	 * @params swapDims True to load into arrays as [column][row], flase for [row][column]
	 * @returns 2D array of the data in the file
	 * @throws IOException 
	 */
	public static double [][] load(String fileName, boolean swapDims) throws IOException{
		double data[][];
		double row[];
		Vector<double[][]> dataChunks = new Vector<double[][]>();
		int rowsPerChunk = 1024;
		int cols;
		int i,j;
		
		//open the file
		FileInputStream file = new FileInputStream(fileName);		
		BufferedReader reader = new BufferedReader( new InputStreamReader(file));

		//get 1 line of data, get the column count from it and use it to create the data object
		row = getLine(reader);
		if(row == null)throw new RuntimeException("There is no data in '"+fileName+"'.");
		cols = row.length;
		data = new double[rowsPerChunk][cols];
		
		i=0;
		// foreach row
		do{
			//check the number of columns is correct
			if(row.length != cols)
				throw new RuntimeException("Line " + (i+1) + " of file '" + fileName + "' has " + row.length + " columns but the 1st line had " + cols);
			
			//check we have room
			if(i >= rowsPerChunk){
				//if we've got the the end, add the data to the data chunks vector, allocate a new array and reset the counter
				dataChunks.add(data);
				data = new double[rowsPerChunk][cols];
				i = 0;
			}
			
			//copy the row into the data object
			for(j=0;j<cols;j++)
				data[i][j] = row[j];
			
			//move to the next row
			i++;
			row = getLine(reader);
			
		} //getLine returns null if it has no more lines to get
		while(row != null);
		
		//we're done with the file now
		file.close();
		
		int chunkCount = dataChunks.size();
		int rowsInLastChunk = i;
		
		//allocate the final array and define a spair one for copying chunks		
		double bigData[][];
		double chunk[][];
		if(swapDims)
			bigData = new double[cols][(chunkCount * rowsPerChunk) + rowsInLastChunk];			
		else
			bigData = new double[(chunkCount * rowsPerChunk) + rowsInLastChunk][cols];					
			
		//copy each chunk (may not have any) into bigData
		for(int c=0;c<chunkCount;c++){
			chunk = (double[][])dataChunks.get(c);
			for(i=0;i<rowsPerChunk;i++)
				for(j=0;j<cols;j++)
					if(swapDims)
						bigData[j][(c*rowsPerChunk)+i] = chunk[i][j];
					else
						bigData[(c*rowsPerChunk)+i][j] = chunk[i][j];
		}
		
		//copy in whatever's in the last chunk 
		for(i=0;i<rowsInLastChunk;i++){
			for(j=0;j<cols;j++)				
				if(swapDims)
					bigData[j][(chunkCount*rowsPerChunk)+i] = data[i][j];
				else
					bigData[(chunkCount*rowsPerChunk)+i][j] = data[i][j];
		}
		
		return bigData;
	}
	  
	/**
	 * getLine - reads a line from 'reader' and attempts to get an array of at least 1 double value
	 * the function also ignores any line starting with //
	 * 
	 *  @params reader - BufferedReader object to read from
	 *  @throws IOException
	 *  @returns The array of doubles
	 */
	private static double[] getLine(BufferedReader reader) throws IOException{
		String line,fields[];
		double vals[];
		int i,n;
		  
		do{
			line = reader.readLine();				
			if(line == null)break;
			line = line.trim();
		}
		while(line.length() == 0 || (line.charAt(0) == '/' && line.charAt(1) == '/'));
			  
		if(line == null)
			return null;
		
		line = line.replaceAll("\\s+", "\t");
			  
		fields = line.split("\t");
		n = fields.length;
		
		/*if(n==1){
			fields2 = line.split("\\s+");
			if(fields2.length > 1){
				fields = fields2;
				n = fields2.length;
			}
		}*/
		
		vals = new double[n];
			  
		for(i=0;i<n;i++){
			try{
				vals[i] = Double.parseDouble(fields[i]);
			}
			catch(NumberFormatException nfe){
				vals[i] = 0.0;
			}
		}
		
		
		return vals;
	}
	
	public static void mustWrite(String fileName, double data[]){ mustWrite(fileName,data,null,true); }
	public static void mustWrite(String fileName, double data[], String initialComments){ mustWrite(fileName,new double[][]{ data },initialComments,true); }
	public static void mustWrite(String fileName, double data[], boolean swapDims){ mustWrite(fileName,data,null,swapDims); }
	public static void mustWrite(String fileName, double data[], String initialComments, boolean swapDims){ mustWrite(fileName,new double[][]{ data },initialComments,swapDims); }
	
	public static void mustWrite(String fileName, double data[][]){ mustWrite(fileName,data,null,false); }
	public static void mustWrite(String fileName, double data[][], boolean swapDims){ mustWrite(fileName,data,null,swapDims); }
	public static void mustWrite(String fileName, double data[][], String initialComments){ mustWrite(fileName,data,initialComments,false); }
		
	public static void write(String fileName, double data[][]) throws IOException{ write(fileName,data,null); }
	
	public static void mustWrite(String fileName, double data[][], String initialComments, boolean swapDims){
		try{
		    algoOneLiners.makePath(fileName);
			write(fileName, data, initialComments, swapDims);
		}
		catch(IOException err){
 			throw new RuntimeException("AsciiMatrixFile.mustWrite() caught the following error from write():\n" + err.toString());
		}		
	}
	
	
	
	public static void write(String fileName, double data[][], String initialComments) throws IOException
	{ write(fileName,data,initialComments,false); }
	
	public static void write(String fileName, double data[][], String initialComments, boolean swapDims) throws IOException{
		
		FileOutputStream file = new FileOutputStream(fileName);
		PrintStream ps = new PrintStream(file);
		
		if(initialComments != null)
			ps.println(initialComments);
		
		int i,j;
		int ni = data.length;
		if(ni<1)throw new RuntimeException("Nothing to put into '"+fileName+"', data has 0 rows!");
		int nj = data[0].length;
		
		if(ni > 0 && nj > 0){ //if we have something to write (no dim is size 0)
			if(swapDims){
				for(j=0;j<nj;j++){			
					for(i=0;i<(ni-1);i++){
						ps.print(data[i][j] + "\t");
					}
					
					ps.println(data[i][j]);
				}			
			}
			else{
				for(i=0;i<ni;i++){
					for(j=0;j<(nj-1);j++){			
						ps.print(data[i][j] + "\t");
					}
						ps.println(data[i][j]);
				}
			}
		}
		file.close();
	}
	
	

	
}

