
/*
 * Copyright 1999 Graeme W. Gill
 * All rights reserved.
 *
 * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
 * see the License.txt file for licencing details.
 */

/* Example use of dnsqe()                                                  */
/*                                                                         */
/*       The problem is to determine the values of X(1), X(2), ..., X(9),  */
/*       which solve the system of tridiagonal equations                   */
/*                                                                         */
/*       (3-2*X(1))*X(1)           -2*X(2)                   = -1          */
/*               -X(I-1) + (3-2*X(I))*X(I)         -2*X(I+1) = -1, I=2-8   */
/*                                   -X(8) + (3-2*X(9))*X(9) = -1          */
/*                                                                         */
/*       Final approximate solution:                                       */
/*                                                                         */
/*       -0.5706545E+00                                                    */
/*       -0.6816283E+00                                                    */
/*       -0.7017325E+00                                                    */
/*       -0.7042129E+00                                                    */
/*       -0.7013690E+00                                                    */
/*       -0.6918656E+00                                                    */
/*       -0.6657920E+00                                                    */
/*       -0.5960342E+00                                                    */
/*       -0.4164121E+00                                                    */

#include "numlib.h"

/* Compute norm of a vector */
static double denorm(int n, double *x);

int fcn(void *fdata, int n, double *x, double *fvec, int iflag);

double expect[9] = {
       -0.5706545E+00,
       -0.6816283E+00,
       -0.7017325E+00,
       -0.7042129E+00,
       -0.7013690E+00,
       -0.6918656E+00,
       -0.6657920E+00,
       -0.5960342E+00,
       -0.4164121E+00 };

int main(void)
{
	int n = 9 /* 9 */;			/* Problem vector size */
	double x[9];		/* Function input values */
	double fvec[9];		/* Function output values */
	double ss;			/* Search area */
	int info, j;
	double fnorm;
	int nprint = 0;		/* Itteration debugging print = off */
	double tol;

	error_program = "dnsqtest";	/* Set global error reporting string */

	/*	 Driver for dnsqe example. */
	/* Not supplying Jacobian, use approximation */

	/*	 The following starting values provide a rough solution. */
	for (j = 1; j <= 9; ++j) {
		x[j - 1] = -1.f;
	}
	ss = 0.1;

	nprint = 0;

	/*	 Set tol to the square root of the machine precision. */
	/*	 Unless high precision solutions are required, */
	/*	 this is the recommended setting. */

	tol = sqrt(M_DIVER);

	info = dnsqe(NULL, fcn, NULL, n, x, ss, fvec, tol, tol, 0, nprint);
	fnorm = denorm(n, fvec);
	fprintf(stdout,"Final L2 norm of the residuals = %e\n",fnorm);
	fprintf(stdout,"Exit return value = %d (1 = sucess)\n",info);
	fprintf(stdout,"Final approximate solution:\n");
	for (j = 0; j < n; j++) {
		fprintf(stdout,"x[%d] = %f, expect %f\n",j,x[j], expect[j]);
	}

	return 0;
} /* main() */

/* Function being solved */
int fcn(void *fdata, int n, double *x, double *fvec, int iflag)
{
	double temp, temp1, temp2;
	int k;

	/* Function Body */
	for (k = 0; k < n; ++k) {
		temp = (3.0 - 2.0 * x[k]) * x[k];
		temp1 = 0.0;
		if (k != 0) {
			temp1 = x[k-1];
		}
		temp2 = 0.0;
		if (k != ((n)-1))
			temp2 = x[k+1];
		fvec[k] = temp - temp1 - 2.0 * temp2 + 1.0;
		if (iflag == 0)
			printf("x[%d] = %f, fvec[%d] + %f\n",k,x[k],k,fvec[k]);

#ifdef DEBUG
printf("~~ x[%d] = %f, fvec[%d] + %f\n",k,x[k],k,fvec[k]);
#endif /* DEBUG */
	}
	/* Return < 0 to abort */
	return 0;
}

/* - - - - - - - - - - - - - - - - - - - */

static double denorm(
	int n,			/* Size of x[] */
	double x[])		/* Input vector */
{
	/* Initialized data */
	static double rdwarf = 3.834e-20;
	static double rgiant = 1.304e19;

	/* Local variables */
	static double xabs, x1max, x3max;
	static int i;
	static double s1, s2, s3, agiant, floatn;
	double ret_val, td;

	s1 = 0.0;	/* Large component */
	s2 = 0.0;	/* Intermedate component */
	s3 = 0.0;	/* Small component */
	x1max = 0.0;
	x3max = 0.0;
	floatn = (double) (n + 1);
	agiant = rgiant / floatn;
	for (i = 0; i < n; i++) {
		xabs = (td = x[i], fabs(td));

		/* Sum for intermediate components. */
		if (xabs > rdwarf && xabs < agiant) {
			td = xabs;				 	/* Computing 2nd power */
			s2 += td * td;

		/* Sum for small components. */
		} else if (xabs <= rdwarf) {
			if (xabs <= x3max) {
				if (xabs != 0.0) {		/* Computing 2nd power */
				td = xabs / x3max;
				s3 += td * td;
				}
			} else { /* Computing 2nd power */
				td = x3max / xabs;
				s3 = 1.0 + s3 * (td * td);
				x3max = xabs;
			}

		/* Sum for large components. */
		} else {
			if (xabs <= x1max) {		/* Computing 2nd power */
				td = xabs / x1max;
				s1 += td * td;
			} else {					/* Computing 2nd power */
				td = x1max / xabs;
				s1 = 1.0 + s1 * (td * td);
				x1max = xabs;
			}
		}
	}

	/* Calculation of norm. */
	if (s1 != 0.0) {		/* Large is present */
		ret_val = x1max * sqrt(s1 + s2 / x1max / x1max);
	} else {				/* Medium and small are present */
		if (s2 == 0.0) {
			ret_val = x3max * sqrt(s3);		/* Small only */
		} else {
			if (s2 >= x3max) {		/* Medium larger than small */
				ret_val = sqrt(s2 * (1.0 + x3max / s2 * (x3max * s3)));
			} else {				/* Small large than medium */
				ret_val = sqrt(x3max * (s2 / x3max + x3max * s3));
			}
		}
	}
	return ret_val;
}

