/*
 * $Id: apprgrdn.c,v 1.1 2007/03/16 21:58:45 gfuer Exp $
 *
 * SOLVOPT version 1.1 (June, 1997)
 * by Alexei Kuntsevich and Franz Kappel
 * University of Graz, Austria
 *
 * http://www.uni-graz.at/imawww/kuntsevich/solvopt
 *
 * Modified by Gerhard Fuernkranz for use in lprof
 *
 * With the kind permisson of the authors, SolvOpt is distributed
 * as part of lprof under the terms of the GNU GPL.
 *
 *	Lieber Herr Fürnkranz,
 *	Ihre Frage bezüglich der Nutzung von SolvOpt kann ich mit "ja"
 *	beantworten. Wir haben den Begriff "freeware" in diesem Sinne
 *	verstanden.
 *
 *	Mit freundlichen Grüßen,
 *	F. Kappel 
 *
 *	-----Ursprüngliche Nachricht-----
 *	Von: gfuer@web.de [mailto:gfuer@web.de] 
 *	Gesendet: Dienstag, 13. März 2007 23:10
 *	An: Kappel, Franz (franz.kappel@uni-graz.at)
 *	Betreff: Frage zur SolvOpt-Nutzung
 *
 *	Sehr geehrter Herr Kappel,
 *
 *	auf der Solvopt-Home-Page schreiben Sie "SolvOpt is a freeware...". Da
 *	"Freeware" jedoch kein genau definierter, rechtsgültiger Begriff ist,
 *	und ich auch in den Sourcen keine Copyright-Vermerke und/oder
 *	Nutzungsbedingungen finde, möchte ich gerne bei Ihnen explizit anfragen,
 *	ob Sie zustimmen würden, dass der SolvOpt-Quellcode unter den
 *	Bedingungen der GNU GPL benutzt und weiterverteilt werden darf, sodass
 *	eine Integration mit anderen freien Softwareprogrammen, die ebenfalls
 *	der GNU GPL untestellt oder GLP-kompatibel sind, ermöglicht wird.
 *
 *	Mit freundlichen Grüßen,
 *	Gerhard Fürnkranz
 *
 * THIS SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
 *
 * IN NO EVENT SHALL MARTI MARIA BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 * OF THIS SOFTWARE.
 *
 * This file 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.
 */

#include <stdlib.h>
#include <math.h>

#include "solvopt.h"

#define max(a,b) ((a) > (b) ? (a) : (b))
#define min(a,b) ((a) < (b) ? (a) : (b))

void
cmsxApprGrdn(void *cargo, unsigned short n,
             double g[], double x[], double f,
             double fun (void *, double[]),
             double deltax[], unsigned obj)
{

/*
   Function APPRGRDN performs the finite difference approximation 
   of the gradient <g> at a point <x>.
   f      is the calculated function value at a point <x>,
   <fun>  is the name of a function that calculates function values,
   deltax is an array of the relative stepsizes.
   obj    is the flag indicating whether the gradient of the objective
          function (1) or the constraint function (0) is to be calculated. 
*/
    double const lowbndobj = 2.0e-10, lowbndcnt = 5.0e-15,
                 ten = 10.0, half = 0.5;

    double d, y, fi;
    unsigned short i, j, center = 0;

    for (i = 0; i < n; i++) {
	y = x[i];
	d = max (lowbndcnt, fabs (y));
	d *= deltax[i];
	if (obj) {
	    if (fabs (d) < lowbndobj) {
		if (deltax[i] < 0.0)
		    d = -lowbndobj;
		else
		    d = lowbndobj;
		center = 1;
	    }
	    else
		center = 0;
	}
	else if (fabs (d) < lowbndcnt) {
	    if (deltax[i] < 0.0)
		d = -lowbndcnt;
	    else
		d = lowbndcnt;
	}
	x[i] = y + d;
	fi = fun (cargo, x);
	if (obj) {
	    if (fi == f) {
		for (j = 1; j <= 3; j++) {
		    d *= ten;
		    x[i] = y + d;
		    fi = fun (cargo, x);
		    if (fi != f)
			break;
		}
	    }
	}
	g[i] = (fi - f) / d;
	if (obj) {
	    if (center) {
		x[i] = y - d;
		fi = fun (cargo, x);
		g[i] = half * (g[i] + (f - fi) / d);
	    }
	}
	x[i] = y;
    }
}

