/*
 *  Draco Econometrics
 *  Copyright (C) 2006-2007 Approximatrix, LLC
 *  http://www.approximatrix.com
 *  
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.approximatrix.draco.fit;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.List;
import java.util.Locale;
import java.util.ArrayList;

import org.netlib.blas.DAXPY;
import org.netlib.blas.DGEMM;
import org.netlib.blas.DGEMV;
import org.netlib.blas.DSCAL;
import org.netlib.lapack.DGECON;
import org.netlib.lapack.DGELS;
import org.netlib.lapack.DGETRF;
import org.netlib.lapack.DGETRI;
import org.netlib.lapack.DLANGE;
import org.netlib.util.intW;

import com.approximatrix.draco.datamodel.StatTableModel;
import com.approximatrix.draco.fit.statistics.ExtendedStatistic;
import com.approximatrix.draco.general.ui.InfoWindow;
import com.approximatrix.draco.io.XMLio.AnalysisLoadStore;
import com.approximatrix.draco.math.MatrixSupport;
import com.approximatrix.draco.math.VectorSupport;
import com.approximatrix.draco.utility.ResultsUtilities;

/**
 * Class for performing simple least squares fits of data.
 * 
 * @author Jeffrey Armstrong
 */
public class RunLS implements LSFit {
	
	public static final int VAR_OLS = 1;
	public static final int VAR_WHITE = 2;
	
	public static final double MINIMUM_R_CONDITION = 1.0E-15;
	
	private static final int NUMBER_EXTENDED_STATISTICS = 1;
	public static final int EXTSTAT_DURBIN_WATSON = 0;
	
	private double[][] indep_data = null;
	private int[] indep_size = null;
	private double[] dep_data = null;
	
	private String[] indep_names = null;
	private String dep_name = null;
	
	// Results information
	private boolean success = false;
	private double[] coef = null;
	private double[] coef_var = null;
	private double[][] covariance_matrix = null;
	private double residualsumsq = 0.0;
	private double[] residuals = null;
	
	private int variance_type = 1;
	private boolean include_constant = true;
	
	private String failure_message = null;
	private String results = null;
	
	private boolean[] extended_statistics = null;
	
	private boolean ignore_conditioning = false;
	
	private StatTableModel base_model = null;
	
	/** The list of extended statistics to compute */
	private List<ExtendedStatistic> estats = null;
	
	/** Quick static interface for performing simple LS fits without the
	 * trouble of generating a StatTableModel.  Function will return either
	 * parameter estimates or null if the system fails.
	 * 
	 * @param independent independent array of data
	 * @param dependent dependent data
	 * @return the parameter estimates or null if an error occurred
	 */
	public static double[] SimpleLS(double[][] independent, double[] dependent) {
		if(independent == null || dependent == null) return null;
		
		intW status = new intW(0);
		double[] work = (double[])dependent.clone();
		
		double[][] solver_data = new double[dependent.length][independent[0].length+1];
		for(int i=0;i<dependent.length;i++) {
			solver_data[i][0] = 1.0;
			for(int j=0;j<independent[0].length;j++) {
				solver_data[i][j+1] = independent[i][j];
			}	
		}
		
		double[][] new_dep = new double[dependent.length][1];
		for(int i=0;i<dependent.length;i++) {
			new_dep[i][0] = dependent[i];
		}
		
		DGELS.DGELS("N",dependent.length,independent[0].length+1,1,solver_data,					
				new_dep,work,work.length,status);
		
		if(status.val == 0) {
			double[] results = new double[independent[0].length+1];
			for(int i=0;i<independent[0].length+1;i++)
				results[i] = new_dep[i][0];
			return results;
		} else
			return null;
	}
	
	/** Dummy constructor
	 * 
	 */
	public RunLS() {
		
		failure_message = "No data provided...";
		
		// Initialize extended statistics
		extended_statistics = new boolean[NUMBER_EXTENDED_STATISTICS];
		for(int i=0;i<NUMBER_EXTENDED_STATISTICS;i++) {
			extended_statistics[i] = false;
		}
		
	}
	
	/** Constructor for a RunLS model that executes the fit as soon as constructed
	 * 
	 * @param indep an array of column names that specify the independent variables
	 * @param dep the name of the column representing the dependent variable
	 * @param passed_model the StatTableModel in which the data resides
	 * @param in_variance_type integer specifying the variance calcualtion method
	 * @param calc_constant boolean stating whether to compute a constant during fit
	 */
	public RunLS(String[] indep, String dep, 
			     StatTableModel passed_model,
				 int in_variance_type,
				 boolean calc_constant){
		
		this();

		base_model = passed_model;
		
		indep_names = indep;
		dep_name = dep;
		
		variance_type = in_variance_type;
		include_constant = calc_constant;

	}
	
