package otherSupport;

import binaryMatrixFile.AsciiMatrixFile;

/** Simple util for profiling code.
 *  Takes a set of marks marking the start of different operations by calls to mark()
 *  Start of the next batch are marked by calling nextSet(); 
 *  dump() then displays the difference between each mark averaged over all the sets.
 *  
 */ 
public class Profiler extends EmptyProfiler {
	private int timeMax,setMax;
	private int timeNo,setNo;
	private int timesUsed;
	
	public static EmptyProfiler global = new EmptyProfiler();
	
	private long t[][];
	String names[];
	String setNames[];
	
	/**
	 * @param setMax Max number of mark sets.
	 * @param timeMax Max number of marks.
	 */
	public Profiler(int setMax, int timeMax) {
		this.setMax = (setMax + 1);
		this.timeMax = (timeMax + 1);
		
		reset();
	}
	
	/** Delete all data and start again */
	public final void reset(){
		t = new long[setMax][timeMax];
		setNames = new String[timeMax];
		//System.gc();
		
		timesUsed = 0;
		setNo=0;
		timeNo=0;
	}
	
	/** Create a mark */
	public final void mark(String markDescription){		
		t[setNo][timeNo] = System.nanoTime();
		setNames[timeNo] = markDescription;
		timeNo++;
	}
	
	/** terminate the set after doing this many blank marks */
	public final void nextSet(int skipMarks){
		timeNo += skipMarks;
		nextSet();	
	}
	
	/** Start the next set of marks */
	public final void nextSet(){
		//if this was the first set, use the names given as the names, otherwise check these are the same as the first ones
		if(setNo == 0){
			names = setNames;
			timesUsed = timeNo;
		}else{
			if(timeNo != timesUsed)
				throw new RuntimeException("Set 0 had "+timesUsed+" time points whereas set"+setNo+" has "+timeNo);
			for(int j=0;j<timesUsed;j++)
				if(setNames[j].compareTo(names[j]) != 0)
					throw new RuntimeException("Name given for point "+j+" of set " + setNo + " is '"+setNames[j]+"', but the same point for set 0 was '" + names[j] + "'.");
		}
		timeNo = 0;
		setNo++;
		if(setNo >= setMax){
			System.err.println("WARNING: Profiler overran the number of sets you allowed for and reset.");
			dump();
			reset();
		}
		
	}
	
	/** Display the info */
	public final void dump(){
		double tAvg[] = new double[timesUsed];
		double tStdDev[] = new double[timesUsed];
		for(int i=0;i<setNo;i++)
			for(int j=0;j<(timesUsed-1);j++)
				tAvg[j] += ((double)(t[i][j+1] - t[i][j])) / ((double)setNo);
		
		for(int i=0;i<setNo;i++)
			for(int j=0;j<(timesUsed-1);j++){
					double d = ((double)(t[i][j+1] - t[i][j])) - tAvg[j];
					tStdDev[j] += d*d;
				}
		
		for(int j=0;j<(timesUsed-1);j++)
			tStdDev[j] = Math.sqrt(tStdDev[j] / setNo);
			
			
		
		System.out.println("Timings (over "+setNo+" sets):");		
		for(int j=0;j<(timesUsed-1);j++){
				System.out.println("   '" + names[j] + "' - '" + names[j+1] + "': "+toSI(tAvg[j]*1e-9)+ "s +/- "+toSI(tStdDev[j]*1e-9)+"s");
		}
	}
	
	public final String toSI(double val){
		if(val > 2e6)
			return (val/1e3) + " M";
		if(val > 2e3)
			return (val/1e3) + " k";
		else if(val > 2e0)
			return (val/1e0) + " ";
		else if(val > 2e-3)
			return (val/1e-3) + " m";
		else if(val > 2e-6)
			return (val/1e-6) + " u";
		else if(val > 2e-9)
			return (val/1e-9) + " n";
		else
			return (val/1e-12) + " p";
	}
	
	public final void toDisk(String fileName){
		String titles = "// ";
		for(int j=0;j<timesUsed;j++)
			titles += names[j] + "\t";
		
		double tdiff[][] = new double[setNo][timesUsed];
		for(int i=0;i<setNo;i++)
			for(int j=0;j<(timesUsed-1);j++){
					tdiff[i][j] = ((double)(t[i][j+1] - t[i][j]));					
				}
		
		AsciiMatrixFile.mustWrite(fileName, tdiff, titles, false);
	}
	
	public final int getSetsUsed(){ return setNo; }

}
