// Copyright (C) 2004   Michael Creel   <michael.creel@uab.es>
//
//  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 2 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, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 

// ========================= finitedifference ==========================
//  finite differences for numeric differentiation
#include <oct.h>
#include <float.h>


static bool
any_bad_argument(const octave_value_list& args)
{
	if (!args(0).is_real_scalar())
	{
		error("finitedifference: first argument must be a real double value");
		return true;
	}
	
	int tmp = args(1).int_value();
	if (error_state || (!(tmp==1 || tmp==2)))
	{
		error("finite difference: 2nd argument must be 1 or 2");
		return true;
	}
	
	return false;
}



DEFUN_DLD(finitedifference, args, ,"finitedifference,\n\
for internal use by numgradient and numhessian")
{
	int nargin = args.length ();
	if (!(nargin == 2))
	{
		error("finitedifference: you must supply exactly 2 arguments");
		return octave_value_list();
	}
	
	// check the arguments
	if (any_bad_argument(args)) return octave_value_list();
	
	double x = args(0).double_value();
	int order = args(1).int_value();
	int test;
	double eps, SQRT_EPS, DIFF_EPS, DIFF_EPS1, DIFF_EPS2, diff, d;
	
	eps = DBL_EPSILON; // machine precision
	SQRT_EPS = sqrt(eps);
	DIFF_EPS = exp(log(eps)/2);
	DIFF_EPS1 = exp(log(eps)/3);
	DIFF_EPS2 = exp(log(eps)/4);
	if (order == 0) diff = DIFF_EPS;
	else if (order == 1) diff = DIFF_EPS1;
	else diff = DIFF_EPS2;
	test = (fabs(x) + SQRT_EPS) * SQRT_EPS > diff;
	if (test) d = (fabs(x) + SQRT_EPS) * SQRT_EPS;
	else d = diff;	
	return octave_value(d);
}


syntax highlighted by Code2HTML, v. 0.9.1