	/** Constructor for a RunLS model that executes the fit as soon as constructed
	 * 
	 * @param indep an array of column names that specify the independent variables
	 * @param dep the name of the column representing the dependent variable
	 * @param passed_model the StatTableModel in which the data resides
	 * @param in_variance_type integer specifying the variance calcualtion method
	 * @param calc_constant boolean stating whether to compute a constant during fit
	 * @paran ignore_condition boolean stating whether a conditioning check should be performed
	 */
	public RunLS(String[] indep, String dep, 
			     StatTableModel passed_model,
				 int in_variance_type,
				 boolean calc_constant,
				 boolean ignore_condition){
		
		this();

		base_model = passed_model;
		
		indep_names = indep;
		dep_name = dep;
		
		variance_type = in_variance_type;
		include_constant = calc_constant;
		
		ignore_conditioning = ignore_condition;
		
	}
	
	/** Performs actual regression based on data stored in the class
	 * 
	 * @return true for success, false otherwise (this.success is set internally)
	 */
	private boolean regress() {
		// Get the necessary data block
		builddata(base_model,dep_name,indep_names);
		
		indep_size = new int[2];
		indep_size[0] = dep_data.length;
		if(include_constant) {
			indep_size[1] = indep_names.length+1;
		} else {
			indep_size[1] = indep_names.length;
		}
		
		double[][] solver_data = null;
		if(include_constant) {
			solver_data = new double[dep_data.length][indep_names.length+1];
		} else {
			solver_data = new double[dep_data.length][indep_names.length];
		}
		
		for(int i=0;i<dep_data.length;i++) {
			if(include_constant) {
				solver_data[i][0] = 1.0;
			}
			for(int j=0;j<indep_names.length;j++) {
				if(include_constant) {
					solver_data[i][j+1] = indep_data[i][j];
				} else {
					solver_data[i][j] = indep_data[i][j];
				}
			}
			
		}
		
		// Change around dep_data
		double[][] new_dep = new double[dep_data.length][1];
		for(int i=0;i<dep_data.length;i++) {
			new_dep[i][0] = dep_data[i];
		}
		
		// Run the analysis
		//DGELS(String, int, int, int, double[][], double[][], double[], int, intW)
		double[] work = (double[])dep_data.clone();
		//work = new double[512];

		double rcond = reciprocal_condition();
		
		//System.out.println();
		
		if(rcond > MINIMUM_R_CONDITION || ignore_conditioning) {
		
			intW result = new intW(0);
			
			DGELS.DGELS("N",dep_data.length,indep_size[1],1,solver_data,					
					new_dep,work,work.length,result);
		
			success = (result.val == 0);
		
			coef = new double[indep_size[1]];
			for(int i=0;i<indep_size[1];i++) {
				coef[i] = new_dep[i][0];
			}
		
			residualsumsq = 0.0;
			for(int i=indep_size[1];i<dep_data.length;i++) {
				residualsumsq = residualsumsq + new_dep[i][0]*new_dep[i][0];
			}			
		} else {
			failure_message = "Bad conditioning of independent data matrix\nRCOND="+(new Double(rcond).toString()); 
			success = false;				
		}
		
		return success;
	}
	
	/** Builds valid double arrays for use by the LS engine, eleiminating any rows
	 * where the model contains null data.
	 * 
	 * @param model the model to extract data from
	 * @param dependent the name of the dependent column
	 * @param independent the names of the independent columns in an array of Strings
	 */
	private void builddata(StatTableModel model, String dependent, String[] independent) {
		
		String[] all_data = new String[independent.length+1];
		all_data[0] = dependent;
		for(int i=0;i<independent.length;i++)
			all_data[i+1] = independent[i];
		
		int[] use_rows = model.getNonNullRows(all_data);
		
		dep_data = model.getDataDouble(use_rows,model.getColumnNumber(dependent));
		indep_data = model.getDataDouble(use_rows,model.getColumnNumber(independent));
		
	}
	
