
/*
 *"@(#)linpack.c 1.1 10/31/94  Copyright Sun Microsystems";
 */

/******************************************************************************
	@(#)linpack.c - Rev 1.1 - 10/24/88
	Copyright (c) 1988 by Sun Microsystems, Inc.
	
	Caveat receptor.  (Jack) dongarra@anl-mcs, (Eric Grosse) research!ehg
	** from netlib@anl-mcs.arpa, Mon Feb  8 09:41:02 CST 1988 ***
	Translated to C by Bonnie Toy 5/88.
	Re-edited by Deyoung Hong 10/24/88.
	
	This source code will be used for both single and double precision
	calculation with the define of DP for double and default for single.

******************************************************************************/


#include <stdio.h>

extern int verbose_mode;

/* Double or single precision defines */
#ifdef	DP
#define	PREC	"double"
#define	LINPACK	dlinpack_test
#define	LINSUB	dlinsub
#define	MATGEN	dmatgen
#define	GEFA	dgefa
#define	GESL	dgesl
#define	AXPY	daxpy
#define	SCAL	dscal
#define	IAMAX	diamax
#define	EPSLON	depslon
#define	MXPY	dmxpy
#define	REAL	double
#define	ZERO	0.0e0
#define	ONE	1.0e0
#define	RESIDN	1.6711730035118721e+00
#define	RESID	7.4162898044960457e-14
#define	EPS	2.2204460492503131e-16
#define	X11	-1.4988010832439613e-14
#define	XN1	-1.8984813721090177e-14
#else
#define	PREC	"single"
#define	LINPACK	slinpack_test
#define	LINSUB	slinsub
#define	MATGEN	smatgen
#define	GEFA	sgefa
#define	GESL	sgesl
#define	AXPY	saxpy
#define	SCAL	sscal
#define	IAMAX	siamax
#define	EPSLON	sepslon
#define	MXPY	smxpy
#define	REAL	float
#define	ZERO	0.0
#define	ONE	1.0
#define	RESIDN	1.8984881639480591e+00
#define	RESID	4.5233617129269987e-05
#define	EPS	1.1920928955078125e-07
#define	X11	-1.3113021850585938e-05
#define	XN1	-1.3053417205810547e-05
#endif


/* Function declarations */
REAL EPSLON();
double fabs();


/*
 * This function performs the single and double precision linpack test.
 */
LINPACK()
{
        REAL residn, resid, eps, x11, xn1;

        LINSUB(&residn,&resid,&eps,&x11,&xn1);
	if (verbose_mode)
        	printf("%s %25.16e %25.16e %25.16e %25.16e %25.16e\n",
		PREC, residn, resid, eps, x11, xn1);
    	if ((residn == RESIDN) && (resid == RESID) && (eps == EPS) &&
		(x11 == X11) && (xn1 == XN1))
	{
		if (verbose_mode)
			printf("        PASSES  %s linpack test \n", PREC);
		return (0);
	}
        else
	{
		if (verbose_mode)
			printf("        Failed %s precision linpack test\n",PREC);
		return (-1);
	}

}

/*
 * Function to begin linpack calculation.
 */
LINSUB(residn,resid,eps,x11,xn1)
REAL *residn, *resid, *eps, *x11, *xn1;
{
	REAL a[200][201],b[200],x[200];
	REAL norma,normx,abs;
	int ipvt[200],n,i,info,lda;

	lda = 201;
	n = 100;

        MATGEN((REAL *)a,lda,n,b,&norma);
        GEFA((REAL *)a,lda,n,ipvt,&info);
        GESL((REAL *)a,lda,n,ipvt,b);

	/* compute a residual to verify results.  */ 
        for (i = 0; i < n; i++) {
            	x[i] = b[i];
	}
        MATGEN((REAL *)a,lda,n,b,&norma);
        for (i = 0; i < n; i++) {
            	b[i] = -b[i];
	}
        MXPY(n,b,n,lda,x,(REAL *)a);
        *resid = ZERO;
        normx = ZERO;
        for (i = 0; i < n; i++) {
		abs = (REAL)fabs((double)b[i]);
            	*resid = (*resid > abs) ? *resid : abs;
		abs = (REAL)fabs((double)x[i]);
            	normx = (normx > abs) ? normx : abs;
	}
        *eps = EPSLON((REAL)ONE);
        *residn = *resid/( n*norma*normx*(*eps) );
	*x11 = x[0] - 1;
	*xn1 = x[n-1] - 1;
}


