DoubleArray.java [src/java/cfa] Revision: af6981c8e32f99e7dfe23929483fbaa0c6d867f3  Date: Wed Feb 05 09:43:06 MST 2014
package cfa;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;

class DateComparator implements Comparator<String[]>{
    //Compares the first entry of sorted data as flow values and sorts largest to smallest
    public int compare(final String[] entry1, final String[] entry2) {
        String value1 = entry1[0];
        String value2 = entry2[0];
        int comparison = value1.compareTo(value2);
        if(comparison>0){
            return 1;
        }else if(comparison<0){
            return -1;
        }else{
            return 0;
        }
    }
}
/**
* Last Updated: 5-February-2014
* @author Tyler Wible
* @since 21-June-2012
*/
public class DoubleArray {
    /**
     * Append a value on to the end of list of values
     * @param currentArray  current list of values (double[])
     * @param newValue  new value to append to the list
     * @return  the new list containing the currentArray list of values and the appened newValue
     */
    public double[] appendcolumn(double[] currentArray, double newValue){
        //Initialize new array to contain the new value
        double[] newArray = new double[currentArray.length + 1];

        //Fill the new array
        for(int i=0; i<currentArray.length; i++){
            newArray[i] = currentArray[i];
        }
        //Append newValue
        newArray[currentArray.length] = newValue;

        return newArray;
    }
    /**
     * Append a value on to the end of list of values
     * @param currentArray  current list of values (double[])
     * @param newValue  new value to append to the list
     * @return  the new list containing the currentArray list of values and the appened newValue
     */
    public Double[] appendcolumn(double[] currentArray, double[] newValue){
        //Initialize new array to contain the new value
        Double[] newArray = new Double[currentArray.length + newValue.length];

        //Fill the new array
        for(int i=0; i<newArray.length; i++){
            if(i<currentArray.length){
                newArray[i] = currentArray[i];
            }else{
                newArray[i] = newValue[i - currentArray.length];
            }
        }

        return newArray;
    }
    /**
     * Appends an array onto another array to create a 2D array.   double[][] newArray = {currentArray, newArray};
     * @param currentArray1  current list of values (double[])
     * @param currentArray2  new value to append to the list (double[])
     * @return  the new list containing the currentArray list of values and the appened newValue
     */
    public double[][] appendcolumn_Matrix(double[] currentArray1, double[] currentArray2){
        //Check that the arrays are the same size to be combined
        if(currentArray1.length != currentArray2.length){
            System.out.println("The arrays are not the same size so they can not be combined");
            return new double[0][0];
        }

        //Initialize new array to contain the new value
        double[][] newArray = new double[currentArray1.length][2];

        //Fill the new array
        for(int i=0; i<newArray.length; i++){
            newArray[i][0] = currentArray1[i];
            newArray[i][1] = currentArray2[i];
        }

        return newArray;
    }
    /**
     * Append a value on to the end of list of values
     * @param currentArray  current list of values (double[][])
     * @param newValue  new value to append to the list (double[])
     * @return  the new list containing the currentArray list of values and the appened newValue
     */
    public double[][] appendcolumn_Matrix(double[][] array1, double[] array2){
        //Check that the arrays are the same size to be combined
        if(array1.length != array2.length){
            System.out.println("The arrays are not the same size so they can not be combined");
            return new double[0][0];
        }

        //Initialize new array to contain the new value
        double[][] newArray = new double[array1.length][array1[0].length + 1];

        //Fill the new array
        for(int i=0; i<newArray.length; i++){
            for(int j=0; j<array1[i].length; j++){
                newArray[i][j] = array1[i][j];
            }
            newArray[i][newArray[i].length - 1] = array2[i];
        }

        return newArray;
    }
    /**
     * Replace the first ("numberOfRows" + 1) of "startingArray" with those of "replacingArray"
     * @param startingArray  the double[] array to begin duplicating
     * @param replacingArray  the double[] array to be used replacing the first (rows + 1) of starting Array
     * @param numberOfRows  the number of rows in startingArray to be replaced by those in replacingArray
     * @return  a duplicate of startingArray with the first rows ("numberOfRows" + 1) being from replacingArray not startingArray
     */
    public double[] replaceRows(double[] startingArray, double[] replacingArray, int numberOfRows){
        //Matlab code:	m(Z+1:end) = mh(Z+1:end);
        double[] newArray = new double[startingArray.length];

        for(int i=0; i<startingArray.length; i++){
            if(i<(numberOfRows+1)){
                //Replace the elements of startingArray with those of replacingArray
                newArray[i] = replacingArray[i];
            }else{
                //Otherwise keep the elements of startingArray
                newArray[i] = startingArray[i];
            }
        }
        return newArray;
    }
    /**
     * Keeps only the rows in startingArray that are not listed in rowIndexList
     * @param startingArray  The array to be minimized (double[][])
     * @param rowIndexList  a row index of the rows not to be included in the new minimized array (ArrayList<Integer>)
     * @return the minimized version of the startingArray (double[][]) 
     */
    public double[][] replaceRows(double[][] startingArray, ArrayList<Integer> rowIndexList){

        //Initialize new array
        double[][] newArray = new double[startingArray.length][startingArray[0].length];

        //Keep only the rows in startingArray that are not contained in the rowIndexList
        int indexCtr = 0;
        int currentRowIndex = rowIndexList.get(indexCtr);

        for(int i=0; i<startingArray.length; i++){//Loop rows of startingArray
            if(i != currentRowIndex){//Check if the current row should be kept or not
                //Keep current row's infomation
                newArray[i][0] = startingArray[i][0];//Populate the newArray with the content of startingArray
                newArray[i][1] = startingArray[i][1];
            }else{
                //Don't keep current row's information
                newArray[i][0] = startingArray[i][0];//Populate the newArray with the content of startingArray
                newArray[i][1] = 99999;//replace the values below 0.002
                indexCtr++;
                if(indexCtr >= rowIndexList.size()){
                    //If the indexCtr == rowIndexList.size then the last row index has been used out 
                    //of the rowIndexList so define an empty early index to keep the loop going
                    currentRowIndex = 0;
                }else{
                    //Then get the next row 
                    currentRowIndex = rowIndexList.get(indexCtr);
                }
            }
        }
        return newArray;
    }
    /**
     * Gets the unique values of the specified column of a double[][] array
     * @param dataArray  the double array
     * @param column  the desired column of the double array
     * @return a double[] array of the unique valued contained in the specified column of the dataArray
     */
    public double[] getUnique(double[][] dataArray, int column){
        //Matlab code:	P = unique(ktable(:,2));
        double[] currentColumn = new double[dataArray.length];

        for(int i=0; i<dataArray.length; i++){
            currentColumn[i] = dataArray[i][column];
        }

        Arrays.sort(currentColumn);

        //Check and remove duplicate values for a list of unique values
        double[] uniqueColumn = getUnique(currentColumn);

        return uniqueColumn;
    }
    /**
     * Gets the unique values of the double[] array
     * @param currentColumn  the double array
     * @return a double[] array of the unique valued contained in the dataArray
     */
    public double[] getUnique(double[] currentColumn){
        Arrays.sort(currentColumn);

        //Check and remove duplicate values for a list of unique values
        int ctr=0;
        for(int i=0; i<currentColumn.length; i++){
            if(i == 0){
                ctr++;
                continue;
            }
            if(Double.compare(currentColumn[i-1],currentColumn[i]) != 0){
                ctr++;
            }
        }
        double[] uniqueColumn = new double[ctr];
        ctr=0;
        for(int i=0; i<currentColumn.length; i++){
            if(i==0){
                uniqueColumn[ctr] = currentColumn[i];
                ctr++;
                continue;
            }
            if(Double.compare(currentColumn[i-1],currentColumn[i]) != 0){
                uniqueColumn[ctr] = currentColumn[i];
                ctr++;
            }
        }
        return uniqueColumn;
    }
    /**
     * Creates an ArrayList<Integer> of the rows in dataArray that have the column number "column" equal to "currentValue"
     * @param dataArray  the double[][] data array
     * @param column  the column of interest of the data array
     * @param currentValue  the desired value from the data array
     * @return  an ArrayList<Integer> of the rows in dataArray[][column] == currentValue
     */
    public ArrayList<Integer> findRowsEqual(double[][] dataArray, int column, double currentValue){
        ArrayList<Integer> rowIndexList = new ArrayList<Integer>();
        for(int i=0; i<dataArray.length; i++){
            if(Double.compare(dataArray[i][column],currentValue) == 0){
                rowIndexList.add(i);
            }
        }
        return rowIndexList;
    }
    /**
     * Creates an ArrayList<Integer> of the rows in dataArray that have the column number "column" less or greater than to "currentValue"
     * @param dataArray  the double[][] data array
     * @param column  the column of interest of the data array
     * @param currentValue  the desired value from the data array
     * @param lessThan  if true, then all the rows with data less than the currentValue will be returned, if false all the rows with data greater than the current value will be returned
     * @return  an ArrayList<Integer> of the rows in dataArray[][column] less than or greater than (depending on the value of "lessThan" provided) currentValue
     */
    public ArrayList<Integer> findRowsConditional(double[][] dataArray, int column, double currentValue, boolean lessThan){
        ArrayList<Integer> rowIndexList = new ArrayList<Integer>();
        for(int i=0; i<dataArray.length; i++){
            if(lessThan){//If lessThan ==  true, then find values less than the currentValue
                if(Double.compare(dataArray[i][column],currentValue) < 0){
                    rowIndexList.add(i);
                }
            }else{//If lessThan ==  false, then find values greater than the currentValue
                if(Double.compare(dataArray[i][column],currentValue) > 0){
                    rowIndexList.add(i);
                }
            }
        }
        return rowIndexList;
    }
    /**
     * Creates an ArrayList<Integer> of the rows in dataArray that have the column number "column" less/equal to or greater than to "currentValue"
     * @param dataArray  the double[][] data array
     * @param column  the column of interest of the data array
     * @param currentValue  the desired value from the data array
     * @param lessThan  if true, then all the rows with data less than the currentValue will be returned, if false all the rows with data greater than the current value will be returned
     * @return  an ArrayList<Integer> of the rows in dataArray[][column] less than/equal to or greater than (depending on the value of "lessThan" provided) currentValue
     */
    public ArrayList<Integer> findRowsConditional(double[] dataArray, double currentValue, boolean lessThan){
        ArrayList<Integer> rowIndexList = new ArrayList<Integer>();
        for(int i=0; i<dataArray.length; i++){
            if(lessThan){//If lessThan ==  true, then find values less than the currentValue
                if(Double.compare(dataArray[i],currentValue) <= 0){
                    rowIndexList.add(i);
                }
            }else{//If lessThan ==  false, then find values greater than the currentValue
                if(Double.compare(dataArray[i],currentValue) >= 0){
                    rowIndexList.add(i);
                }
            }
        }

        return rowIndexList;
    }
    /**
     * Creates a double[] array of the elements in dataArray[all][column]
     * @param dataArray  the double[][] data array
     * @param column  the desired column from data array
     * @return  a double[] array of the elements in dataArray[all][column]
     */
    public double[] getColumn(double[][] dataArray, int column){
        //Gets the values of dataArray[all][column] in a double[]
        double[] currentElements = new double[dataArray.length];

        for(int i=0; i<dataArray.length; i++){
            currentElements[i] = dataArray[i][column];
        }
        return currentElements;
    }
    /**
     * Creates a double[] array of the elements in dataArray[rowIndexList][column]
     * @param dataArray  the double[][] data array
     * @param column  the desired column from data array
     * @param rowIndexList  an ArrayList<Integer> containing the desired row indices from dataArray
     * @return  a double[] array of the elements in dataArray[rowIndexList][column]
     */
    public double[] getColumn(double[][] dataArray, int column, ArrayList<Integer> rowIndexList){
        //Gets the values of dataArray[rowIndexList][column] in a double[]
        double[] currentElements = new double[rowIndexList.size()];

        int indexCtr = 0;
        for(int i=0; i<dataArray.length; i++){
            if(i == rowIndexList.get(indexCtr)){
                currentElements[indexCtr] = dataArray[i][column];
                indexCtr++;
                if(indexCtr == rowIndexList.size()){
                    //If the indexCtr == rowIndexList.size then the last row 
                    //index has been used out of the rowIndexList so exit the loop and return the list
                    break;
                }
            }
        }
        return currentElements;
    }
    /**
     * Creates a double[] array of the elements in dataArray[row][all]
     * @param dataArray  the double[][] data array
     * @param row  the desired row from data array
     * @return  a double[] array of the elements in dataArray[row][all]
     */
    public double[] getRow(double[][] dataArray, int row){
        //Gets the values of dataArray[row][all] in a double[]
        double[] currentElements = new double[dataArray[row].length];

        for(int i=0; i<dataArray[row].length; i++){
            currentElements[i] = dataArray[row][i];
        }
        return currentElements;
    }
    /**
     * Creates a double[] array of the elements in dataArray[row][columnIndexList]
     * @param dataArray  the double[][] data array
     * @param row  the desired row from data array
     * @param columnIndexList  an ArrayList<Integer> containing the desired row indices from dataArray
     * @return  a double[] array of the elements in dataArray[rowIndexList][column]
     */
    public double[] getRow(double[][] dataArray, int row, ArrayList<Integer> columnIndexList){
        //Gets the values of dataArray[rowIndexList][column] in a double[]
        double[] currentElements = new double[columnIndexList.size()];

        int indexCtr = 0;
        for(int i=0; i<dataArray[row].length; i++){
            if(i == columnIndexList.get(indexCtr)){
                currentElements[indexCtr] = dataArray[row][i];
                indexCtr++;
                if(indexCtr == columnIndexList.size()){
                    //If the indexCtr == rowIndexList.size then the last row 
                    //index has been used out of the rowIndexList so exit the loop and return the list
                    break;
                }
            }
        }
        return currentElements;
    }
    /**
     * Creates a double[] array of the elements in dataArray[rowIndexList]
     * @param dataArray  the double[] data array
     * @param rowIndexList  an ArrayList<Integer> containing the desired row indices from dataArray
     * @return a double[] array of the elements in dataArray[rowIndexList]
     */
    public double[] getRows(double[] dataArray, ArrayList<Integer> rowIndexList){
        //Gets the values of dataArray[rowIndexList] in a double[]
        double[] newArray = new double[rowIndexList.size()];

        int indexCtr = 0;
        for(int i=0; i<dataArray.length; i++){
            if(i == rowIndexList.get(indexCtr)){
                newArray[indexCtr] = dataArray[i];
                indexCtr++;
                if(indexCtr == rowIndexList.size()){
                    //If the indexCtr == rowIndexList.size then the last row 
                    //index has been used out of the rowIndexList so exit the loop and return the list
                    break;
                }
            }
        }
        return newArray;
    }
    /**
     * Creates a double[][] array of the elements in dataArray[rowIndexList][] (aka it keeps some rows and all columns)
     * @param dataArray  the double[][] data array
     * @param rowIndexList  an ArrayList<Integer> containing the desired row indices from dataArray
     * @return  a double[][] array containing the desired rows from rowIndexList and all the columns
     */
    public double[][] getDataArrayRows(double[][] dataArray, ArrayList<Integer> rowIndexList){
        //Gets the values of dataArray[rowIndexList][all] in a double[][]
        double[][] currentElements = new double[rowIndexList.size()][dataArray[0].length];

        if(rowIndexList.size() == 0){
            return currentElements;
        }
        int indexCtr = 0;
        for(int i=0; i<dataArray.length; i++){//Loop rows of data array
            if(i == rowIndexList.get(indexCtr)){//Check if current row is a desired row from the rowIndexList
                for(int j=0; j<dataArray[i].length; j++){//Loop columns of data array
                    currentElements[indexCtr][j] = dataArray[i][j];
                }
                indexCtr++;
                if(indexCtr == rowIndexList.size()){
                    //If the indexCtr == rowIndexList.size then the last row 
                    //index has been used out of the rowIndexList so exit the loop and return the list
                    break;
                }				
            }
        }
        return currentElements;
    }
    /**
     * Compares array1 and array2 and returns an ArrayList<Integer> of the "j" rows in which array1[i] =  array2[j]
     * @param array1  double[] first arary to compare
     * @param array2  double[] second array to compare
     * @return  an ArrayList<Integer> containing the rows in which array1[i] = array2[i]
     */
    public ArrayList<Integer> intersect(double[] array1, double[] array2){
        ArrayList<Integer> rowIndex = new ArrayList<Integer>();

        //Find the "j" rows where array1[i] = array2[j]
        for(int i=0; i<array1.length; i++){
            for(int j=0; j<array2.length; j++){
                if(Double.compare(array1[i], array2[j]) == 0){
                    rowIndex.add(j);
                }
            }
        }
        Collections.sort(rowIndex);
        return rowIndex;		
    }
    /**
     * Creates an double[] array of random numbers of array.length = size
     * @param size  the number of elements desired in the array
     * @return
     */
    public double[] RandomArray(int size){

        double[] randomArray = new double[size];
        for(int i=0; i<size; i++){
            randomArray[i] = Math.random();
        }

        return randomArray;
    }
    /**
     * Merges the public dataset (first input) with the user dataset (second input)
     * based on the merge method selected
     * @param publicDataset  a String[][] array with the first column containing dates (yyyy-mm-dd)
     * and the second column values of flow or water quality test to be treated as the "public dataset"
     * for merge method purposes
     * @param userDataset  a String[][] array with the first column containing dates (yyyy-mm-dd)
     * and the second column values of flow or water quality test to be treated as the "user dataset"
     * for merge method purposes
     * @param mergeMethod  the method to merge the two datasets, one of the following:
     * "user" = overwrite public data with user data
     * "public" = overwrite user data with public data
     * "maximum" =  take the maximum value for that day from both datasets
     * "average" =  take the average of the two values for that day from both datasets
     * "minimum" =  take the minimum value for that day from both datasets
     * @return
     */
    public String[][] mergeData(String[][] publicDataset, String[][] userDataset, String mergeMethod){
        //Allocate an array of values to determine if userdata needs to be kept (true) or merged (false)
        int ctr = 0;
        boolean[] addUserDataset = new boolean[userDataset.length];
        for(int i=0; i<userDataset.length; i++){
            addUserDataset[i] = true;
            for(int j=0; j<publicDataset.length; j++){
                if(userDataset[i][0].equalsIgnoreCase(publicDataset[j][0])){
                    addUserDataset[i] = false;
                    ctr++;
                    break;
                }
            }
        }        
        
        String[][] mergeDataset = new String[publicDataset.length + userDataset.length - ctr][2];
        boolean merge = false;
        ctr = 0;
        for(int i=0; i<publicDataset.length; i++){
            for(int j=0; j<userDataset.length; j++){
                //If the dates match, merge the datasets
                if(publicDataset[i][0].equalsIgnoreCase(userDataset[j][0])){
                    if(mergeMethod.equalsIgnoreCase("user")){
                        mergeDataset[ctr][0] = userDataset[j][0];
                        mergeDataset[ctr][1] = userDataset[j][1];
                        ctr++;
                    }else if(mergeMethod.equalsIgnoreCase("public")){
                        mergeDataset[ctr][0] = publicDataset[i][0];
                        mergeDataset[ctr][1] = publicDataset[i][1];
                        ctr++;
                    }else if(mergeMethod.equalsIgnoreCase("max")){
                        double userValue = Double.valueOf(userDataset[j][1]);
                        double publicValue = Double.valueOf(publicDataset[i][1]);
                        if(Double.compare(userValue, publicValue) < 0){
                            mergeDataset[ctr][1] = String.valueOf(publicValue);
                        }else{
                            mergeDataset[ctr][1] = String.valueOf(userValue);
                        }
                        mergeDataset[ctr][0] = publicDataset[i][0];
                        ctr++;
                    }else if(mergeMethod.equalsIgnoreCase("average")){
                        double userValue = Double.valueOf(userDataset[j][1]);
                        double publicValue = Double.valueOf(publicDataset[i][1]);
                        mergeDataset[ctr][0] = publicDataset[i][0];
                        mergeDataset[ctr][1] = String.valueOf((userValue + publicValue)/2);
                        ctr++;
                    }else if(mergeMethod.equalsIgnoreCase("min")){
                        double userValue = Double.valueOf(userDataset[j][1]);
                        double publicValue = Double.valueOf(publicDataset[i][1]);
                        if(Double.compare(userValue, publicValue) < 0){
                            mergeDataset[ctr][1] = String.valueOf(userValue);
                        }else{
                            mergeDataset[ctr][1] = String.valueOf(publicValue);
                        }
                        mergeDataset[ctr][0] = publicDataset[i][0];
                        ctr++;
                    }
                    merge = true;
                    break;
                }
            }
            if(!merge){
                mergeDataset[ctr][0] = publicDataset[i][0];
                mergeDataset[ctr][1] = publicDataset[i][1];
                ctr++;
            }
            merge = false;
        }
        
        //Include the points from the user dataset that aren't duplicate points
        for(int i=0; i<addUserDataset.length; i++){
            if(addUserDataset[i]){
                mergeDataset[ctr][0] = userDataset[i][0];
                mergeDataset[ctr][1] = userDataset[i][1];
                ctr++;
            }
        }
        
        Arrays.sort(mergeDataset, new DateComparator());
        return mergeDataset;
    }
    /**
     * Merges the public dataset (first input) with the user dataset (second input)
     * based on the merge method selected
     * @param publicDataset  a double[][] array with the first column containing years (yyyy)
     * and the second column values of flow or water quality test to be treated as the "public dataset"
     * for merge method purposes
     * @param userDataset  a String[][] array with the first column containing years (yyyy)
     * and the second column values of flow or water quality test to be treated as the "user dataset"
     * for merge method purposes
     * @param mergeMethod  the method to merge the two datasets, one of the following:
     * "user" = overwrite public data with user data
     * "public" = overwrite user data with public data
     * "max" =  take the maximum value for that day from both datasets
     * "average" =  take the average of the two values for that day from both datasets
     * "min" =  take the minimum value for that day from both datasets
     * @return
     */
    public double[][] mergeData(double[][] publicDataset, double[][] userDataset, String mergeMethod){
        
        //Sort by dates for comparison purposes
        Arrays.sort(publicDataset, new sort1_smallToLargeDoubleMath());
        Arrays.sort(userDataset, new sort1_smallToLargeDoubleMath());
        
        //Allocate an array of values to determine if userdata needs to be kept (true) or merged (false)
        boolean[] useUserDataset = new boolean[userDataset.length];
        for(int i=0; i<useUserDataset.length; i++){
            useUserDataset[i] = true;
        }
        int ctr = 0;
        for(int i=0; i<publicDataset.length; i++){
            for(int j=0; j<userDataset.length; j++){
                if(Double.compare(publicDataset[i][0],userDataset[j][0]) == 0){
                    useUserDataset[j] = false;
                    ctr++;
                    break;
                }
            }
        }
        
        double[][] mergeDataset = new double[publicDataset.length + userDataset.length - ctr][2];
        boolean merge = false;
        ctr = 0;
        for(int i=0; i<publicDataset.length; i++){
            for(int j=0; j<userDataset.length; j++){
                //If the dates match, merge the datasets
                if(Double.compare(publicDataset[i][0],userDataset[j][0]) == 0){
                    if(mergeMethod.equalsIgnoreCase("user")){
                        mergeDataset[ctr][0] = userDataset[j][0];
                        mergeDataset[ctr][1] = userDataset[j][1];
                        ctr++;
                    }else if(mergeMethod.equalsIgnoreCase("public")){
                        mergeDataset[ctr][0] = publicDataset[i][0];
                        mergeDataset[ctr][1] = publicDataset[i][1];
                        ctr++;
                    }else if(mergeMethod.equalsIgnoreCase("max")){
                        if(Double.compare(userDataset[i][1], publicDataset[i][1]) < 0){
                            mergeDataset[ctr][1] = publicDataset[i][1];
                        }else{
                            mergeDataset[ctr][1] = userDataset[j][1];
                        }
                        mergeDataset[ctr][0] = publicDataset[i][0];
                        ctr++;
                    }else if(mergeMethod.equalsIgnoreCase("average")){
                        mergeDataset[ctr][0] = publicDataset[i][0];
                        mergeDataset[ctr][1] = (userDataset[j][1] + publicDataset[j][1])/2;
                        ctr++;
                    }else if(mergeMethod.equalsIgnoreCase("min")){
                        if(Double.compare(userDataset[j][1], publicDataset[j][1]) < 0){
                            mergeDataset[ctr][1] = userDataset[j][1];
                        }else{
                            mergeDataset[ctr][1] = publicDataset[i][1];
                        }
                        mergeDataset[ctr][0] = publicDataset[i][0];
                        ctr++;
                    }
                    merge = true;
                    break;
                }
            }
            if(!merge){
                mergeDataset[ctr][0] = publicDataset[i][0];
                mergeDataset[ctr][1] = publicDataset[i][1];
                ctr++;
            }
            merge = false;
        }
        
        //Include the points from the user dataset that aren't duplicate points
        for(int i=0; i<useUserDataset.length; i++){
            if(useUserDataset[i]){
                mergeDataset[ctr][0] = userDataset[i][0];
                mergeDataset[ctr][1] = userDataset[i][1];
                ctr++;
            }
        }
        
        Arrays.sort(mergeDataset, new sort1_smallToLargeDoubleMath());
        return mergeDataset;
    }
    /**
     * Sorts the String[][] by the first column (dates) and then removes duplicate 
     * date values and returns an array of equal or lesser size than the original
     * @param currentStringArray the String[][] to be sorted and removed, column1 = dates (yyyy-mm-dd), column2 = values, other columns will also be kept
     * @return  a String[][] of equal or lesser size than the original containing the original data sorted by the first column
     */
    public String[][] removeDuplicateDates(String[][] currentStringArray){
        //Sort the Data by date to remove duplicate date entries
        Arrays.sort(currentStringArray, new DateComparator());
        
        //Check and remove duplicate days as to correct statistical implication of duplicate flow values on a day
        int ctr=0;
        for(int i=0; i<(currentStringArray.length); i++){
            if(i == 0){
                ctr++;
                continue;
            }
            if(!currentStringArray[i-1][0].equals(currentStringArray[i][0])){
                ctr++;
            }
        }
        String[][] sortedData = new String[ctr][2];
        ctr=0;
        for(int i=0; i<(currentStringArray.length); i++){
            if(i==0){
                sortedData[ctr][0] = currentStringArray[i][0];
                sortedData[ctr][1] = currentStringArray[i][1];
                ctr++;
                continue;
            }
            if(!currentStringArray[i-1][0].equals(currentStringArray[i][0])){
                sortedData[ctr][0] = currentStringArray[i][0];
                sortedData[ctr][1] = currentStringArray[i][1];
                ctr++;
            }
        }

        return sortedData;
    }
    /**
     * Reduces the provided water quality data to only within the user specified season
     * @param allWQdata  all the WQdata for the user specified date range to be minimized by season (column1 = date (yyyy-mm-dd), column2 = value)
     * @param seasonBegin  the user specified begin month of the season (ex "February")
     * @param seasonEnd  the user specified end month of the season (ex "April")
     * @return  an array containing the dates and values of water quality tests that occur within the specified season
     */
    public String[][] getSeasonalWQData(String[][] allWQdata, String seasonBegin, String seasonEnd){
        //Convert season month into season integer
        int seasonBegin_number = convertMonth(seasonBegin);
        int seasonEnd_number = convertMonth(seasonEnd);

        //Remove water quality tests that are outside user specified seasonal range
        int ctr = 0;
        if(seasonBegin_number < seasonEnd_number){
            for(int i=0; i<allWQdata.length; i++){
                int tempdate = Integer.parseInt(allWQdata[i][0].substring(5,7));
                if((tempdate > seasonBegin_number) && (tempdate < seasonEnd_number)){
                    ctr++;
                }
            }
        }else{
            for(int i=0; i<allWQdata.length; i++){
                int tempdate = Integer.parseInt(allWQdata[i][0].substring(5,7));
                if((tempdate > seasonBegin_number) || (tempdate < seasonEnd_number)){
                    ctr++;
                }
            }
        }
        String[][] SeasonalWQdata = new String[ctr][2];
        ctr = 0;
        if(seasonBegin_number < seasonEnd_number){
            for(int i=0; i<allWQdata.length; i++){
                int tempdate = Integer.parseInt(allWQdata[i][0].substring(5,7));
                if((tempdate > seasonBegin_number) && (tempdate < seasonEnd_number)){
                    SeasonalWQdata[ctr][0] = allWQdata[i][0];
                    SeasonalWQdata[ctr][1] = allWQdata[i][1];
                    ctr++;
                }
            }
        }else{
            for(int i=0; i<allWQdata.length; i++){
                int tempdate = Integer.parseInt(allWQdata[i][0].substring(5,7));
                if((tempdate > seasonBegin_number) || (tempdate < seasonEnd_number)){
                    SeasonalWQdata[ctr][0] = allWQdata[i][0];
                    SeasonalWQdata[ctr][1] = allWQdata[i][1];
                    ctr++;
                }
            }
        }

        return SeasonalWQdata;
    }
    /**
     * Sub-function to convert month text to month integer
     * @param month_String  the text version of the month (Ex. January).
     * @return An Integer (Ex. 1).
     */
     public int convertMonth (String month_String){
        int month_number = 0;
        if (month_String.equalsIgnoreCase("January")){
            month_number = 1;
        }else if (month_String.equalsIgnoreCase("February")){
            month_number = 2;
        }else if (month_String.equalsIgnoreCase("March")){
            month_number = 3;
        }else if (month_String.equalsIgnoreCase("April")){
            month_number = 4;
        }else if (month_String.equalsIgnoreCase("May")){
            month_number = 5;
        }else if (month_String.equalsIgnoreCase("June")){
            month_number = 6;
        }else if (month_String.equalsIgnoreCase("July")){
            month_number = 7;
        }else if (month_String.equalsIgnoreCase("August")){
            month_number = 8;
        }else if (month_String.equalsIgnoreCase("September")){
            month_number = 9;
        }else if (month_String.equalsIgnoreCase("October")){
            month_number = 10;
        }else if (month_String.equalsIgnoreCase("November")){
            month_number = 11;
        }else if (month_String.equalsIgnoreCase("December")){
            month_number = 12;
        }
        return month_number;
     }
     /**
     * Reduces the provided data to only within the specified year
     * @param allData  all the data for the to be minimized by year (column1 = date (yyyy-mm-dd), column2 = value)
     * @param year  the specified year (ex "1981")
     * @return an array (same format as allData) containing the dates and values that occur within the specified year
     */
    public String[][] getYearsData(String[][] allData, String year){
        //Remove data that are outside specified year
        int ctr = 0;
        for(int i=0; i<allData.length; i++){
            String tempYear = allData[i][0].substring(0,4);
            if(year.compareToIgnoreCase(tempYear) == 0){
                ctr++;
            }
        }

        String[][] SeasonalData = new String[ctr][2];
        ctr = 0;
        for(int i=0; i<allData.length; i++){
            String tempYear = allData[i][0].substring(0,4);
            if(year.compareToIgnoreCase(tempYear) == 0){
                SeasonalData[ctr][0] = allData[i][0];
                SeasonalData[ctr][1] = allData[i][1];
                ctr++;
            }
        }

        return SeasonalData;
    }
    /**
     * Calculates a unique list of years contained in the flow data from STORET then proceeds to 
     * calculate the annual maximum flow value for each of the unique years and returns the list 
     * of years and flow values as a double array
     * @param sortableData  string[][] array containing the output of STORET_unzip_FDC.main which contains
     * dates (YYYY-mm-dd) in the first column and flow values (cfs) in the second column
     * @return returns a double[][] array the same size as the provided string array containing 
     * the first column of years and the second column of flow values
     */
    public double[][] convertSTORETpeakData(String[][] sortableData){

        Arrays.sort(sortableData, new DateComparator());
        //Find a list of unique years for which an annual maximum flow will be calculated later
        int ctr=0;
        for(int i=0; i<(sortableData.length); i++){
            if(i == 0){
                ctr++;
                continue;
            }
            String year1 = sortableData[i-1][0].substring(0,4);
            String year2 = sortableData[i][0].substring(0,4);
            if (!year1.equals(year2)){
                ctr++;
            }
        }
        String[] uniqueYears = new String[ctr];
        ctr=0;
        for(int i=0; i<(sortableData.length); i++){
            if(i==0){
                uniqueYears[ctr] = sortableData[i][0].substring(0,4);
                ctr++;
                continue;
            }
            String year1 = sortableData[i-1][0].substring(0,4);
            String year2 = sortableData[i][0].substring(0,4);
            if (!year1.equals(year2)){
                uniqueYears[ctr] = sortableData[i][0].substring(0,4);
                ctr++;
            }
        }


        //Loop through and find the annual maximum flow value for each unique year
        double[][] peakFlowData = new double[uniqueYears.length][2];
        for(int i=0; i<uniqueYears.length; i++){
            peakFlowData[i][0] = Double.parseDouble(uniqueYears[i]);
            peakFlowData[i][1] = 0;
            for(int j=0; j<sortableData.length; j++){
                String currentYear = sortableData[j][0].substring(0,4);
                if(uniqueYears[i].equals(currentYear)){
                    double flowValue = Double.parseDouble(sortableData[j][1]);
                    if(Double.compare(flowValue, peakFlowData[i][1]) > 0){//If current value larger than "max" change the max to the current value
                        peakFlowData[i][1] = flowValue;
                    }
                }
            }
        }

        return peakFlowData;
    }
    /**
     * Checks if the provided dates are subsequent dates, aka nextDate = date + 1day.  
     * This check includes December 31st to January 1st catchs, 4-year leap-year catches, 
     * 100-year non-leap-year catches and 400-year leap-year catches
     * @param date  the first date to be compared (expected format = yyyy-mm-dd)
     * @param nextDate  the second date to be compared (expected format = (yyyy-mm-dd)
     * @return returns true if nextDate = date + 1day, false otherwise
     */
    private boolean checkSubsequentDates(String date, String nextDate){
        double year = Double.parseDouble(date.substring(0,4));
        double month = Double.parseDouble(date.substring(5,7));
        double day = Double.parseDouble(date.substring(8));

        double year2 = Double.parseDouble(nextDate.substring(0,4));
        double month2 = Double.parseDouble(nextDate.substring(5,7));
        double day2 = Double.parseDouble(nextDate.substring(8));

        boolean subsequentDates = false;
        //Check if nextDate = date + 1 day

        if(Double.compare(year,year2) == 0){//Check if same year
            if(Double.compare(month, month2) == 0){//Check if same month
                if(Double.compare(day + 1, day2) == 0){//Check if subsequent day
                    subsequentDates = true;	
                }
            }else{
                if((Double.compare(month + 1, month2) == 0) && //Check if subsequent month
                        (Double.compare(day2,1) == 0)){//Check if first day
                    //Check months with 31 days
                    if((Double.compare(day, 31) == 0) &&
                            ((Double.compare(month, 1) == 0) ||
                            (Double.compare(month, 3) == 0) ||
                            (Double.compare(month, 5) == 0) ||
                            (Double.compare(month, 7) == 0) ||
                            (Double.compare(month, 8) == 0) ||
                            (Double.compare(month, 10) == 0))){
                        subsequentDates = true;

                    //Check months with 30 days
                    }else if((Double.compare(day, 30) == 0) &&
                            ((Double.compare(month, 4) == 0) ||
                            (Double.compare(month, 6) == 0) ||
                            (Double.compare(month, 9) == 0) ||
                            (Double.compare(month, 11) == 0))){
                        subsequentDates = true;
                    
                    //Check February for leap years (including the 100-year-not-leap-year and 400-year-leap-year)
                    }else if((Double.compare(month, 2) == 0)){
                        boolean leapYear = false;
                        double yearUp4 = Math.ceil(year/4);
                        double yearDown4 = Math.floor(year/4);
                        double yearUp100 = Math.ceil(year/100);
                        double yearDown100 = Math.floor(year/100);
                        double yearUp400 = Math.ceil(year/400);
                        double yearDown400 = Math.floor(year/400);
                        if(yearUp400 == yearDown400){
                            leapYear = true;
                        }else if(yearUp100 == yearDown100){
                            leapYear = false;
                        }else if(yearUp4 == yearDown4){
                            leapYear = true;
                        }
                        //Check non-leap years (28 day February)
                        if(!leapYear  && (Double.compare(day, 28) == 0)){//Check if is subsequent day
                            subsequentDates = true;

                        //Check leap years (29 day February)
                        }else if(leapYear && (Double.compare(day, 29) == 0)){//Check if is subsequent day
                            subsequentDates = true;
                        }
                    }
                }
            }

        }else{
            //Check if subsequent years, months, and days from December 31st to January 1st
            if((Double.compare(year + 1, year2) == 0) && //Check if subsequent years
                    (Double.compare(month, 12) == 0) && //and the first date is December
                    (Double.compare(month2, 1) == 0) && //and the second date is January
                    (Double.compare(day, 31) == 0) && //and the first date is the 31st
                    (Double.compare(day2, 1) == 0)){ //and the second date is the 1st
                subsequentDates = true;
            }
        }

        return subsequentDates;
    }
}