/*
find largest eigenvalues of a symmetric square matrix

from Handbook of C Tools for Scientists and Engineers by L. Baker

uses vector iteration

int elarge(a,value,vector,work,worka,pivot,workp,n,m,eigvect)
	matrix a(n,n). find the m eigenvalues of smallest(see text)
	absolute value. If eigvect nonzero, return eigenvalues
	as well in vector[].  Eigenvalues returned in value.
	Function itself returns count of eigenvalues found.
	Other arguments work space.

DEPENDENCIES:
	ftoc.h
	vector.c routines
*/

#include <ftoc.h>
/*#include "libc.h"
#include "math.h"
*/
#define INDEX(i,j) [j+(i)*coln]
#define INDEK(i,j) [j+(i)*k]
#define DOFOR(i,to) for(i=0;i<to;i++)
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
#define abs(a)  ((a)>0?(a):-(a))

#define wait 6
#define waitrs 3
#define maxit 100
#define tola 1.e-9
#define tolr .00000
#define tola2 .000000
#define tolr2 .000000

int elarge(a,value,vector,work,worka,pivot,workp,n,m,eigvect)
int n,m,pivot[],workp[],eigvect;
float a[],value[],vector[],work[],worka[];
/* find m largest eigenvalues of symmetric matrix a(n,n) 
 by iteration  */
