matrixutil.cpp.html mathcode2html   
 Source file:   matrixutil.cpp
 Converted:   Wed Jan 6 2016 at 15:24:07
 This documentation file will not reflect any later changes in the source file.

$$\phantom{******** If you see this on the webpage then the browser could not locate *********}$$
$$\phantom{******** jsMath/easy/load.js or the variable root is set wrong in this file *********}$$
$$\newcommand{\vector}[1]{\left[\begin{array}{c} #1 \end{array}\right]}$$ $$\newenvironment{matrix}{\left[\begin{array}{cccccccccc}} {\end{array}\right]}$$ $$\newcommand{\A}{{\cal A}}$$ $$\newcommand{\W}{{\cal W}}$$

/* Copyright 1998-2014 Research Foundation State University of New York   */

/* This file is part of QUB Express.                                      */

/* QUB Express 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.                                    */

/* QUB Express 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,     */
/* named LICENSE.txt, in the QUB Express program directory.  If not, see  */
/* <http://www.gnu.org/licenses/>.                                        */

/*
See also: Up: Index
*/


#ifdef _WIN32
  #include <windows.h>
#else
  #include <stdlib.h>
  #define BOOL int
  #define TRUE 1
  #define FALSE 0
#endif

#include <math.h>
#include <iostream>

#include "matrixutil.h"

using namespace std;
using namespace fq;

//----- Local functions ( optional declaration ) 
void balanc(double** a, int n);
void elmhes0(double** a, int n);		// TODO - not yet used 
void elmhes(double** a, int n);
void hmges(int n, double* w, double** v, double* x);
BOOL hqr(double** a, int n, double* wr, double* wi);
double pythag(double a, double b);
void swap(double *a, double *b, int iItems);


// ----------------------------------------------------------
// Swap two doubles - swap(&n,&n) function or SWAP(n,n) macro
// you must define locally:
// double swap_nTemp;
#define SWAP(g,h) {swap_nTemp=(g);(g)=(h);(h)=swap_nTemp;}

#define sign(a,b)   ((b)>=0.0 ? fabs(a) : -fabs(a))
#define SIGN(a,b) ((b) >= 0.0 ? fabs(a) : -fabs(a))

void swap(double *a, double *b, int iItems){
	double tmp;
	for( int i=0; i<iItems; ++i ) {
		tmp=a[i];
		a[i]=b[i];
		b[i]=tmp;
		}
	}

/*	---------------------------------------------------------------
	Gauss-Jordan linear equation solver
	2 versions - 0 based arrays and 1 based arrays
	a[1..n][1..n] is the input matrix
	b[1..n][1..m] is input containing the m right-hand side vectors
	on output, a is replaced by its matrix inverse,
	and b is replaced by the corresponding set of solution vectors.
*/
/*
BOOL gaussj(double** a, int n, double **b, int m, ostream &msgOut){
	int i, icol=-1, irow=-1, j, k, l, ll;
	double big,dum,pivinv;
	int * indxc = int_alloc1D(n+1);
	int * indxr = int_alloc1D(n+1);
	int * ipiv  = int_alloc1D(n+1);
	
	for (j=1; j<=n; j++) 
		ipiv[j]=0;

	for (i=1; i<=n; i++) {
		irow = icol = -1; // should be inside i ?
		big=0.0;
		for (j=1; j<=n; j++) {
			if (ipiv[j] != 1) {
				for (k=1; k<=n; k++) {
					if (ipiv[k] == 0) {
						if (fabs(a[j][k]) >= big) {
							big=fabs(a[j][k]);
							irow=j;
							icol=k;
							}
						} 
					else if (ipiv[k]>1) {
						msgOut << "Fatal err: Singular matrix in gaussj" << endl;
						free(ipiv);
						free(indxr);
						free(indxc);
						return FALSE;
						}
					}
				}
			}
		
		if ( irow == -1 || icol == -1 ) {
			msgOut << "gaussj found no pivot" << endl;
			free(ipiv);
			free(indxr);
			free(indxc);
			return FALSE;
			}
		
		++(ipiv[icol]);
		if (irow != icol) {
			swap(a[irow], a[icol], n+1);	// SWAP ROWS
			if( b != NULL ) 
				swap(b[irow], b[icol], m+1);
			}

		indxr[i]=irow;
		indxc[i]=icol;
		if (a[icol][icol] == 0.0) {
			msgOut << "Fatal err: Singular matrix in gaussj" << endl;
			free(ipiv);
			free(indxr);
			free(indxc);
			return FALSE;
			}
		
		pivinv=1.0/a[icol][icol];
		a[icol][icol]=1.0;
		for (l=1; l<=n; l++) 
			a[icol][l] *= pivinv;
		for (l=1; l<=m; l++) 
			b[icol][l] *= pivinv;
		for (ll=1; ll<=n; ll++) {
			if (ll != icol) {
				dum=a[ll][icol];
				a[ll][icol]=0.0;
				for (l=1; l<=n; l++) 
					a[ll][l] -= a[icol][l]*dum;
				for (l=1; l<=m; l++) 
					b[ll][l] -= b[icol][l]*dum;
				}
			}
		}
	
	for (l=n; l>=1; l--)
		if (indxr[l] != indxc[l])
			for (k=1; k<=n; k++)
				SWAP( (a[k][indxr[l]]), (a[k][indxc[l]]) );	//SWAP COLUMNS
			
	free(ipiv);
	free(indxr);
	free(indxc);
	return TRUE;
	}
*/

//--  Similar to Gauss-J above.  Removes memory leaks.  Uses 0 based arrays.
//--  Added comments.  More meaningful variable names.  Better Swap usage.
extern "C" QUBFAST_API BOOL gaussj0(double** a, int n, double **b, int m, ostream &msgOut){
	int iIter, iRow, iCol, iMaxCol, iMaxRow;	
	double 	big, dum, pivinv;
	int * indxc = int_alloc1D(n);	// [i] is the column of the i'th pivot element
	int * indxr = int_alloc1D(n);	// [i] is the original row for the i'th pivot 
	int * ipiv = int_alloc1D(n);
	double swap_nTemp;
	
	//-- Set all pivots to 0 ( false ) 
	for( iRow=0; iRow<n; iRow++) 
		ipiv[iRow]=0;

	iMaxRow = iMaxCol = -1;

	//-- For each iteration, find max and {divide, swap}.
	for (iIter=0; iIter<n; iIter++) {
		//-- For i,j not yet pivoted, find where max abs(a[i][j]) occurs ==> Pivot 
		big=0.0;
		for (iRow=0; iRow<n; iRow++) {
			if (ipiv[iRow] != 1) {
				for (iCol=0; iCol<n; iCol++) {
					if (ipiv[iCol] == 0) { 
						if (fabs(a[iRow][iCol]) > big)
							big=fabs(a[iMaxRow=iRow][iMaxCol=iCol]);
						}
					else if (ipiv[iCol]>1) {
						msgOut << "Fatal err: Singular matrix in gauss-j" << endl;
						free(ipiv);
						free(indxr);
						free(indxc);
						return FALSE;
						}
					}
				}
			}
		if ( iMaxRow == -1 || iMaxCol == -1 ) {
		  // msgOut << "gauss-j found no pivot" << endl;
			free(ipiv);
			free(indxr);
			free(indxc);
			return FALSE;
			}

		//-- Got the pivot element, interchange rows to force the pivot element to 
		//-- be on the diagonal.  
		++(ipiv[iMaxCol]);
		if (iMaxRow != iMaxCol) {
			swap( a[iMaxRow], a[iMaxCol], n );
			if( b != NULL ) 
				swap( b[iMaxRow], b[iMaxCol], m );
			}

		indxr[iIter]=iMaxRow;
		indxc[iIter]=iMaxCol;
		pivinv = a[iMaxCol][iMaxCol];
		a[iMaxCol][iMaxCol]=1.0;		//-- this is unusual, but is fixed by the next if stmt.
		//-- divide row iCol by big in a and b
		for (iCol=0; iCol<n; iCol++) 
			a[iMaxCol][iCol] /= pivinv;
		for (iCol=0; iCol<m; iCol++) 
			b[iMaxCol][iCol] /= pivinv;

		//-- For each row, subtract a multiple of the maxrow which makes maxcol element 0
		for (iRow=0; iRow<n; iRow++) {
			if (iRow != iMaxCol) {
				dum=a[iRow][iMaxCol];
				a[iRow][iMaxCol]=0.0;
				for (iCol=0; iCol<n; iCol++) 
					a[iRow][iCol] -= a[iMaxCol][iCol]*dum;
				for (iCol=0; iCol<m; iCol++) 
					b[iRow][iCol] -= b[iMaxCol][iCol]*dum;
				}
			}
		}
	
	// unscramble the solution w.r.t. the column interchanges
	// by interchanging columns in the reverse order 
	for( iIter=n-1; iIter>=0; iIter--)
		if (indxr[iIter] != indxc[iIter])
			for (iRow=0; iRow<n; iRow++)
				SWAP( (a[iRow][indxr[iIter]]), (a[iRow][indxc[iIter]]) );

	free(ipiv);
	free(indxr);
	free(indxc);
	return TRUE;
	}

extern "C" QUBFAST_API BOOL gaussj_invert(matrix<double>& mat, int n, std::ostream& msgOut){
	return gaussj0(mat, n, NULL, 0, msgOut );
	}

/*	---------------------------------------------------------------
	Pythagoras theorem without destructive underflow or overflow.
	Used by svdecomp function.
*/
double pythag(double a, double b){
	a=fabs(a);
	b=fabs(b);
	if( a>b )
		return a*sqrt(1.0+DSQR(b/a));
	if ( b==0.0 )		
		return 0.0;
	return b*sqrt(1.0+DSQR(a/b));
	}

/*	---------------------------------------------------------------
	Singular-value decomposition routine
*/

extern "C" QUBFAST_API int svdecomp(double **a, int m, int n, double *w, double **v, ostream &msgOut){
	int     flag,i,its,j,jj,k,l,nm;
	double  anorm,c,f,g,h,s,scale,x,y,z,*rv1;
	int rtn = 0;

	rv1 = double_alloc1D(n+1);
	for (i=0; i<=n; ++i)
	  rv1[i] = 0.0;
	
	// householder reduction to bidiagonal form 
	g=scale=anorm=0.0;
	for (i=1; i<=n; i++) {
		l=i+1;
		rv1[i]=scale*g;
		g=s=scale=0.0;
		if (i <= m) {
			for (k=i; k<=m; k++) 
				scale += fabs(a[k][i]);
			if (scale) {
				for (k=i; k<=m; k++) {
					a[k][i] /= scale;
					s += a[k][i]*a[k][i];
					}
				f=a[i][i];
				g = -sign(sqrt(s),f);
				h=f*g-s;
				a[i][i]=f-g;
				for (j=l; j<=n; j++) {
					for (s=0.0,k=i ;k<=m; k++) 
						s += a[k][i]*a[k][j];
					f=s/h;
					for (k=i; k<=m; k++) 
						a[k][j] += f*a[k][i];
					}
				for (k=i; k<=m; k++) a[k][i] *= scale;
				}
			}
		w[i]=scale *g;
		g=s=scale=0.0;
		if (i <= m && i != n) {
			for (k=l; k<=n; k++) 
				scale += fabs(a[i][k]);
			if (scale) {
				for (k=l; k<=n; k++) {
					a[i][k] /= scale;
					s += a[i][k]*a[i][k];
					}
				f=a[i][l];
				g = -sign(sqrt(s),f);
				h=f*g-s;
				a[i][l]=f-g;
				for (k=l; k<=n; k++) 
					rv1[k]=a[i][k]/h;
				for (j=l; j<=m; j++) {
					for (s=0.0,k=l; k<=n; k++) 
						s += a[j][k]*a[i][k];
					for (k=l; k<=n; k++) 
						a[j][k] += s*rv1[k];
					}
				for (k=l; k<=n; k++) 
					a[i][k] *= scale;
				}
			}
		anorm=DMAX(anorm,(fabs(w[i])+fabs(rv1[i])));
		}

	//-- accumulation of right hand transformations 
	for (i=n; i>=1; i--) {
		if (i < n) {
			if (g) {
				for (j=l; j<=n; j++) v[j][i]=(a[i][j]/a[i][l])/g;
				for (j=l; j<=n; j++) {
					for (s=0.0,k=l; k<=n; k++) s += a[i][k]*v[k][j];
					for (k=l; k<=n; k++) v[k][j] += s*v[k][i];
					}
				}
			for (j=l; j<=n; j++) v[i][j]=v[j][i]=0.0;
			}
		v[i][i]=1.0;
		g=rv1[i];
		l=i;
		}
	
	//-- Accumulation of right hand transformations
	for (i=IMIN(m,n); i>=1; i--) {
		l=i+1;
		g=w[i];
		for (j=l; j<=n; j++) a[i][j]=0.0;
		if (g) {
			g=1.0/g;
			for (j=l; j<=n; j++) {
				for (s=0.0,k=l; k<=m; k++) 
					s += a[k][i]*a[k][j];
				f=(s/a[i][i])*g;
				for (k=i; k<=m; k++) 
					a[k][j] += f*a[k][i];
				}
			for (j=i; j<=m; j++) 
				a[j][i] *= g;
			} 
		else 
			for (j=i; j<=m; j++) 
				a[j][i]=0.0;
		++a[i][i];
		}

	//-- diagonalization of the bidiagonal form: 
	//   loop over singular values and allowed iterations
	for (k=n; k>=1; k--) {
		for (its=1; its<=30; its++) {
			flag=1;
			for (l=k; l>=1; l--) {		// test for splitting
				nm=l-1;					// note that rv1[1] is always zero...
				if (fabs(rv1[l])+anorm == anorm) {
					flag=0;
					break;
					}
				if (fabs(w[nm])+anorm == anorm)	// ...w[nm] could err if rv1[1]!=0
					break;
				}
			if (flag) {
				c=0.0;
				s=1.0;
				for (i=l; i<=k; i++) {
					f=s*rv1[i];
					rv1[i]=c*rv1[i];
					if (fabs(f)+anorm == anorm) 
						break;
					g=w[i];
					h=pythag(f,g);
					w[i]=h;
					h=1.0/h;
					c=g*h;
					s = -f*h;
					for (j=1; j<=m; j++) {
						y=a[j][nm];
						z=a[j][i];
						a[j][nm]=y*c+z*s;
						a[j][i]=z*c-y*s;
						}
					}
				}
			z=w[k];

			if (l == k) {		// convergence.  Singular value is made non negative.
				if (z < 0.0) {
					w[k] = -z;
					for (j=1; j<=n; j++) 
						v[j][k] = -v[j][k];
					}
				break;
				}
			if(its==50) {
				rtn = 1;
				msgOut << "Err: no convergence in svdecomp" << endl;
				}
			
			// shift from bottom 2-by-2 minor
			x=w[l];
			nm=k-1;
			y=w[nm];
			g=rv1[nm];
			h=rv1[k];
			f=((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y);
			g=pythag(f,1.0);
			f=((x-z)*(x+z)+h*((y/(f+sign(g,f)))-h))/x;
			c=s=1.0;					// Next QR transformation
			for (j=l; j<=nm; j++) {
				i=j+1;
				g=rv1[i];
				y=w[i];
				h=s*g;
				g=c*g;
				z=pythag(f,h);
				rv1[j]=z;
				c=f/z;
				s=h/z;
				f=x*c+g*s;
				g = g*c-x*s;
				h=y*s;
				y *= c;
				for (jj=1; jj<=n; jj++) {
					x=v[jj][j];
					z=v[jj][i];
					v[jj][j]=x*c+z*s;
					v[jj][i]=z*c-x*s;
					}
				z=pythag(f,h);
				w[j]=z;				// rotation can be arbitrary if z=0
				if (z) {
					z=1.0/z;
					c=f*z;
					s=h*z;
					}
				f=c*g+s*y;
				x=c*y-s*g;
				for (jj=1; jj<=m; jj++) {
					y=a[jj][j];
					z=a[jj][i];
					a[jj][j]=y*c+z*s;
					a[jj][i]=z*c-y*s;
					}
				}
			rv1[l]=0.0;
			rv1[k]=f;
			w[k]=x;
			}
		}

	// Reorders columns of a,v matrices and w vector ordered by abs(w) descending.
	// This appears to be an inefficient swap sort but check more thoroughly.
	// More efficient : Quick sort the W array and a parallel vector of column 
	// destinations then move the a and v columns efficiently.
	double *aa,*vv,ww;
	aa  = double_alloc1D(m+1);
	vv  = double_alloc1D(n+1);
	for (j=2; j<=n; j++) {
	    ww=w[j];
	    for (i=1; i<=m; i++) aa[i]=a[i][j];		// aa = col j of a
	    for (i=1; i<=n; i++) vv[i]=v[i][j];		// vv = col j of v 
	    k=j-1;
	    while ( k > 0 && -fabs(w[k]) > -fabs(ww) ) {
	        w[k+1]=w[k];
	        for (i=1; i<=m; i++) a[i][k+1]=a[i][k];	// copy col k of a to k+1
	        for (i=1; i<=n; i++) v[i][k+1]=v[i][k];	// copy col k of v to k+1
	        k--;
			}
	    w[k+1]=ww;
	    for (i=1; i<=m; i++) a[i][k+1]=aa[i];
	    for (i=1; i<=n; i++) v[i][k+1]=vv[i];
		}
	free(aa);
	free(vv);
	
	free(rv1);

	return rtn;
	}


/*
 * eigen utility - Reduction to hessenberg form by the elimination method.
 Replace with elmhes0 from qublib ( same ) 
 */
void elmhes0(double **a, int n){
	int m,j,i;
	double y,x;
	double swap_nTemp;
	
	for (m=2; m<n; m++) {
		x=0.0;
		i=m;
		for (j=m; j<=n; j++) {			// find the pivot
			y= fabs(a[j-1][m-1-1]);
			if ( y > fabs(x) ) {
				x=a[j-1][m-1-1];
				i=j;
				}             
			}

		if (i != m) {
			for (j=m-1; j<=n; j++) 
				SWAP(a[i-1][j-1],a[m-1][j-1])
			for (j=1; j<=n; j++) 
				SWAP(a[j-1][i-1],a[j-1][m-1])
			}

		if (x) {
			for (i=m+1; i<=n; i++) {
				if ((y=a[i-1][m-1-1]) != 0.0) {
					y /= x;
					a[i-1][m-1-1]=y;
					for (j=m; j<=n; j++) 
						a[i-1][j-1] -= y*a[m-1][j-1];
					for (j=1; j<=n; j++) 
						a[j-1][m-1] += y*a[j-1][i-1];
					}
				}
			}
		}
	}

void elmhes(double **a, int n){			
	int m,j,i;
	double y,x;
	double swap_nTemp;
	
	for (m=2; m<n; m++) {
		x=0.0;
		i=m;
		for (j=m; j<=n; j++) {			// find the pivot
			y= fabs(a[j][m-1]);
			if ( y > fabs(x) ) {
				x=a[j][m-1];
				i=j;
				}             
			}

		if (i != m) {
			for (j=m-1; j<=n; j++) 
				SWAP(a[i][j],a[m][j])
			for (j=1; j<=n; j++) 
				SWAP(a[j][i],a[j][m])
			}

		if (x) {
			for (i=m+1; i<=n; i++) {
				if ((y=a[i][m-1]) != 0.0) {
					y /= x;
					a[i][m-1]=y;
					for (j=m; j<=n; j++) 
						a[i][j] -= y*a[m][j];
					for (j=1; j<=n; j++) 
						a[j][m] += y*a[j][i];
					}
				}
			}
		}
	}

void hmges(int n, double* w, double** v, double* x){
	 const double TOL = 1.0e-8;
	 int	i,j;
	 double	wmx,s,*y;
	 
	 y=double_alloc1D(n+1);
	 wmx=0.0;
	 for (i=1; i<=n; i++)
		 if(fabs(w[i]) > wmx) 
			 wmx=fabs(w[i]);
		 
	 for (i=1; i<=n; i++)
		 if(fabs(w[i])<TOL*wmx) 
			 w[i]=0.0;
		 
	 /* y=V'x  */
	 for (i=1; i<=n; i++)
		 y[i]= (w[i]==0.0 ? 1.0 : 0.0 );   /* + ncall + i;*/

	 for (i=1; i<=n; i++) {
		 s=0.0;
		 for (j=1; j<=n; j++) 
			 s=s+v[i][j]*y[j];
		 x[i]=s;
		 }
	 
	 s=0.0;
	 for (i=1; i<=n; i++) 
		 s=s+x[i]*x[i];
	 if( s > 0.0 ) { 
		 s=sqrt(s);
		 for (i=1; i<=n; i++) 
			 x[i]=x[i]/s;
		 free(y);
		 }

	 }
 
// WARNING : a, v are 0 based arrays. wr and wi are 1 based arrays.
extern "C" QUBFAST_API BOOL eigen(double** a, int n, double* wr, double* wi, double** v, ostream &msgOut){
	const double TINY = 1.0e-5;

	int	i,j;
	BOOL lRet=TRUE;
	
     /* find eigenvalues */
	double **u=double_alloc2D(n+1,n+1);
	for (i=1; i<=n; i++) 
	   for (j=1; j<=n; j++) 
         u[i][j]=a[i-1][j-1];

	balanc(u,n); 

	elmhes(u,n);

	if ( ! hqr(u,n,wr,wi) )    {
	    free_2D( (char **)u );
	    return FALSE;
		}	
    free_2D( (char **)u );

    

	double **u1=double_alloc2D(n+1,n+1);
	double **v1=double_alloc2D(n+1,n+1);
	double *w1=double_alloc1D(n+1);
	double *x1=double_alloc1D(n+1);
	double **u2=double_alloc2D(2*n+1,2*n+1);
	double **v2=double_alloc2D(2*n+1,2*n+1);
	double *w2=double_alloc1D(2*n+1);
	double *x2=double_alloc1D(2*n+1);

     /* find eigenvectors */

	int k=1;
	while ( k <= n ) {
		if( fabs(wi[k]) < TINY ) {
			for (i=1; i<=n; i++) {
				for (j=1; j<=n; j++) 
					u1[i][j]=a[i-1][j-1];
				u1[i][i]=u1[i][i]-wr[k];
				}
			
			if ( svdecomp(u1,n,n,w1,v1,msgOut) ) {
				lRet=FALSE;
				break;
				}	

			hmges(n,w1,v1,x1);
			for (i=1; i<=n; i++) {
				v[i-1][k-1]=x1[i];
				}
			k++;
			} 
		else {
			for (i=1; i<=n; i++) {
				for (j=1; j<=n; j++) 
					u2[i][j]=a[i-1][j-1];
				u2[i][i]=u2[i][i]-wr[k];
				for (j=n+1; j<=2*n; j++) 
					u2[i][j]=0.0;
				u2[i][n+i]=wi[k];
				}
			for (i=n+1; i<=2*n; i++) {
				for (j=1; j<=n; j++) 
					u2[i][j]=-u2[i-n][j+n];
				for (j=n+1; j<=2*n; j++) 
					u2[i][j]=u2[i-n][j-n];
				}
			if ( svdecomp(u2,2*n,2*n,w2,v2,msgOut) ) {
				lRet=FALSE;
				break;
				}	
			
			hmges(2*n,w2,v2,x2);
			for (i=1; i<=n; i++) {
				v[i-1][k-1]=x2[i];
				v[i-1][k+1-1]=x2[n+i];
				}	
			k=k+2;
			}
		}

	free_2D( (char **)u1 );
	free_2D( (char **)v1 );
	free(w1);
	free(x1);
	free_2D( (char **)u2 );
	free_2D( (char **)v2 );
	free(w2);
	free(x2);
	return lRet;
	}	

// BALANCE A MATRIX TO PREVENT ROUND OFF ERRORS ON EIGENVALUE DETERMINATION.
void balanc(double** a, int n){
	const double RADIX=2.0;
	const double SQRDX=RADIX*RADIX;
	
	int j,i;
	double s,r,g,f,c;
	bool last=false;
	
	while( ! last ) {
		last=true;
		for( i=1; i<=n; i++ ) {
			r=c=0.0;
			for( j=1; j<=n; j++)
				if (j != i) {
					c += fabs(a[j][i]);
					r += fabs(a[i][j]);
					}
			if( c!=0.0 && r!=0.0 ) {
				g=r/RADIX;
				f=1.0;
				s=c+r;
				while (c<g) {
					f *= RADIX;
					c *= SQRDX;
					}
				g=r*RADIX;
				while (c>g) {
					f /= RADIX;
					c /= SQRDX;
					}
				if ((c+r)/f < 0.95*s) {
					last=false;
					g=1.0/f;
					for( j=1; j<=n; j++ ) {
						a[i][j] *= g;
						a[j][i] *= f;
						}
					}
				}
			}
		}
	}

//-- Find all roots of an upper Hessenberg matrix a[][] is destroyed.
//-- wr[] and wi[] are filled with the real and imaginary parts of the eigenvalues.
BOOL hqr(double** a, int n, double* wr, double* wi) {
	int 	nn,m,l,k,j,its,i,mmin;
	double	z,y,x,w,v,u,t,s,r=0.,q=0.,p=0.,anorm;
	
	//-- compute matrix norm for possible use in locating single small subdiagonal element
	anorm=fabs(a[1][1]);
	for (i=2; i<=n; i++)
		for (j=(i-1); j<=n; j++) 
			anorm += fabs(a[i][j]);

	nn=n;
	t=0.0;					// gets changed only by an exceptional shift.
	while (nn >= 1) {		// begin search for next eigenvalue.
		its=0;
		do  {
			// begin iteration : look for a single small subdiagonal element
			for (l=nn; l>=2; l--) {
				s=fabs(a[l-1][l-1])+fabs(a[l][l]);
				if (s == 0.0) 
					s=anorm;
				if ((double)(fabs(a[l][l-1]) + s) == s) 
					break;
				}
			
			x=a[nn][nn];
			if (l == nn) {			// one root found
				wr[nn]=x+t;
				wi[nn--]=0.0;
				} 
			else {
				y=a[nn-1][nn-1];
				w=a[nn][nn-1]*a[nn-1][nn];
				if (l == (nn-1)) {	// two roots found ...
					p=0.5*(y-x);
					q=p*p+w;
					z=sqrt(fabs(q));
					x += t;
					if (q >= 0.0) {	// ...a real pair
						z=p+sign(z,p);
						wr[nn-1]=wr[nn]=x+z;
						if (z) 
							wr[nn]=x-w/z;
						wi[nn-1]=wi[nn]=0.0;
						} 
					else {			// ...a complex pair
						wr[nn-1]=wr[nn]=x+p;
						wi[nn-1]= -(wi[nn]=z);
						}
					nn -= 2;
					} 
				else {						// no roots found, continue iteration
					if (its == 30) 
						return FALSE;
					if (its == 10 || its == 20) {	// form exceptional shift 
						t += x;
						for (i=1;i<=nn;i++) 
							a[i][i] -= x;
						s=fabs(a[nn][nn-1])+fabs(a[nn-1][nn-2]);
						y=x=0.75*s;
						w = -0.4375*s*s;
						}
					++its;
					// form shift and then look for 2 consecutive small subdiagonal elements
					for (m=(nn-2);m>=l;m--) {
						z=a[m][m];
						r=x-z;
						s=y-z;
						p=(r*s-w)/a[m+1][m]+a[m][m+1];
						q=a[m+1][m+1]-z-r-s;
						r=a[m+2][m+1];
						s=fabs(p)+fabs(q)+fabs(r);	// scale to prevent overflow / underflow
						p /= s;
						q /= s;
						r /= s;
						if (m == l) 
							break;
						u=fabs(a[m][m-1])*(fabs(q)+fabs(r));
						v=fabs(p)*(fabs(a[m-1][m-1])+fabs(z)+fabs(a[m+1][m+1]));
						if ((double)(u+v) == v) 
							break;
						}
					for (i=m+2;i<=nn;i++) {
						a[i][i-2]=0.0;
						if (i != (m+2)) 
							a[i][i-3]=0.0;
						}
					for (k=m;k<=nn-1;k++) {
						// double QR step on rows l to nn and m to nn
						if (k != m) {
							p=a[k][k-1];
							q=a[k+1][k-1];
							r=0.0;
							if (k != (nn-1)) 
								r=a[k+2][k-1];
							if ((x=fabs(p)+fabs(q)+fabs(r)) != 0.0) {
								p /= x;
								q /= x;
								r /= x;
								}
							}
						if ((s=sign(sqrt(p*p+q*q+r*r),p)) != 0.0) {
							if (k == m) {
								if (l != m) 
									a[k][k-1] = -a[k][k-1];
								} 
							else
								a[k][k-1] = -s*x;
							p += s;
							x=p/s;
							y=q/s;
							z=r/s;
							q /= p;
							r /= p;
							for (j=k;j<=nn;j++) {	// row modification
								p=a[k][j]+q*a[k+1][j];
								if (k != (nn-1)) {
									p += r*a[k+2][j];
									a[k+2][j] -= p*z;
									}
								a[k+1][j] -= p*y;
								a[k][j] -= p*x;
								}
							mmin = nn<k+3 ? nn : k+3;	
							for (i=l;i<=mmin;i++) {		/// column modification 
								p=x*a[i][k]+y*a[i][k+1];
								if (k != (nn-1)) {
									p += z*a[i][k+2];
									a[i][k+2] -= p*r;
									}
								a[i][k+1] -= p*q;
								a[i][k] -= p;
								}
							}
						}
					}
				}
			} while (l < nn-1);
		}
	return TRUE;
	}