/*
 * Function to generate real matrices.
 */
MATGEN(a,lda,n,b,norma)
REAL a[],b[],*norma;
int lda, n;
{
	int init, i, j;

	init = 1325;
	*norma = ZERO;
	for (j = 0; j < n; j++) {
		for (i = 0; i < n; i++) {
			init = 3125*init % 65536;
			a[lda*j+i] = (init - 32768.0)/16384.0;
			*norma = (a[lda*j+i] > *norma) ? a[lda*j+i] : *norma;
		}
	}
	for (i = 0; i < n; i++) {
        	b[i] = ZERO;
	}
	for (j = 0; j < n; j++) {
		for (i = 0; i < n; i++) {
			b[i] = b[i] + a[lda*j+i];
		}
	}
}


/*
 * Function factors a real matrix by gaussian elemination.
 */
GEFA(a,lda,n,ipvt,info)
REAL a[];
int lda,n,ipvt[],*info;
{
	REAL t;
	int IAMAX(),j,k,kp1,l,nm1;


	/* gaussian elimination with partial pivoting */

	*info = 0;
	nm1 = n - 1;
	if (nm1 >=  0) {
		for (k = 0; k < nm1; k++) {
			kp1 = k + 1;

          		/* find l = pivot index	*/

			l = IAMAX(n-k,&a[lda*k+k]) + k;
			ipvt[k] = l;

			/* zero pivot implies this column already 
			   triangularized */

			if (a[lda*k+l] != ZERO) {

				/* interchange if necessary */

				if (l != k) {
					t = a[lda*k+l];
					a[lda*k+l] = a[lda*k+k];
					a[lda*k+k] = t; 
				}

				/* compute multipliers */

				t = -ONE/a[lda*k+k];
				SCAL(n-(k+1),t,&a[lda*k+k+1]);

				/* row elimination with column indexing */

				for (j = kp1; j < n; j++) {
					t = a[lda*j+l];
					if (l != k) {
						a[lda*j+l] = a[lda*j+k];
						a[lda*j+k] = t;
					}
					AXPY(n-(k+1),t,&a[lda*k+k+1],
					      &a[lda*j+k+1]);
  				} 
  			}
			else { 
            			*info = k;
			}
		} 
	}
	ipvt[n-1] = n-1;
	if (a[lda*(n-1)+(n-1)] == ZERO) *info = n-1;
}


/*
 * Function to solve the real system a * x = b or trans(a) * x = b.
 */
GESL(a,lda,n,ipvt,b)
int lda,n,ipvt[];
REAL a[],b[];
{
	REAL t;
	int k,kb,l,nm1;

	nm1 = n - 1;

	/* solve  a * x = b
	   first solve  l*y = b	*/

	if (nm1 >= 1) {
		for (k = 0; k < nm1; k++) {
			l = ipvt[k];
			t = b[l];
			if (l != k){ 
				b[l] = b[k];
				b[k] = t;
			}	
			AXPY(n-(k+1),t,&a[lda*k+k+1],&b[k+1]);
		}
	} 

	/* now solve  u*x = y */

	for (kb = 0; kb < n; kb++) {
	    k = n - (kb + 1);
	    b[k] = b[k]/a[lda*k+k];
	    t = -b[k];
	    AXPY(k,t,&a[lda*k+0],&b[0]);
	}
}


/*
 * Function to perform constant times a vector plus a vector.
 */
AXPY(n,da,dx,dy)
REAL dx[],dy[],da;
int n;
{
	int i, m;

	if(n <= 0) return;
	if (da == ZERO) return;

	m = n % 4;
	if ( m != 0) {
		for (i = 0; i < m; i++) 
			dy[i] = dy[i] + da*dx[i];
		if (n < 4) return;
	}
	for (i = m; i < n; i = i + 4) {
		dy[i] = dy[i] + da*dx[i];
		dy[i+1] = dy[i+1] + da*dx[i+1];
		dy[i+2] = dy[i+2] + da*dx[i+2];
		dy[i+3] = dy[i+3] + da*dx[i+3];
	}
}
   
/*
 * Function to scale a vector by a constant.
 */
SCAL(n,da,dx)
REAL da,dx[];
int n;
{
	int i,m;

	if(n <= 0)return;
	m = n % 5;
	if (m != 0) {
		for (i = 0; i < m; i++)
			dx[i] = da*dx[i];
		if (n < 5) return;
	}
	for (i = m; i < n; i = i + 5){
		dx[i] = da*dx[i];
		dx[i+1] = da*dx[i+1];
		dx[i+2] = da*dx[i+2];
		dx[i+3] = da*dx[i+3];
		dx[i+4] = da*dx[i+4];
	}

}

