/*
 *  Draco Econometrics
 *  Copyright (C) 2008-2009 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 static org.junit.Assert.*;
import org.junit.Test;

import com.approximatrix.draco.datamodel.StatTableModel;

public class RunNLLSTest extends RegressionTestSkeleton  {

	/** A weaker delta is used since this is an imperfect calculation */
	private static final double DELTA_NLLS = 1.0E-5;
	
	/** An even weaker delta is utilized for the error term... */
	private static final double DELTA_NLLS_BIG = 1.0E-3; // 0.1%
	
	/** An even weaker delta is utilized for the Kirby2 test... */
	private static final double DELTA_NLLS_HUGE = 0.05; // 5%
	
	/** A "just similar" test delta for Kirby2 variances */
	private static final double DELTA_NLLS_SIMILAR = 0.25; // 25%
	
	@Test
	public void testRunNLLSNoInt1() {
		String equation = "alpha*x";
		String[] parameters = new String[1]; parameters[0] = "alpha";
		double[] estimates = new double[1];  estimates[0] = 1.75;
		
		RunNLLS analysis = new RunNLLS(super.noint1,"y",equation,parameters,estimates,1.0);
		analysis.perform();
		
		assertTrue(analysis.success);
		
		double[] coef = analysis.getCoef();
		assertCloseTo(2.07438016528926,coef[0],DELTA_NLLS);
		
		double[] var = analysis.getCoefVariances();
		assertCloseTo(0.165289256198347E-01*0.165289256198347E-01,var[0],DELTA_NLLS);
	}
	
	@Test
	public void testRunNLLSBadEqn() {
		String equation = "alpha*[x";
		String[] parameters = new String[1]; parameters[0] = "alpha";
		double[] estimates = new double[1];  estimates[0] = 1.75;
		
		RunNLLS analysis = new RunNLLS(super.noint1,"y",equation,parameters,estimates,1.0);
		analysis.perform();
		
		assertFalse(analysis.success);
		assertNotNull(analysis.getFailureMessage());
	}
	
	@Test
	public void testRunNLLSBadParameter() {
		String equation = "alpha*x+beta";
		String[] parameters = new String[1]; parameters[0] = "alpha";
		double[] estimates = new double[1];  estimates[0] = 1.75;
		
		RunNLLS analysis = new RunNLLS(super.noint1,"y",equation,parameters,estimates,1.0);
		analysis.perform();
		
		assertFalse(analysis.success);
		assertNotNull(analysis.getFailureMessage());
	}
	
	@Test
	public void testRunNLLSBadDataSet() {
		String equation = "alpha*ser0";
		String[] parameters = new String[1]; parameters[0] = "alpha";
		double[] estimates = new double[1];  estimates[0] = 1.75;
		
		RunNLLS analysis = new RunNLLS(new StatTableModel(),"ser1",equation,parameters,estimates,1.0);
		analysis.perform();
		
		assertFalse(analysis.success);
		assertNotNull(analysis.getFailureMessage());
	}
	
	@Test
	public void testRunNLLSMisra1a() {
		String equation = "a*(1.0-exp(-1.0*b*x))";
		String[] parameters = new String[2]; parameters[0] = "a"; parameters[1] = "b";
		double[] estimates = new double[2];  estimates[0] = 250.0; estimates[1] = 0.0005;
		
		RunNLLS analysis = new RunNLLS(super.misra1a,"y",equation,parameters,estimates,1.0);
		analysis.perform();
		
		assertTrue(analysis.success);
		
		double[] coef = analysis.getCoef();
		assertCloseTo(2.3894212918E+02,coef[0],DELTA_NLLS);
		assertCloseTo(5.5015643181E-04,coef[1],DELTA_NLLS);
		
		// This calculation is particularly nasty - bigger delta ia alright here...
		double[] var = analysis.getCoefVariances();
		assertCloseTo(2.7070075241E+00*2.7070075241E+00,var[0],DELTA_NLLS_BIG);
		assertCloseTo(7.2668688436E-06*7.2668688436E-06,var[1],DELTA_NLLS_BIG);
	}
	
	@Test
	public void testRunNLLSKirby2() {
		String equation = "(b1 + b2*x + b3*x*x) / (1 + b4*x + b5*x*x) ";
		String[] parameters = new String[5]; 
		parameters[0] = "b1"; 
		parameters[1] = "b2";
		parameters[2] = "b3";
		parameters[3] = "b4";
		parameters[4] = "b5";
		
		double[] estimates = new double[5];  
		estimates[0] = 2.0;
		estimates[1] = -0.1;
		estimates[2] = 0.003;
		estimates[3] = -0.001;
		estimates[4] = 0.00001;
		
		RunNLLS analysis = new RunNLLS(super.kirby2,"y",equation,parameters,estimates,1.0);
		analysis.perform();
		
		assertTrue(analysis.success);
		
		double[] coef = analysis.getCoef();
		assertCloseTo(1.6745063063E+00,coef[0],DELTA_NLLS_HUGE);
		assertCloseTo(-1.3927397867E-01,coef[1],DELTA_NLLS_HUGE);
		assertCloseTo(2.5961181191E-03,coef[2],DELTA_NLLS_HUGE);
		assertCloseTo(-1.7241811870E-03,coef[3],DELTA_NLLS_HUGE);
		assertCloseTo(2.1664802578E-05,coef[4],DELTA_NLLS_HUGE);
		
		double[] var = analysis.getCoefVariances();
		assertCloseTo(8.7989634338E-02,Math.sqrt(var[0]),DELTA_NLLS_SIMILAR);
		assertCloseTo(4.1182041386E-03,Math.sqrt(var[1]),DELTA_NLLS_SIMILAR);
		assertCloseTo(4.1856520458E-05,Math.sqrt(var[2]),DELTA_NLLS_SIMILAR);
		// The final two will not match...
		//assertCloseTo(5.8931897355E-05,Math.sqrt(var[3]),DELTA_NLLS_SIMILAR);
		//assertCloseTo(2.0129761919E-07,Math.sqrt(var[4]),DELTA_NLLS_SIMILAR);
		
		// The variance differences are exacerbated by the fact that the estimates
		// are not particularly close in this test.  The test does show, however,
		// that the LM method does converge.
	}

	@Test
	public void testRunNLLSBennett5() {
		String equation = "b1 * (b2+x)^(-1.0/b3)";
		
		String[] parameters = new String[3]; 
		parameters[0] = "b1"; 
		parameters[1] = "b2";
		parameters[2] = "b3";
		
		double[] estimates = new double[3];  
		estimates[0] = -2000.0;
		estimates[1] = 50.0;
		estimates[2] = 0.8;
		
		RunNLLS analysis = new RunNLLS(super.bennett5,"y",equation,parameters,estimates,1.0);
		analysis.perform();
		
		assertTrue(analysis.success);
		
		double[] coef = analysis.getCoef();
		assertCloseTo(-2.5235058043E+03,coef[0],DELTA_NLLS_BIG);
		assertCloseTo(4.6736564644E+01,coef[1],DELTA_NLLS_BIG);
		assertCloseTo(9.3218483193E-01,coef[2],DELTA_NLLS_BIG);
		
		double[] var = analysis.getCoefVariances();
		assertCloseTo(2.9715175411E+02,Math.sqrt(var[0]),DELTA_NLLS_BIG);
		assertCloseTo(1.2448871856E+00,Math.sqrt(var[1]),DELTA_NLLS_BIG);
		assertCloseTo(2.0272299378E-02,Math.sqrt(var[2]),DELTA_NLLS_BIG);

	}
	
}