	/** Determines if a specified index is present in an array of integers
	 * 
	 * @param index the index of interest
	 * @param avoid the array of unallowables, or null if none
	 * @return true if index does NOT appear in avoid, false otherwise
	 */
	private boolean isValidIndex(int index, int[] avoid) {
		if(avoid == null) return true;
		
		boolean return_val = true;
		for(int i=0;i<avoid.length;i++) {
			if(index == avoid[i]) {
				return_val = false;
				break;
			}
		}
		return return_val;
	}
	
	/** Merges integer arrays of null indices automatically
	 * 
	 * @param existing the original data
	 * @param newdata the data to merge
	 * @return the merged arrays with no duplicate entries
	 */
	private int[] mergeNullIndices(int[] existing, int[] newdata) {
		if(existing == null) 
			return newdata;
		else
			if(newdata == null) return existing;
		
		// Determine the number of entries first
		int count_size = existing.length;
		ArrayList newdata_indices = new ArrayList();
		for(int i=0;i<newdata.length;i++) {
			boolean add_one = true;
			for(int j=0;j<existing.length;j++) {
				if(newdata[i] == existing[j]) {
					add_one = false;
					break;
				}
			}
			if(add_one) {
				count_size++;
				newdata_indices.add(new Integer(newdata[i]));
			}
		}
		
		// Create the return value
		int[] return_val = new int[count_size];
		for(int i=0;i<existing.length;i++) {
			return_val[i] = existing[i];
		}
		int new_count = 0;
		for(int i=0;i<newdata_indices.size();i++) {
			return_val[existing.length+i] = ((Integer)newdata_indices.get(i)).intValue();
		}
		
		return return_val;
	}
	
	/** Returns an array of integers that specify the locations in a model column
	 * where the data is null.
	 * 
	 * @param model the valid StatTableModel
	 * @param column the column name to search
	 * @return the array of indices with null data, or null if all data is valid
	 */
	private int[] getNullIndices(StatTableModel model, String column) {
		ArrayList skips = new ArrayList();
		
		Object[] column_data = model.getColumn(model.findColumn(column));
		
		for(int i=0;i<column_data.length;i++) {
			if(column_data == null) skips.add(new Integer(i));
		}
		
		if(skips.size() == 0) return null;
		
		int[] return_val = new int[skips.size()];
		for(int i=0;i<skips.size();i++) {
			return_val[i] = ((Integer)skips.get(i)).intValue();
		}
		return return_val;
	}
	
	private double[][] generate_X_matrix() {
		double[][] solver_data = new double[indep_size[0]][indep_size[1]];
		
		for(int i=0;i<indep_size[0];i++) {
			if(include_constant) {
				solver_data[i][0] = 1.0;
				for(int j=0;j<indep_size[1]-1;j++) {
					solver_data[i][j+1] = indep_data[i][j];
				}
			} else {
				for(int j=0;j<indep_size[1];j++) {
					solver_data[i][j] = indep_data[i][j];
				}
			}
							
		}			
		return solver_data;
	}
	
	public String getResults() {
		if(results == null) {
			StringBuffer text = null;
			if(!success) {
				text = LSFitResultsGen.GenerateFailureResults(this,null,"Results of the Multiple Regression Model\n");
			} else {
				text = LSFitResultsGen.GenerateResults(this,"Results of the Multiple Regression Model\n");
				print_special_options(text);
				checkExtendedOutput(text);
			}
			results = text.toString();
		}
		return results;
	}
	
	public String getFailureMessage() {
		if(!success)
			return failure_message;
		else
			return null;
	}
	
	private void print_special_options(StringBuffer current) {
		if(variance_type != VAR_OLS) {
			switch(variance_type) {
				case VAR_WHITE:
					current.append("White's Robust Standard Errors used in calculations\n");
			}
			current.append(ResultsUtilities.SeparatorLine());
		}
	}
	
	private void checkExtendedOutput(StringBuffer current) {
		// Generate the extended statisics
		SupportStatistics stats = new SupportStatistics(indep_data,
														indep_size,
														dep_data,
														covariance_matrix,
														coef,
														generate_residuals());
		boolean ext_start = false;
		
		for(int i=0;i<NUMBER_EXTENDED_STATISTICS;i++) {
			if(extended_statistics[i]) {
				if(!ext_start) {
					current.append(ResultsUtilities.SeparatorLine());
					current.append("Extended Statistics:\n");
					ext_start = true;
				}
				current.append(getExtendedStatisticText(i));
				current.append(": ");
				current.append(ResultsUtilities.OutNumber(getExtendedStatisticValue(i,stats)));
				current.append("\n");
			}
		}		
	}