/* pivot,work must be at least n long*/
/* the ith eigenvalue is returned in value[i-1]
	"       vector is in the i-th ROW of vectors
note that the rows may have been swapped. This info is in pivot.
eigvect=0 if we only want eigenvalues

*/
{
double sqrt();
double enew,eold;
double alpha,resid,deltrq,rnew,dot(),sqnor(),rq,rqold,shift,shifto;
int iflag,info,i,j,it,ke,nn,nn1,coln,index,k;
float *v1,sdot();
nn=n;
coln=n;
index=0;
rqold=1.e10;
rnew=resid=1.e6;
DOFOR(i,m*n) vector[i]=0.;
DOFOR(ke,m)
	{
	nn1=nn-1;
	shift=0.;/* will it still work midway between two eigenvalues?YES*/
	if(ke>0)shift=value[ke-1];
		/*above is attempt to insure the largest m eigenvalues
			are found */
	shifto=shift;
	v1=&(vector[coln*ke]);
	if(nn==1)
		{value[ke]= a[0];
		v1[0]=1.00;
/* set single known component of eigenvector to 1. (arb.)
 this will work except in the rare case its identically zero-
*/		
		iflag=1;
		pivot[ke]=0;
printf(" going to loner\n");
		goto loner;
		}
	vset(work,1.,n);work[0]=-1.;
	DOFOR(it,maxit)
		{
		vcopy(work,v1,nn);
		/*shift*/
/*printm(a,coln,nn,nn,nn);*/
		mcopy(a,worka,coln,nn,nn);
		DOFOR(index,nn) worka INDEX(index,index)-= (shift);
/*printm(worka,coln,nn,nn,nn);*/
		mvc(worka,work,v1 ,nn ,nn ,coln);
		/* Rayleigh quotient*/
		rq= dot(v1,work,nn)/dot(work,work,nn);
/*printf(" shift=%f,rq=%f\n",shift,rq);*/
		alpha=1./ sqrt(sqnor(v1,nn));
		vs(v1,alpha,nn);/*renormalize v1*/
		alpha=1./ sqrt(sqnor(work,nn));
		vs(work,alpha,nn);
		if(it>wait)
			{/* check for termination*/
/*pv(v1,nn);pv(work,nn);*/
			if( v1[0]*work[0]<0.)
				vs(work,-1.,nn);
			/* don't need work any more*/
			vdif(v1,work,work,nn);
			rnew= sqnor(work,nn);
			deltrq=abs(rq-rqold);
/*printf("dif=");pv(work,nn);
printf(" rnew=%f,resid=%f\n",rnew,resid);*/
			if ( rnew*(alpha*alpha)<tolr ||rnew<tola
			    || deltrq<tola2
			    || deltrq<tolr2*rq)
				{/*converged*/
				convg:
				value[ke]=shift+rq;
/*printf(" eigenvalue=%f ke=%d\n",value[ke],ke);	
printf(" eigenvector:");pv(v1,nn);*/
				/*deflate*/
/*printf(" before deflation");printm(a,coln,coln,nn,nn);*/
				alpha=0.;/*find max element*/
				DOFOR(i,nn)
					{resid=abs(v1[i]);
					 if(resid>alpha)
						{
						j=i;
						alpha=resid;
						}
					}
				pivot[ke]=j;
			/*swap eigenv elements, a row and col j<->nn*/
				index=nn-1;
				vcopy(v1,work,nn);
				if(j!=index)
					{
					resid=work[j];
					work[j]=work[index];
					work[index]=resid;
					swaprow(a,index,j,coln);
					swapcol(a,index,j,coln);
					alpha=1./resid;
					}
				else
					alpha=1./work[j];
				
				work[index]=1.;
				DOFOR(i,index) work[i]*=alpha;
				/* work[nn-1]=1., all other work<1*/
/*printf(" work vector=\n");	pv(work,nn);*/
				DOFOR(i,index)
					{
printf(" deflating row %d\n",i);
					DOFOR(j,index)
						{
						a INDEX(i,j)-=
						work[i]* a INDEX(index,j);
printf("work=%f,a=%f,i=%d j=%d index=%d\n",work[i],a INDEX(index,j),i,j,index);
						
						}
					}	
					nn--;
printf(" after deflation ");printm(a,coln,coln,coln,coln);
					iflag=0;
loner:				if( eigvect && ke>0)
					{
					enew=value[ke];
					index=ke;
					for(j=coln-ke;j<coln;j++)
					{
					index--;
					eold=value[index];
					if(enew==eold)
							{
							printf(" degenerate eigenvalues\n");
							goto giveup;
							}
					alpha= dot(&(a INDEX(j,0) ),v1,j)/(enew-eold);
printf(" eold %f enew %f j %d ke %d\n",eold,enew,j,ke);
printf(" alpha=%f\n",alpha);
					v1[j]=alpha;
					/* multiply by all previous t matrices, latest first 
					 this is the permuted vector,not just vector!
					 don't unpermute until done */
pv(v1,j+1);
					/* modify alpha to account for fact eigenvectors
					are not stored with last component (biggest)=1.
					we do not use this last component of the prev eigvector
					in vv() below. 
					*/
					vcopy(&(vector[coln*(index)]),work,n);
					i=pivot[index];
					if(i!=0)
						{
						resid=work[i];
						work[i]=work[j];
						work[j]=resid;
						}
					alpha/=work[j];
printf(" norm alpha by %f,new alpha%f\n",vector[coln*index+j],alpha);	
					vv(v1,v1,work,alpha,j);
pv(v1,j+1);
pv(&(vector[coln*index]),j);
					normv(v1,j+1);
printf(" renorm v now\n");pv(v1,j+1);
					
					}

					if(iflag)goto fini;
	
					}/*if */
				giveup: break;
				}/* if converged*/
/*			if(rnew>resid*1.1)
				{
printf(" diverging %f %f eigenvalues permuted\n",rnew,resid);
				return(ke);
				}
*/
			}/* it> wait*/
		/*update iteration*/
		resid=rnew;
		rqold=rq;
		vcopy(v1,work,nn);
		shifto=shift;
		}/* do it*/
	if(it>=(maxit-1)){/* iteration count exceeded,no conv*/
			printf(" no converg, eigevalues permuted\n");
			 return(ke);
			}
	if(nn==0)break;
	}/* loop over eignvalues*/
fini:
DOFOR(i,m)printf(" pivot[%d]=%d\n",i,pivot[i]);

for(i=1;i<m;i++)
	{/* which element to pivot*/
	/*permute eigenvalues*/
	k=pivot[i-1];
	if(k)
		{
		for(j=i;j<m;j++)/*loop over eigenvectors to be affected*/ 
			{ 
			index= j*coln+k;
			ke= j*coln+ (coln-i) ;/*coln-i was nn for ith eigenv.*/
			alpha= vector[index];
			vector[index]=vector[ke];
			vector[ke]=alpha;
			}
		}	
	}
/*graham Schmidt improvement orthog. with respect to previous eigenv.
    cannot do sooner easily due to permutations*/
if(eigvect)
{
DOFOR(i,m)
	{
	DOFOR(j,i)
		{
		alpha= dot(&(vector[i*coln]),&(vector[j*coln]),n);
		DOFOR(k,n) vector[i*coln+k]-=alpha*vector[j*coln+k];		
		}
	normv(&(vector[i*coln]),n);
	}
}
return(m);
}