/*-*-macsyma-*-*/
/*	THIS IS THE FILE EIGEN > DSK:SHARE;.
	THIS IS THE SOURCE CODE FOR THE NEW EIGEN PACKAGE AND IT IS
	MACSYMA BATCHABLE, I.E. BATCH(EIGEN,>,DSK,SHARE);.  IF YOU DO NOT WANT
	TO WASTE TIME (AND/OR PAPER...) THE FASTLOADABLE VERSION IS ON THE FILE
	EIGEN FASL DSK:SHARE;.  YOU CAN LOAD THE LATTER USING MACSYMA'S 
	LOADFILE COMMAND, I.E. LOADFILE(EIGEN,FASL,DSK,SHARE);. THE FUNCTIONS
	ARE DESCRIBED IN THE FILE EIGEN USAGE DSK:SHARE;, AND THE DEMO FILE IN
	WHICH THE FUNCTIONS ARE DEMONSTRATED IS EIGEN DEMO DSK:SHARE;.     */

EVAL_WHEN(TRANSLATE_FILE,
          MODEDECLARE([HERMITIANMATRIX,NONDIAGONALIZABLE,KNOWNEIGVALS,
                       KNOWNEIGVECTS],BOOLEAN,
		      [INDEX1,INDEX2,INDEX3,INDEX4,DIMNSN,COUNT,
	               %RNUM],FIXNUM),
	  DECLARE([HERMITIANMATRIX,NONDIAGONALIZABLE,KNOWNEIGVALS,
                       KNOWNEIGVECTS,LISTEIGVECTS,LISTEIGVALS,%RNUM,LISTARITH,
                   PROGRAMMODE],SPECIAL))$

SSTATUS(FEATURE,EIGEN)$

HERMITIANMATRIX:FALSE$

NONDIAGONALIZABLE:FALSE$

KNOWNEIGVALS:FALSE$

KNOWNEIGVECTS:FALSE$

LISTEIGVECTS:[]$

LISTEIGVALS:[]$