	public double[] generate_data_from_fit(double[] incoming_x) {
		double[] holding = new double[incoming_x.length];
		for(int i = 0;i<incoming_x.length;i++) {
			holding[i] = coef[0] + coef[1]*incoming_x[1];
		}
		return holding;
	}
	
	public double[] generate_data_from_fit() {			
		double[] return_val = VectorSupport.VectorOfOnes(dep_data.length);
		DGEMV.DGEMV("N",dep_data.length,coef.length,1.0,generate_X_matrix(),coef,1,0.0,return_val,1);
		return return_val;
	}
	
	public String get_dependent_name() {
		return dep_name;
	}
	
	public String[] get_independent_names() {
		return indep_names;
	}
	
	public double residual_variance() {
		return residualsumsq/(dep_data.length-coef.length);
	}
	
	private double reciprocal_condition() {
		// The result matrix
		double[][] temp = MatrixSupport.Identity(indep_size[1]);
		
		// The independent variable matrices
		double[][] X = generate_X_matrix();				
		
		// Matrix multiply to determine (X'X)
		DGEMM.DGEMM("T","N",indep_size[1],indep_size[1],indep_size[0],
				1.0,X,X,0.0,temp);
				
		return MatrixSupport.GetRecipricolCondition(temp);
	}
	
	public void calculate_coef_variances() {

		// The result matrix
		double[][] temp = MatrixSupport.Identity(indep_size[1]);
		
		// The independent variable matrices
		double[][] X = generate_X_matrix();				
		
		// Matrix multiply to determine (X'X)
		DGEMM.DGEMM("T","N",indep_size[1],indep_size[1],indep_size[0],
				1.0,X,X,0.0,temp);
				
		int[] ipiv = new int[indep_size[1]];
		double[] work = new double[indep_size[1]];
		intW result = new intW(0);
		
		// Together, these invert a matrix (temp)
		DGETRF.DGETRF(indep_size[1],indep_size[1],temp,ipiv,result);
		DGETRI.DGETRI(indep_size[1],temp,ipiv,work,work.length,result);
		
		// Multiply by residual variance
		if(variance_type == VAR_OLS) {
			MatrixSupport.ScalarTimesMatrix(residual_variance(),temp,indep_size[1],indep_size[1]);
		} else if(variance_type == VAR_WHITE) {				
			temp = calculateWhiteVCVMatrix(temp);
		}
			
		// Store results
		coef_var = new double[coef.length];
		for(int i=0;i<coef.length;i++) {
			coef_var[i] = temp[i][i];
		}
		covariance_matrix = temp;
	}
	
	/** Calculates White's Covariance matrix
	 *  using B = X'ee'X
	 * @return
	 */
	private double[][] calculateWhiteVCVMatrix(double[][] XpX) {
		
		// The independent variable matrices
		double[][] X = generate_X_matrix();
		
		// Need to calculate residuals
		double[] residuals = generate_residuals();
		
		// Calculate ee'
		double[][] ee = MatrixSupport.MatrixofZeros(indep_size[0]);
		
		// Apparently White's estimate is way easier than we thought
		/* DGER.DGER(indep_size[0],indep_size[0],1.0,residuals,1,residuals,1,ee);
		for(int i=0;i<residuals.length;i++) {
			System.out.println(ee[i][i]);
		}*/
		
		ee = MatrixSupport.MatrixWithDiagonal(VectorSupport.MultiplyElements(residuals,residuals));			
		
		// Must calculate (X'X)^-1 * XomegaX * (X'X)^-1
		double[][] temp = MatrixSupport.MatrixofZeros(indep_size[1],indep_size[0]);
		double[][] temp2 = MatrixSupport.MatrixofZeros(indep_size[1],indep_size[0]);
		DGEMM.DGEMM("N","T",indep_size[1],indep_size[0],indep_size[1],
				1.0,XpX,X,0.0,temp);
		
		DGEMM.DGEMM("N","N",indep_size[1],indep_size[0],indep_size[0],
				1.0,temp,ee,0.0,temp2);
		
		temp = MatrixSupport.MatrixofZeros(indep_size[1],indep_size[1]);
		DGEMM.DGEMM("N","N",indep_size[1],indep_size[1],indep_size[0],
				1.0,temp2,X,0.0,temp);
		
		temp2 = MatrixSupport.MatrixofZeros(indep_size[1],indep_size[1]);
		DGEMM.DGEMM("N","N",indep_size[1],indep_size[1],indep_size[1],
				1.0,temp,XpX,0.0,temp2);
		
		return temp2;
	}
	