/*
 * Function to find the index of element having maximum absolute value.
 */
int IAMAX(n,dx)
REAL dx[];
int n;
{
	REAL dmax, abs;
	int i, itemp;

	if( n < 1 ) return(-1);
	if(n == 1 ) return(0);

	itemp = 0;
	dmax = (REAL)fabs((double)dx[0]);
	for (i = 1; i < n; i++) {
		abs = (REAL)fabs((double)dx[i]);
		if(abs > dmax)  {
			itemp = i;
			dmax = abs;
		}
	}
	return (itemp);
}


/*
 * Function to estimate unit roundoff in quantities of size x.
 */
REAL EPSLON(x)
REAL x;
{
	REAL a,b,c,eps,abs;

	a = 4.0e0/3.0e0;
	eps = ZERO;
	while (eps == ZERO) {
		b = a - ONE;
		c = b + b + b;
		eps = (REAL)fabs((double)(c-ONE));
	}
	abs = (REAL)fabs((double)x);
	return(eps*abs);
}
 

/*
 * Function to multiply matrix m times vector x and add the result to vector y.
 */
MXPY(n1, y, n2, ldm, x, m)
REAL y[], x[], m[];
int n1, n2, ldm;
{
	int j,i,jmin;
	/* cleanup odd vector */

	j = n2 % 2;
	if (j >= 1) {
		j = j - 1;
		for (i = 0; i < n1; i++) 
            		y[i] = (y[i]) + x[j]*m[ldm*j+i];
	} 

	/* cleanup odd group of two vectors */

	j = n2 % 4;
	if (j >= 2) {
		j = j - 1;
		for (i = 0; i < n1; i++)
            		y[i] = ( (y[i])
                  	       + x[j-1]*m[ldm*(j-1)+i]) + x[j]*m[ldm*j+i];
	} 

	/* cleanup odd group of four vectors */

	j = n2 % 8;
	if (j >= 4) {
		j = j - 1;
		for (i = 0; i < n1; i++)
			y[i] = ((( (y[i])
			       + x[j-3]*m[ldm*(j-3)+i]) 
			       + x[j-2]*m[ldm*(j-2)+i])
			       + x[j-1]*m[ldm*(j-1)+i]) + x[j]*m[ldm*j+i];
	} 

	/* cleanup odd group of eight vectors */

	j = n2 % 16;
	if (j >= 8) {
		j = j - 1;
		for (i = 0; i < n1; i++)
			y[i] = ((((((( (y[i])
			       + x[j-7]*m[ldm*(j-7)+i]) + x[j-6]*m[ldm*(j-6)+i])
		  	       + x[j-5]*m[ldm*(j-5)+i]) + x[j-4]*m[ldm*(j-4)+i])
			       + x[j-3]*m[ldm*(j-3)+i]) + x[j-2]*m[ldm*(j-2)+i])
			       + x[j-1]*m[ldm*(j-1)+i]) + x[j]  *m[ldm*j+i];
	} 
	
	/* main loop - groups of sixteen vectors */

	jmin = (n2%16)+16;
	for (j = jmin-1; j < n2; j = j + 16) {
		for (i = 0; i < n1; i++) 
			y[i] = ((((((((((((((( (y[i])
			       	+ x[j-15]*m[ldm*(j-15)+i]) 
				+ x[j-14]*m[ldm*(j-14)+i])
			        + x[j-13]*m[ldm*(j-13)+i]) 
				+ x[j-12]*m[ldm*(j-12)+i])
			        + x[j-11]*m[ldm*(j-11)+i]) 
				+ x[j-10]*m[ldm*(j-10)+i])
			        + x[j- 9]*m[ldm*(j- 9)+i]) 
				+ x[j- 8]*m[ldm*(j- 8)+i])
			        + x[j- 7]*m[ldm*(j- 7)+i]) 
				+ x[j- 6]*m[ldm*(j- 6)+i])
			        + x[j- 5]*m[ldm*(j- 5)+i]) 
				+ x[j- 4]*m[ldm*(j- 4)+i])
			        + x[j- 3]*m[ldm*(j- 3)+i]) 
				+ x[j- 2]*m[ldm*(j- 2)+i])
			        + x[j- 1]*m[ldm*(j- 1)+i]) 
				+ x[j]   *m[ldm*j+i];
	}
}
