FlowStatistics.java [src/java/cfa] Revision: 68930ad69d02f306db82b2beaffa2c6da8906811  Date: Mon Jun 09 10:32:03 MDT 2014
package cfa;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;

/**
* Last Updated: 16-October-2012
* @author Tyler Wible
* @since 29-June-2011
*/
public class FlowStatistics {
	/**
	 * Finds the m-day statistical average low value from the data set
	 * @param timeData  the data set
	 * @param m  the m-day average value desired
	 * @param ctr  a counter of how large variables need to be
	 * @return  the m-day average statistical low value of the data set
	 */
	public double minMflow(ArrayList<String> timeData, int m, int ctr){
		double minMflow = 999999999;
		
		
		//Pull out data from ArrayList
		String currentLine = " $$ ";
		String[] date = new String[ctr]; 
		String[] flow = new String[ctr];
		double[] Flows = new double[(int) Math.ceil(ctr/m)];
		ctr=0;
		
		for(int i=0; i<Flows.length; i++){
			Flows[i] = 999999999;
		}
		Iterator<String> iterate1 = timeData.iterator( );
		while(iterate1.hasNext()){
			currentLine = (String) iterate1.next();
			date[ctr] = currentLine.substring(0,currentLine.indexOf("$$"));
			flow[ctr] = currentLine.substring(currentLine.indexOf("$$")+2);
			ctr++;
		}
		
		//Find 'm' consecutive days with the same flow value
		double[][] dates = new double[m][3];
		int consecutiveFlowctr=0;
		double consecutiveAverage = 0;
		double yearUp4=0,yearDown4=0,yearUp100=0,yearDown100=0,yearUp400,yearDown400;
		boolean leapyear = false;
		ctr=0;
		for(int i=0; i<date.length; i++){
			if((date.length - i) >= m){
				for(int j=0; j<m; j++){
					dates[j][0] = Double.parseDouble(date[i+j].substring(0,4));//year
					dates[j][1] = Double.parseDouble(date[i+j].substring(5,7));//month
					dates[j][2] = Double.parseDouble(date[i+j].substring(8));//day
					//Compare dates to find consecutive days, if days aren't consecutive break the loop
					if(j==0){
						consecutiveAverage = consecutiveAverage + Double.parseDouble(flow[i+j]);
						consecutiveFlowctr++;
					}else if(j!=0 && (dates[j-1][0] == dates[j][0])){//compare year
						if(dates[j-1][1] == dates[j][1]){//check if same month
							if((dates[j-1][2] + 1) == dates[j][2]){//Check if is subsequent day
								consecutiveAverage = consecutiveAverage + Double.parseDouble(flow[i+j]);
								consecutiveFlowctr++;
							}else{
								break;
							}
						}else if((dates[j-1][1] + 1) == dates[j][1]){//check if last day of month and first day of next
							if(dates[j-1][1]==1 ||dates[j-1][1]==3 || dates[j-1][1]==5 || dates[j-1][1]==7 || dates[j-1][1]==8 || dates[j-1][1]==10 || dates[j-1][1]==12){
								if((dates[j-1][2]==31) && (dates[j][2]==1)){//Check if is subsequent day
									consecutiveAverage = consecutiveAverage + Double.parseDouble(flow[i+j]);
									consecutiveFlowctr++;
								}else{
									break;
								}
							}else if(dates[j-1][1]==4 || dates[j-1][1]==6 || dates[j-1][1]==9 || dates[j-1][1]==11){
								if((dates[j-1][2]==30) && (dates[j][2]==1)){//Check if is subsequent day
									consecutiveAverage = consecutiveAverage + Double.parseDouble(flow[i+j]);
									consecutiveFlowctr++;
								}else{
									break;
								}
							}else if(dates[j-1][1]==2){//If February check for leap years
								leapyear = false;
								yearUp4 = Math.ceil(dates[j-1][0]/4);
								yearDown4 = Math.floor(dates[j-1][0]/4);
								yearUp100 = Math.ceil(dates[j-1][0]/100);
								yearDown100 = Math.floor(dates[j-1][0]/100);
								yearUp400 = Math.ceil(dates[j-1][0]/400);
								yearDown400 = Math.floor(dates[j-1][0]/400);
								if(yearUp400 == yearDown400){
									leapyear = true;
								}else if(yearUp100 == yearDown100){
									leapyear = false;
								}else if(yearUp4 == yearDown4){
									leapyear = true;
								}
								if(!leapyear  && (dates[j-1][2]==28) && (dates[j][2]==1)){//Check if is subsequent day
									consecutiveAverage = consecutiveAverage + Double.parseDouble(flow[i+j]);
									consecutiveFlowctr++;
								}else if(leapyear && (dates[j-1][2]==29) && (dates[j][2]==1)){//Check if is subsequent day
									consecutiveAverage = consecutiveAverage + Double.parseDouble(flow[i+j]);
									consecutiveFlowctr++;
								}else{
									break;
								}
							}else{
								break;
							}
						}else{
							break;
						}
					}else if(j!=0 && (dates[j-1][0] + 1) == dates[j][0]){//check if last day of the year and first day of next
						if(dates[j-1][1] == 31 && dates[j][1] == 1){//compare months
							if((dates[j-1][2]==31) && (dates[j][2]==1)){//Check if is subsequent day
								consecutiveAverage = consecutiveAverage + Double.parseDouble(flow[i+j]);
								consecutiveFlowctr++;
							}else{
								break;
							}
						}else{
							break;
						}
					}else{
						break;
					}
				}
				//If there are seven consecutive days find the average of the flows
				if(consecutiveFlowctr == m ){
					consecutiveAverage = consecutiveAverage/m;
					Flows[ctr] = consecutiveAverage;
					ctr++;
					i = i + m - 1;
				}
				consecutiveAverage = 0;
				consecutiveFlowctr = 0;
			}
		}
		//Sort all the "m" day flow to find the "time"'s minimum
		if(Flows.length > 0){
			Arrays.sort(Flows);
			if(Flows.length >= 2 && Flows[0] == 0 && Flows[1] != 0){
				minMflow = Flows[1];
			}else if(Flows.length >= 3 && Flows[0] == 0 && Flows[1] == 0 && Flows[2] != 0){
				minMflow = Flows[1];
			}else{
			minMflow = Flows[0];
			}
		}
		if(minMflow == 0){
			minMflow = 10;
		}
		return minMflow;
	}
	/**
	 * Finds the m-day statistical average high value from the data set
	 * @param timeData  the data set
	 * @param m  the m-day average value desired
	 * @param ctr  a counter of how large variables need to be
	 * @return  the m-day average statistical high value of the data set
	 */
	public double maxMflow(ArrayList<String> timeData, int m, int ctr){
		double maxMflow = 999999999;
		
		
		//Pull out data from ArrayList
		String currentLine = " $$ ";
		String[] date = new String[ctr]; 
		String[] flow = new String[ctr];
		double[] Flows = new double[(int) Math.ceil(ctr/m)];
		ctr=0;
		
		for(int i=0; i<Flows.length; i++){
			Flows[i] = 999999999;
		}
		Iterator<String> iterate1 = timeData.iterator( );
		while(iterate1.hasNext()){
			currentLine = (String) iterate1.next();
			date[ctr] = currentLine.substring(0,currentLine.indexOf("$$"));
			flow[ctr] = currentLine.substring(currentLine.indexOf("$$")+2);
			ctr++;
		}
		
		//Find seven consecutive days with the same flow value
		double[][] dates = new double[m][3];
		int consecutiveFlowctr=0;
		double consecutiveAverage = 0;
		double yearUp4=0,yearDown4=0,yearUp100=0,yearDown100=0,yearUp400,yearDown400;
		boolean leapyear = false;
		ctr=0;
		for(int i=0; i<date.length; i++){
			if((date.length - i) >= m){
				for(int j=0; j<m; j++){
					dates[j][0] = Double.parseDouble(date[i+j].substring(0,4));//year
					dates[j][1] = Double.parseDouble(date[i+j].substring(5,7));//month
					dates[j][2] = Double.parseDouble(date[i+j].substring(8));//day
					//Compare dates to find consecutive days, if days aren't consecutive break the loop
					if(j==0){
						consecutiveAverage = consecutiveAverage + Double.parseDouble(flow[i+j]);
						consecutiveFlowctr++;
					}else if(j!=0 && (dates[j-1][0] == dates[j][0])){//compare year
						if(dates[j-1][1] == dates[j][1]){//check if same month
							if((dates[j-1][2] + 1) == dates[j][2]){//Check if is subsequent day
								consecutiveAverage = consecutiveAverage + Double.parseDouble(flow[i+j]);
								consecutiveFlowctr++;
							}else{
								break;
							}
						}else if((dates[j-1][1] + 1) == dates[j][1]){//check if last day of month and first day of next
							if(dates[j-1][1]==1 ||dates[j-1][1]==3 || dates[j-1][1]==5 || dates[j-1][1]==7 || dates[j-1][1]==8 || dates[j-1][1]==10 || dates[j-1][1]==12){
								if((dates[j-1][2]==31) && (dates[j][2]==1)){//Check if is subsequent day
									consecutiveAverage = consecutiveAverage + Double.parseDouble(flow[i+j]);
									consecutiveFlowctr++;
								}else{
									break;
								}
							}else if(dates[j-1][1]==4 || dates[j-1][1]==6 || dates[j-1][1]==9 || dates[j-1][1]==11){
								if((dates[j-1][2]==30) && (dates[j][2]==1)){//Check if is subsequent day
									consecutiveAverage = consecutiveAverage + Double.parseDouble(flow[i+j]);
									consecutiveFlowctr++;
								}else{
									break;
								}
							}else if(dates[j-1][1]==2){//If February check for leap years
								yearUp4 = Math.ceil(dates[j-1][0]/4);
								yearDown4 = Math.floor(dates[j-1][0]/4);
								yearUp100 = Math.ceil(dates[j-1][0]/100);
								yearDown100 = Math.floor(dates[j-1][0]/100);
								yearUp400 = Math.ceil(dates[j-1][0]/400);
								yearDown400 = Math.floor(dates[j-1][0]/400);
								if(yearUp400 == yearDown400){
									leapyear = true;
								}else if(yearUp100 == yearDown100){
									leapyear = false;
								}else if(yearUp4 == yearDown4){
									leapyear = true;
								}
								if(!leapyear  && (dates[j-1][2]==28) && (dates[j][2]==1)){//Check if is subsequent day
									consecutiveAverage = consecutiveAverage + Double.parseDouble(flow[i+j]);
									consecutiveFlowctr++;
								}else if(leapyear && (dates[j-1][2]==29) && (dates[j][2]==1)){//Check if is subsequent day
									consecutiveAverage = consecutiveAverage + Double.parseDouble(flow[i+j]);
									consecutiveFlowctr++;
								}else{
									break;
								}
							}else{
								break;
							}
						}else{
							break;
						}
					}else if(j!=0 && (dates[j-1][0] + 1) == dates[j][0]){//check if last day of the year and first day of next
						if(dates[j-1][1] == 31 && dates[j][1] == 1){//compare months
							if((dates[j-1][2]==31) && (dates[j][2]==1)){//Check if is subsequent day
								consecutiveAverage = consecutiveAverage + Double.parseDouble(flow[i+j]);
								consecutiveFlowctr++;
							}else{
								break;
							}
						}else{
							break;
						}
					}else{
						break;
					}
				}
				//If there are seven consecutive days find the average of the flows
				if(consecutiveFlowctr == m ){
					consecutiveAverage = consecutiveAverage/m;
					Flows[ctr] = consecutiveAverage;
					ctr++;
					i = i + m - 1;
				}
				consecutiveAverage = 0;
				consecutiveFlowctr = 0;
			}
		}
		//Sort all the "m" day flow to find the "time"'s minimum
		if(Flows.length > 0){
			Arrays.sort(Flows);
			if(Flows.length >= 2 && Flows[0] == 0 && Flows[1] != 0){
				maxMflow = Flows[Flows.length - 1];
			}else if(Flows.length >= 3 && Flows[0] == 0 && Flows[1] == 0 && Flows[2] != 0){
				maxMflow = Flows[Flows.length - 1];
			}else{
				maxMflow = Flows[Flows.length - 1];
			}
		}
		return maxMflow;
	}
	/**
	 * @param m  the m day average of flows
	 * @param n  the n return period of flows
	 * @param FlowTextData  flow data with associated date
	 */
	public String[] main(int m, int n, String[][] FlowTextData, String period, String minMax){
		double mQnFlow = 0;
		String[] returnValue = new String[2];
		//Check if m or n is zero
		if(m == 0 || n == 0){
			returnValue[0] = "";
			returnValue[1] = String.valueOf(mQnFlow);
			return returnValue;
		}
		//Find the time interval of interest, make sure there is sufficient data for the specified return period "n"
		String time1 = "", time2 = "";
		int endInt = 0;
		if(period.equalsIgnoreCase("year")){
			endInt = 4;
		}else if(period.equalsIgnoreCase("month")){
			endInt = 7;
		}
		
		//Find the number of unique time intervals available in the data for a maximum sized list
		int ctr = 0;
		for(int i=0; i<(FlowTextData.length); i++){
			if(i == 0){
				ctr++;
				continue;
			}
			time1 = FlowTextData[i-1][0].substring(0,endInt);
			time2 = FlowTextData[i][0].substring(0,endInt);
			if (!time1.equalsIgnoreCase(time2)){
				ctr++;
			}
		}
if (ctr < n){
	String message = "There is insufficient available flow data for the " + m + "Q" + n + " flow calculation and the specified date range";
	returnValue[0] = message;
	returnValue[1] = String.valueOf(mQnFlow);
	return returnValue;
}
		String[] times = new String[ctr];
		ctr=0;
		for(int i=0; i<(FlowTextData.length); i++){
			if(i==0){
				times[ctr] = FlowTextData[i][0].substring(0,endInt);
				ctr++;
				continue;
			}
			time1 = FlowTextData[i-1][0].substring(0,endInt);
			time2 = FlowTextData[i][0].substring(0,endInt);
			if (!time1.equalsIgnoreCase(time2)){
				times[ctr] = FlowTextData[i][0].substring(0,endInt);
				ctr++;
			}
		}
		//Sort all the m consecutive low flows in ascending order
		ArrayList<String> timeData = new ArrayList<String>();
		double[] mQnFlows = new double[ctr];
		double zero = 0;
		ctr=0;
		
		//For each unique time period find all of its data and search it for m consecutive low flows
		for(int j=0; j<times.length; j++){
			for(int i=0; i<FlowTextData.length; i++){
				if(FlowTextData[i][0].substring(0,endInt).equalsIgnoreCase(times[j])){
					zero = Double.parseDouble(FlowTextData[i][1]);
					if(zero != 0){
					timeData.add(FlowTextData[i][0] + "$$" + FlowTextData[i][1]);
					ctr++;
					}
				}
			}
			if(minMax.equalsIgnoreCase("minimum")){
				mQnFlows[j] = minMflow(timeData,m,ctr);
			}else{
				mQnFlows[j] = maxMflow(timeData,m,ctr);
			}
			timeData.removeAll(timeData);
			ctr=0;
		}

		//Sort the Data by flow to prepare for ranking
			Arrays.sort(mQnFlows);	
		
		//Find the recurrence interval of the sorted flows T = 1/p = (N+1)/M
			double totalSamples = mQnFlows.length;
			double[] sortedRank = new double[mQnFlows.length];
			double[] rankedFlow = new double[mQnFlows.length];
			double[] recurenceInterval = new double[mQnFlows.length];
			int i=0, h=0, g=0;
			for(i=1; (i<mQnFlows.length +1); i++){
				if((i != mQnFlows.length) && (mQnFlows[i-1] == (mQnFlows[i]))){
					//Find how many elements equal
					h = i;
					while((h != mQnFlows.length) && (mQnFlows[h-1] ==(mQnFlows[h]))){
						h++;
					} 
					//Give all the equal elements the rank of the largest equal element (max rank for tied rank scenarios)
					for(g=i; g<=h; g++){
						sortedRank[g-1] = h;
						rankedFlow[g-1] = mQnFlows[g-1];
						recurenceInterval[g-1] = (totalSamples + 1)/(sortedRank[g-1]);
						if (g==h){
							//If on the last repeated element, set the initial counter "i" that last 
							//rank value as to not repeat comparing already sorted and ranked values
							i=h;
						}
					}
				}else {
					sortedRank[i-1] = i;
					rankedFlow[i-1] = mQnFlows[i-1];
					recurenceInterval[i-1] = (totalSamples + 1)/(sortedRank[i-1]);
				}
			}
			//Find the "n" recurrence interval and return its corresponding flow as the mQnFlow
			double target = (double) n;
			for(i=0; i < (recurenceInterval.length - 1); i++){
				if(n < recurenceInterval[i] && n > recurenceInterval[i+1]){
					//Linear interpolation for flow value for "n" recurrence interval
					mQnFlow = ((target-recurenceInterval[i+1])/(recurenceInterval[i]-recurenceInterval[i+1]))*(rankedFlow[i] - rankedFlow[i+1]) + rankedFlow[i+1];
				}else if(n == recurenceInterval[i]){
					mQnFlow = rankedFlow[i];
				}
			}
			String message = "";
			if(mQnFlow == 0){
				message = "There is no " + m + " consecutive day flow recorded so there was no " + m + "Q" + n + " flow value calculated.";
			}
			returnValue[0] = message;
			returnValue[1] = String.valueOf(mQnFlow);
		return returnValue;
	}
}