	public double[] generate_residuals() {
		double[] return_val = (double[])dep_data.clone();
		DGEMV.DGEMV("N",dep_data.length,coef.length,-1.0,generate_X_matrix(),coef,1,1.0,return_val,1);
		return return_val;
	}
	
	public double[][] getCovarianceMatrix() {
		if(covariance_matrix == null) {
			calculate_coef_variances();
		}
		return covariance_matrix;
	}
	
	public void setIndependentColumns(String[] col_names) {
		indep_names = col_names;
	}
	
	public String[] getVariableNames() {
		return indep_names;
	}
	
	public double[] getCoefTScores() {
		if(coef_var == null) {
			calculate_coef_variances();
		}
		return VectorSupport.DivideElements(coef,
				  VectorSupport.SqrtElements(coef_var));
		//return coef;
	}
	
	public double getCoefTScore(int i) {
		
		return getCoefTScores()[i];
	}
	
	public double[] getCoef() {
		return coef;
	}
	
	public boolean includesConstant() {
		return include_constant;
	}
	
	public String[] getCoefLabels() {
		String[] vars = getIndependentVariables();
		String[] return_val = null;
		int start = 0;
		if(include_constant) {
			return_val = new String[vars.length + 1];
			return_val[0] = "Constant";
			start = 1;
		} else {
			return_val = new String[vars.length];
		}
		
		for(int i=0;i<vars.length;i++) {
			StringBuffer sb = new StringBuffer("Coef ");
			sb.append(i);
			sb.append(" (");
			sb.append(vars[i]);
			sb.append(")");
			return_val[i+start] = sb.toString();
		}
		
		return return_val;
	}
	
	public double getFitStdError() {
		return Math.sqrt(residualsumsq/((double)(dep_data.length-coef.length)));
	}
	
	public double getResidualSumSqared() {
		return residualsumsq;
	}
	
	
	/** Sets an extended statistic to be shown in results
	 * 
	 * @param statistic the enumerated integer specifying the desired statistic
	 * @param set_value true to display the statistic, false otherwise
	 */
	public void setExtendedStatistic(int statistic, boolean set_value) {
		if(statistic < 0 || statistic >=NUMBER_EXTENDED_STATISTICS) {
			System.err.println("Error: unsupported statistic requested");
			return;
		}
		extended_statistics[statistic] = set_value;
	}
	
	/** Returns whether or not an extended statistic will be printed
	 * 
	 * @param statistic the enumerated integer specifying the desired statistic
	 * @return true if computed, false otherwise
	 */
	public boolean getExtendedStatistic(int statistic) {
		if(statistic < 0 || statistic >=NUMBER_EXTENDED_STATISTICS) {
			System.err.println("Error: unsupported statistic requested");
			return false;
		}
		return extended_statistics[statistic];
	}
	
	/** Returns the string description of a specified extended statistic
	 * 
	 * @param statistic the enumerated integer specifying the desired statistic 
	 * @return the String describingthe statistic
	 */
	public String getExtendedStatisticText(int statistic) {
		if(statistic < 0 || statistic >=NUMBER_EXTENDED_STATISTICS) {
			System.err.println("Error: unsupported statistic requested");
			return null;
		}
		String return_val = null;
		switch(statistic){
			case EXTSTAT_DURBIN_WATSON:
				return_val = new String("Durbin-Watson Statistic");
				break;
		}
		return return_val;
	}
	
	/** Returns the double value of a desired statistic
	 * 
	 * @param statistic the enumerated integer specifying the desired statistic 
	 * @param stats a valid SupportStatistics object
	 * @return the value of the statistic, or NaN if not valid
	 */
	public double getExtendedStatisticValue(int statistic, SupportStatistics stats) {
		if(statistic < 0 || statistic >=NUMBER_EXTENDED_STATISTICS) {
			System.err.println("Error: unsupported statistic requested");
			return Double.NaN;
		}
		double return_val = Double.NaN;
		switch(statistic){
			case EXTSTAT_DURBIN_WATSON:
				return_val = stats.getDurbinWatsonStatistic();
				break;
		}
		return return_val;
	}
	
	private class SupportStatistics {
		
		private double[][] indep_data = null;
		private int[] indep_size = null;
		private double[] dep_data = null;
		
		double[][] covariance_matrix = null;
		double[] fit_coefficients = null;
		double[] residuals = null;
		