CONJUGATE(X):=SUBLIS('[%I=-%I],X)$

INNERPRODUCT(X,Y):=BLOCK([LISTARITH],LISTARITH:TRUE,RATSIMP(CONJUGATE(X).Y))$

UNITVECTOR(X):=BLOCK([LISTARITH,INTRN],LISTARITH:TRUE,INTRN:INNERPRODUCT(X,X),INTRN:SQRT(INTRN),X/INTRN)$

COLUMNVECTOR(X):=TRANSPOSE(MATRIX(X))$


GRAMSCHMIDT(X):=
		BLOCK([LISTARITH,DIMNSN,LISTALL,INTERN,COUNT,DENOM,UNIT,INDEX1,INDEX2],
		LISTARITH:TRUE,DIMNSN:LENGTH(X),LISTALL:[PART(X,1)],
		COUNT:1,IF DIMNSN=1 THEN RETURN(X)
		ELSE (FOR INDEX1:2 THRU DIMNSN DO
		(UNIT:PART(X,INDEX1),FOR INDEX2 THRU COUNT DO
		(INTERN:PART(LISTALL,INDEX2),DENOM:INNERPRODUCT(INTERN,INTERN),
		UNIT:FACTOR(RATSIMP(UNIT-INNERPRODUCT(INTERN,UNIT)*INTERN/DENOM))),
		COUNT:COUNT+1,LISTALL:ENDCONS(UNIT,LISTALL)),
		RETURN(LISTALL)))$


EIGENVALUES(MAT):=
		BLOCK([DIMNSN,LISTALL,SOLUTION,MULTIPLICITIES,SOLVEEXPLICIT,
	        dummy:?GENSYM(),INDEX2],
		LISTALL:[],
		SOLVEEXPLICIT:TRUE,
		DIMNSN:LENGTH(MAT),
		SOLUTION:BLOCK([PROGRAMMODE:TRUE],
	                       SOLVE(CHARPOLY(MAT,DUMMY),DUMMY)),
		IF SOLUTION=[] THEN 
		(PRINT("SOLVE is unable to find the roots of characteristic polynomial."),
		RETURN(LISTALL))
		ELSE (FOR INDEX2 THRU DIMNSN DO
		(DIMNSN:DIMNSN-PART(MULTIPLICITIES,INDEX2)+1,
		LISTALL:ENDCONS(RHS(PART(SOLUTION,INDEX2)),LISTALL)),
		LISTALL:ENDCONS(MULTIPLICITIES,[LISTALL]),
		RETURN(LISTALL)))$


EIGENVECTORS(MAT):=
		BLOCK([EQUATIONS,UNKNOWNS,SOLUTION,LISTALL,EIGVALS,DIMNSN,COUNT,VECTR,
		INDEX3,INDEX4,INDEX2,INDEX1,NOTKNWN,MATRX,MMATRX,UNIT,MULTIPLICITIES,
		%RNUM,REALONLY,INTERM,INTERN],
		LOCAL(NOTKNWN),UNKNOWNS:[],DIMNSN:LENGTH(MAT),COUNT:DIMNSN,
		IF KNOWNEIGVALS THEN EIGVALS:LISTEIGVALS
		ELSE EIGVALS:EIGENVALUES(MAT),
		IF EIGVALS=[] THEN (NONDIAGONALIZABLE:TRUE,RETURN(EIGVALS))
		ELSE (MULTIPLICITIES:PART(EIGVALS,2),
		FOR INDEX1 THRU DIMNSN DO UNKNOWNS:ENDCONS(NOTKNWN[INDEX1],UNKNOWNS),
		VECTR:COLUMNVECTOR(UNKNOWNS),MATRX:MAT.VECTR,NONDIAGONALIZABLE:FALSE,
		LISTALL:[EIGVALS],REALONLY:FALSE,
		FOR INDEX1 THRU COUNT DO 
		 (COUNT:COUNT-PART(MULTIPLICITIES,INDEX1)+1,
		  MMATRX:MATRX-PART(EIGVALS,1,INDEX1)*VECTR,
		  EQUATIONS:[],
		  FOR INDEX2 THRU DIMNSN DO EQUATIONS:CONS(MMATRX[INDEX2,1],EQUATIONS),%RNUM:0,
		  SOLUTION:ALGSYS(EQUATIONS,UNKNOWNS),INTERM:MAP(RHS,SOLUTION[1]),
		  UNIT:[],IF %RNUM#PART(MULTIPLICITIES,INDEX1) THEN NONDIAGONALIZABLE:TRUE,
		  FOR INDEX3 THRU %RNUM DO
		   (INTERN:SUBSTVECTK(%RNUM_LIST,INDEX3,INTERM),
                    UNIT:APPEND(UNIT,[INTERN])),

		  IF HERMITIANMATRIX AND %RNUM>1 THEN UNIT:GRAMSCHMIDT(UNIT),
		  LISTALL:APPEND(LISTALL,UNIT)),

		RETURN(LISTALL)))$


mEIGENVECTORS(MAT):=
		BLOCK([EIGVALS:if knowneigvals then listeigvals
		                               else eigenvalues(mat),
		       DIMNSN:length(mat),
		       UNIT],
		IF EIGVALS=[] THEN (NONDIAGONALIZABLE:TRUE,RETURN(EIGVALS)),
		NONDIAGONALIZABLE:FALSE,
		cons(eigvals,
		     apply('append,
		            apply('map,
			           cons(lambda([val,multiplicity],
		      unit:args(transpose(%meig(echelon(mat-val*ident(dimnsn)),
			                        dimnsn))),
		      if multiplicity#length(unit) then nondiagonalizable:true,
		      if hermitianmatrix then gramschmidt(unit) else unit),
					eigvals)))))$

%meig(mat,dimnsn):=
      if mat=matrix() then matrix()
		      else
      if mat[1,1]=0 then addcol(ematrix(dimnsn,1,1,1,1),
                                (%meig(submatrix(dimnsn,mat,1),
				       dimnsn-1),
				 addrow(matrix(0*%%[1]),%%)))
		    else (%meig(submatrix(1,mat,1),dimnsn-1),
		          addrow(-rest(mat[1]).%%,%%))$

/* The first arg is of the form [r1,r2,r3]
    we want to construct [r1=0,r2=1,r3=0] for example. */		

SUBSTVECTK(L,N,EXP):=
 BLOCK([SUB_LIST:[],J:0],
       FOR VAR IN L DO
       (J:J+1,
        SUB_LIST:CONS(VAR = IF J=N THEN 1 ELSE 0,SUB_LIST)),
       SUBLIS(SUB_LIST,EXP))$


UNITEIGENVECTORS(MAT):=
		BLOCK([LISTUEVEC,LISTALL,INDEX1,UNIT],
		IF KNOWNEIGVECTS THEN LISTUEVEC:LISTEIGVECTS
		ELSE LISTUEVEC:EIGENVECTORS(MAT),
		IF LISTUEVEC=[] THEN RETURN(LISTUEVEC)
		ELSE (LISTALL:[PART(LISTUEVEC,1)],
		FOR INDEX1:2 THRU LENGTH(LISTUEVEC) DO 
		(UNIT:PART(LISTUEVEC,INDEX1),
		UNIT:RATSIMP(UNITVECTOR(UNIT)),
		LISTALL:ENDCONS(UNIT,LISTALL)),
		RETURN(LISTALL)))$


SIMILARITYTRANSFORM(MAT):=
		BLOCK([LISTVEC,LISTUEVEC],
		LISTUEVEC:UNITEIGENVECTORS(MAT),
		IF NONDIAGONALIZABLE THEN RETURN(LISTUEVEC)
		ELSE (LISTVEC:DELETE(PART(LISTUEVEC,1),LISTUEVEC),
		RIGHTMATRIX:TRANSPOSE(APPLY(MATRIX,LISTVEC)),
		IF HERMITIANMATRIX THEN LEFTMATRIX:CONJUGATE(TRANSPOSE(RIGHTMATRIX))
		ELSE LEFTMATRIX:RIGHTMATRIX^^-1,
		RETURN(LISTUEVEC)))$


CONJ(X):=CONJUGATE(X)$

INPROD(X,Y):=INNERPRODUCT(X,Y)$

UVECT(X):=UNITVECTOR(X)$

COVECT(X):=COLUMNVECTOR(X)$

GSCHMIT(X):=GRAMSCHMIDT(X)$

EIVALS(MAT):=EIGENVALUES(MAT)$

EIVECTS(MAT):=EIGENVECTORS(MAT)$

UEIVECTS(MAT):=UNITEIGENVECTORS(MAT)$

SIMTRAN(MAT):=SIMILARITYTRANSFORM(MAT)$