		public SupportStatistics(double[][] indep,
								 int[] insize,
								 double[] dep,
								 double[][] cov,
								 double[] coef,
								 double[] inresiduals) {
			indep_data = indep;
			indep_size = insize;
			dep_data = dep;
			covariance_matrix = indep;
			fit_coefficients = coef;
			residuals = inresiduals;
		}
		
		public double getDurbinWatsonStatistic() {
			double numerator = 0.0;
			double denom = 0.0;
			
			
			denom = residuals[0]*residuals[0];
			for(int i=1;i<residuals.length;i++) {
				numerator = numerator +
					(residuals[i]-residuals[i-1])*
					(residuals[i]-residuals[i-1]);
				
				denom = denom + residuals[i]*residuals[i];
			}
			
			if(denom != 0.0) {
				return numerator/denom;
			} else {
				return -1.0;
			}
			
		}
		
		public double getDurbinHStatistic(int lag_variable_index) {
			return (1.0-getDurbinWatsonStatistic()/2.0)*
				Math.sqrt((double)dep_data.length/
						(1.0-((double)dep_data.length)*covariance_matrix[lag_variable_index][lag_variable_index]));
		}

	}
	
	public String getDependentVariable() {
		return dep_name;
	}
	
	public String[] getIndependentVariables() {
		return indep_names;
	}
	
	public double[] getResiduals() {
		if(residuals==null)
			residuals = generate_residuals();
		return residuals;
	}

	public double[][] getIndependentData() {
		return indep_data;
	}
	
	public double[] getDependentData() {
		return dep_data;
	}
	
	public double[][] getXMatrix() {
		return generate_X_matrix();
	}
	
	public double getTotalSumSqared() {
		double mean = VectorSupport.VectorElementSum(dep_data)/
	       (new Double(dep_data.length).doubleValue());
		double[] ones = VectorSupport.VectorOfOnes(dep_data.length);

		DSCAL.DSCAL(ones.length,-1.0*mean,ones,1);
		DAXPY.DAXPY(ones.length,1.0,dep_data,1,ones,1);
		ones = VectorSupport.MultiplyElements(ones,ones);
		return VectorSupport.VectorElementSum(ones);	
	}
	
	public double[] getCoefVariances() {
		if(coef_var == null) {
			calculate_coef_variances();
		}
		return coef_var;
	}
	
	public boolean isSuccessful() {
		return success;
	}

	public Number[] getFittedData() {
		if(base_model != null) {
			return LSFitResultsGen.GetEstimates(this, base_model);
		} else {
			return null;
		}
	}
	
	public Double[] getResidualData() {
		if(base_model != null) {
			return LSFitResultsGen.GetResidualsFromEstimates(this, base_model);
		} else {
			return null;
		}
	}
	
	/** Returns the original model used in the fit
	 * 
	 * @return the unmodified StatTableModel
	 */
	public StatTableModel getModel() {
		return base_model;
	}
	
	/** Sets all data in the fit to the information loaded from
	 * a file
	 * 
	 * @param data a valid AnalysisLoadStore object that has been properly filled
	 * @param model the model with which this fit is associated
	 */
	public void setLoadedAnalysis(AnalysisLoadStore data, StatTableModel model) {
		this.base_model = model;
		
		this.coef = data.getCoef();
		this.covariance_matrix = data.getCovarianceMatrix();
		this.dep_name = data.getDependentVariable();
		this.indep_names = data.getIndependentVariables();
		this.coef_var = MatrixSupport.GetDiagonal(this.covariance_matrix);
		this.residuals = data.getResiduals();
		this.results = data.getResults();
		
		// Include a constant?
		this.include_constant = (this.covariance_matrix.length > this.indep_names.length);
		
		this.success = true;
		
		// Now set some selections in the model table
		
			
	}

	/** This is simply a wrapper for regress();
	 */
	public void perform() {
		System.out.println("Performing...");
		regress();
	}

	/** This method computes and returns the extended statistics
	 * 
	 * @see com.approximatrix.draco.fit.LSFit#getExtendedStatistics()
	 */
	public List<ExtendedStatistic> getExtendedStatistics() {
		if(estats == null) return null;
		for(ExtendedStatistic stat : estats) {
			stat.setAnalysis(this);
			stat.compute();
		}
		return estats;
	}

	/** Sets the list of extended statistics
	 * @see com.approximatrix.draco.fit.LSFit#setExtendedStatistics(java.util.List)
	 */
	public void setExtendedStatistics(List<ExtendedStatistic> statistics) {
		estats = statistics;
	}
}
