{ This file is part of the Numlib package. Copyright (c) 1986-2000 by Kees van Ginneken, Wil Kortsmit and Loek van Reij of the Computational centre of the Eindhoven University of Technology FPC port Code by Marco van de Voort (marco@freepascal.org) Documentation by Michael van Canneyt (Michael@freepascal.org) This is a helper unit for the unit eig. These functions aren't documented, so if you find out what it does, please mail it to us. See the file COPYING.FPC, included in this distribution, for details about the copyright. This program 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. **********************************************************************} unit eigh2; {$I DIRECT.INC} interface uses typ; procedure orthes(var a: ArbFloat; n, rwidth: ArbInt; var u: ArbFloat); procedure hessva(var h: ArbFloat; n, rwidth: ArbInt; var lam: complex; var term: ArbInt); procedure balance(var a: ArbFloat; n, rwidtha: ArbInt; var low, hi: ArbInt; var d: ArbFloat); procedure orttrans(var a: ArbFloat; n, rwidtha: ArbInt; var q: ArbFloat; rwidthq: ArbInt); procedure balback(var pd: ArbFloat; n, m1, m2, k1, k2: ArbInt; var pdx: ArbFloat; rwidth: ArbInt); procedure hessvec(var h: ArbFloat; n, rwidthh: ArbInt; var lam: complex; var v: ArbFloat; rwidthv: ArbInt; var term: ArbInt); procedure normeer(var lam: complex; n: ArbInt; var v: ArbFloat; rwidthv: ArbInt); procedure transx(var v: ArbFloat; n, rwidthv: ArbInt; var lam, x: complex; rwidthx: ArbInt); procedure reduc1(var a: ArbFloat; n, rwidtha: ArbInt; var b: ArbFloat; rwidthb: ArbInt; var term: ArbInt); procedure rebaka(var l: ArbFloat; n, rwidthl, k1, k2: ArbInt; var x: ArbFloat; rwidthx: ArbInt; var term: ArbInt); implementation procedure orthes(var a: ArbFloat; n, rwidth: ArbInt; var u: ArbFloat); var pa, pu, d : ^arfloat1; sig, sig2, h, f, tol : ArbFloat; k, i, j : ArbInt; begin pa:=@a; pu:=@u; tol:=midget/macheps; getmem(d, n*sizeof(ArbFloat)); for k:=1 to n-2 do begin sig2:=0; for i:=k+2 to n do begin d^[i]:=pa^[(i-1)*rwidth+k]; f:=d^[i]; sig2:=sig2+sqr(f) end; {i} if sig2= 1) and (term=1) do begin n1:=(nn-1)*rwidth; na:=nn-1; n2:=(na-1)*rwidth; its:=0; repeat l:=nn+1; test:=true; while test and (l>2) do begin l:=l-1; test:=abs(ph^[(l-1)*(rwidth+1)]) > meps*(abs(ph^[(l-2)*rwidth+l-1])+abs(ph^[(l-1)*rwidth+l])) end; if (l=2) and test then l:=l-1; if l l then test:=abs(ph^[mr-rwidth+m-1])*(abs(q)+abs(r)) <= meps*abs(p)*(abs(ph^[mr-2*rwidth+m-1])+abs(z)+ abs(ph^[mr+m+1])) until (m=l) or test; for i:=m+2 to nn do ph^[(i-1)*rwidth+i-2]:=0; for i:=m+3 to nn do ph^[(i-1)*rwidth+i-3]:=0; { double qp-step involving rows l to nn and columns m to nn} for k:=m to na do begin notlast:=k <> na; if k <> m then begin p:=ph^[(k-1)*(rwidth+1)]; q:=ph^[k*rwidth+k-1]; if notlast then r:=ph^[(k+1)*rwidth+k-1] else r:=0; x:=abs(p)+abs(q)+abs(r); if x>0 then begin p:=p/x; q:=q/x; r:=r/x end end else x:=1; if x>0 then begin s:=sqrt(p*p+q*q+r*r); if p<0 then s:=-s; if k <> m then ph^[(k-1)*(rwidth+1)]:=-s*x else if l <> m then begin kk:=(k-1)*(rwidth+1); ph^[kk]:=-ph^[kk] end; p:=p+s; x:=p/s; y:=q/s; z:=r/s; q:=q/p; r:=r/p; { row moxification} for j:=k to nn do begin k1:=(k-1)*rwidth+j; k2:=k1+rwidth; k3:=k2+rwidth; p:=ph^[k1]+q*ph^[k2]; if notlast then begin p:=p+r*ph^[k3]; ph^[k3]:=ph^[k3]-p*z; end; ph^[k2]:=ph^[k2]-p*y; ph^[k1]:=ph^[k1]-p*x; end; {j} if k+3 0} end {k}; end; {l < na} its:=its+1 until (l=na) or (l=nn) or (its=30); if l=nn then begin { one root found} plam^[nn].Init(ph^[n1+nn]+t, 0); nn:=na end else if l=na then begin { two roots found} x:=ph^[n1+nn]; y:=ph^[n2+na]; w:=ph^[n1+na]*ph^[n2+nn]; p:=(y-x)/2; q:=p*p+w; y:=sqrt(abs(q)); x:=x+t; if q>0 then begin { ArbFloat pair} if p<0 then y:=-y; y:=p+y; plam^[na].Init(x+y, 0); plam^[nn].Init(x-w/y, 0) end else begin { complex pair} plam^[na].Init(x+p, y); plam^[nn].Init(x+p, -y) end; nn:=nn-2 end else term:=2 end {while } end {hessva}; procedure balance(var a: ArbFloat; n, rwidtha: ArbInt; var low, hi: ArbInt; var d: ArbFloat); const radix = 2; var i, j, k, l, ii, jj: ArbInt; b2, b, c, f, g, r, s: ArbFloat; pa, pd: ^arfloat1; nonconv, cont: boolean; procedure exc(j, k: ArbInt); var i, ii, jj, kk: ArbInt; h: ArbFloat; begin pd^[k]:=j; if j <> k then begin for i:=1 to n do begin ii:=(i-1)*rwidtha; h:=pa^[ii+j]; pa^[ii+j]:=pa^[ii+k]; pa^[ii+k]:=h end; {i} for i:=1 to n do begin jj:=(j-1)*rwidtha+i; kk:=(k-1)*rwidtha+i; h:=pa^[jj]; pa^[jj]:=pa^[kk]; pa^[kk]:=h end; {i} end {j <> k} end {exc}; begin pa:=@a; pd:=@d; b:=radix; b2:=b*b; l:=1; k:=n; cont:=true; while cont do begin j:=k+1; repeat j:=j-1; r:=0; jj:=(j-1)*rwidtha; for i:=1 to j-1 do r:=r+abs(pa^[jj+i]); for i:=j+1 to k do r:=r+abs(pa^[jj+i]); until (r=0) or (j=1); if r=0 then begin exc(j,k); k:=k-1 end; cont:=(r=0) and (k >= 1); end; cont:= true ; while cont do begin j:=l-1; repeat j:=j+1; r:=0; for i:=l to j-1 do r:=r+abs(pa^[(i-1)*rwidtha+j]); for i:=j+1 to k do r:=r+abs(pa^[(i-1)*rwidtha+j]) until (r=0) or (j=k); if r=0 then begin exc(j,l); l:=l+1 end; cont:=(r=0) and (l <= k); end; for i:=l to k do pd^[i]:=1; low:=l; hi:=k; nonconv:=l <= k; while nonconv do begin for i:=l to k do begin c:=0; r:=0; for j:=l to i-1 do begin c:=c+abs(pa^[(j-1)*rwidtha+i]); r:=r+abs(pa^[(i-1)*rwidtha+j]) end; for j:=i+1 to k do begin c:=c+abs(pa^[(j-1)*rwidtha+i]); r:=r+abs(pa^[(i-1)*rwidtha+j]) end; g:=r/b; f:=1; s:=c+r; while c= g do begin f:=f/b; c:=c/b2 end; if (c+r)/f<0.95*s then begin g:=1/f; pd^[i]:=pd^[i]*f; ii:=(i-1)*rwidtha; for j:=l to n do pa^[ii+j]:=pa^[ii+j]*g; for j:=1 to k do pa^[(j-1)*rwidtha+i]:=pa^[(j-1)*rwidtha+i]*f; end else nonconv:=false end end {while} end; {balance} procedure orttrans(var a: ArbFloat; n, rwidtha: ArbInt; var q: ArbFloat; rwidthq: ArbInt); var i, j, k : ArbInt; sig, sig2, f, h, tol : ArbFloat; pa, pq, d : ^arfloat1; begin pa:=@a; pq:=@q; tol:=midget/macheps; getmem(d, n*sizeof(ArbFloat)); for k:=1 to n-2 do begin sig2:=0; for i:=k+2 to n do begin d^[i]:=pa^[(i-1)*rwidtha+k]; f:=d^[i]; sig2:=sig2+sqr(f) end; if sig2 0 then begin for i:=k+2 to n do d^[i]:=pa^[(i-1)*rwidtha+k]; for i:=k+2 to n do pa^[(i-1)*rwidtha+k]:=0; for j:=k+1 to n do begin f:=0; for i:=k+1 to n do f:=f+d^[i]*pq^[(i-1)*rwidthq+j]; f:=f/h; for i:=k+1 to n do pq^[(i-1)*rwidthq+j]:=pq^[(i-1)*rwidthq+j]+f*d^[i] end end end; freemem(d, n*sizeof(ArbFloat)); end; {orttrans} procedure balback(var pd: ArbFloat; n, m1, m2, k1, k2: ArbInt; var pdx: ArbFloat; rwidth: ArbInt); var i, j, k, ii, kk: ArbInt; s: ArbFloat; ppd, ppdx: ^arfloat1; begin ppd:=@pd; ppdx:=@pdx; for i:=m1 to m2 do begin ii:=(i-1)*rwidth; s:=ppd^[i]; for j:=k1 to k2 do ppdx^[ii+j]:=ppdx^[ii+j]*s; end; for i:=m1-1 downto 1 do begin k:=round(ppd^[i]); ii:=(i-1)*rwidth; kk:=(k-1)*rwidth; if k <> i then for j:=k1 to k2 do begin s:=ppdx^[ii+j]; ppdx^[ii+j]:=ppdx^[kk+j]; ppdx^[kk+j]:=s end end; for i:=m2+1 to n do begin k:=round(ppd^[i]); ii:=(i-1)*rwidth; kk:=(k-1)*rwidth; if k <> i then for j:=k1 to k2 do begin s:=ppdx^[ii+j]; ppdx^[ii+j]:=ppdx^[kk+j]; ppdx^[kk+j]:=s end end end; {balback} procedure cdiv(xr, xi, yr, yi: ArbFloat; var zr, zi: ArbFloat); var h:ArbFloat; begin if abs(yr)>abs(yi) then begin h:=yi/yr; yr:=h*yi+yr; zr:=(xr+h*xi)/yr; zi:=(xi-h*xr)/yr; end else begin h:=yr/yi; yi:=h*yr+yi; zr:=(h*xr+xi)/yi; zi:=(h*xi-xr)/yi end end; {cdiv} procedure hessvec(var h: ArbFloat; n, rwidthh: ArbInt; var lam: complex; var v: ArbFloat; rwidthv: ArbInt; var term: ArbInt); var iterate, stop, notlast, contin: boolean; i, j, k, l, m, na, its, en, n1, n2, ii, kk, ll, ik, i1, k0, k1, k2, mr: ArbInt; meps, p, q, r, s, t, w, x, y, z, ra, sa, vr, vi, norm: ArbFloat; ph, pv: ^arfloat1; plam : ^arcomp1; begin ph:=@h; pv:=@v; plam:=@lam; term:=1; en:=n; t:=0; meps:=macheps; while (term=1) and (en>=1) do begin its:=0; na:=en-1; iterate:=true; while iterate and (term=1) do begin l:=en; contin:=true; while (l>=2) and contin do begin ll:=(l-1)*rwidthh+l; if abs(ph^[ll-1])>meps*(abs(ph^[ll-rwidthh-1])+abs(ph^[ll])) then l:=l-1 else contin:=false end; n1:=(na-1)*rwidthh; n2:=(en-1)*rwidthh; x:=ph^[n2+en]; if l=en then begin iterate:=false; plam^[en].Init(x+t, 0); ph^[n2+en]:=x+t; en:=en-1 end else if l=en-1 then begin iterate:=false; y:=ph^[n1+na]; w:=ph^[n2+na]*ph^[n1+en]; p:=(y-x)/2; q:=p*p+w; z:=sqrt(abs(q)); x:=x+t; ph^[n2+en]:=x; ph^[n1+na]:=y+t; if q>0 then begin if p<0 then z:=p-z else z:=p+z; plam^[na].Init(x+z, 0); s:=x-w/z; plam^[en].Init(s, 0); x:=ph^[n2+na]; r:=sqrt(x*x+z*z); p:=x/r; q:=z/r; for j:=na to n do begin z:=ph^[n1+j]; ph^[n1+j]:=q*z+p*ph^[n2+j]; ph^[n2+j]:=q*ph^[n2+j]-p*z end; for i:=1 to en do begin ii:=(i-1)*rwidthh; z:=ph^[ii+na]; ph^[ii+na]:=q*z+p*ph^[ii+en]; ph^[ii+en]:=q*ph^[ii+en]-p*z; end; for i:=1 to n do begin ii:=(i-1)*rwidthv; z:=pv^[ii+na]; pv^[ii+na]:=q*z+p*pv^[ii+en]; pv^[ii+en]:=q*pv^[ii+en]-p*z; end end {q>0} else begin plam^[na].Init(x+p, z); plam^[en].Init(x+p, -z) end; en:=en-2; end {l=en-1} else begin y:=ph^[n1+na]; w:=ph^[n1+en]*ph^[n2+na]; if (its=10) or (its=20) then begin t:=t+x; for i:=1 to en do ph^[(i-1)*rwidthh+i]:=ph^[(i-1)*rwidthh+i]-x; s:=abs(ph^[n2+na])+abs(ph^[n1+en-2]); y:=0.75*s; x:=y; w:=-0.4375*s*s; end; m:=en-1; stop:=false; repeat m:=m-1; mr:=m*rwidthh; z:=ph^[mr-rwidthh+m]; r:=x-z; s:=y-z; p:=(r*s-w)/ph^[mr+m]+ph^[mr-rwidthh+m+1]; q:=ph^[mr+m+1]-z-r-s; r:=ph^[mr+rwidthh+m+1]; s:=abs(p)+abs(q)+abs(r); p:=p/s; q:=q/s; r:=r/s; if m>l then stop:=abs(ph^[mr-rwidthh+m-1])*(abs(q)+abs(r))<= meps*abs(p)*(abs(ph^[mr-2*rwidthh+m-1])+ abs(z)+abs(ph^[mr+m+1])) until stop or (m=l); for i:=m+2 to en do ph^[(i-1)*rwidthh+i-2]:=0; for i:=m+3 to en do ph^[(i-1)*rwidthh+i-3]:=0; for k:=m to na do begin k0:=(k-1)*rwidthh; k1:=k0+rwidthh; k2:=k1+rwidthh; notlast:=km then begin p:=ph^[k0+k-1]; q:=ph^[k1+k-1]; if notlast then r:=ph^[k2+k-1] else r:=0; x:=abs(p)+abs(q)+abs(r); if x>0 then begin p:=p/x; q:=q/x; r:=r/x end else contin:=false end; if contin then begin s:=sqrt(p*p+q*q+r*r); if p<0 then s:=-s; if k>m then ph^[k0+k-1]:=-s*x else if l <> m then ph^[k0+k-1]:=-ph^[k0+k-1]; p:=p+s; x:=p/s; y:=q/s; z:=r/s; q:=q/p; r:=r/p; for j:=k to n do begin p:=ph^[k0+j]+q*ph^[k1+j]; if notlast then begin p:=p+r*ph^[k2+j]; ph^[k2+j]:=ph^[k2+j]-p*z end; ph^[k1+j]:=ph^[k1+j]-p*y; ph^[k0+j]:=ph^[k0+j]-p*x end; {j} if k+3= 30 then term:=2 end {ifl} end {iterate} end; {term=1} if term=1 then begin norm:=0; k:=1; for i:=1 to n do begin for j:=k to n do norm:=norm+abs(ph^[(i-1)*rwidthh+j]); k:=i end; if norm=0 then begin { matrix is nulmatrix: eigenwaarden zijn alle 0 en aan de eigenvectoren worden de eenheidsvectoren toegekend } for i:=1 to n do plam^[i].Init(0, 0); for i:=1 to n do fillchar(pv^[(i-1)*rwidthv+1], n*sizeof(ArbFloat), 0); for i:=1 to n do pv^[(i-1)*rwidthv+i]:=1; exit end; {norm=0} for en:=n downto 1 do begin p:=plam^[en].re; q:=plam^[en].im; na:=en-1; n1:=(na-1)*rwidthh; n2:=(en-1)*rwidthh; if q=0 then begin m:=en; ph^[n2+en]:=1; for i:=na downto 1 do begin ii:=(i-1)*rwidthh; i1:=ii+rwidthh; w:=ph^[ii+i]-p; r:=ph^[ii+en]; for j:=m to na do r:=r+ph^[ii+j]*ph^[(j-1)*rwidthh+en]; if plam^[i].im < 0 then begin z:=w; s:=r end else begin m:=i; if plam^[i].im=0 then if w=0 then ph^[ii+en]:=-r/(meps*norm) else ph^[ii+en]:=-r/w else begin x:=ph^[ii+i+1]; y:=ph^[i1+i]; q:=sqr(plam^[i].xreal-p)+sqr(plam^[i].imag); ph^[ii+en]:=(x*s-z*r)/q; t:=ph^[ii+en]; if abs(x)>abs(z) then ph^[i1+en]:=(-r-w*t)/x else ph^[i1+en]:=(-s-y*t)/z; end {plam^[i].imag > 0} end {plam^[i].imag >= 0} end {i} end {q=0} else if q<0 then begin m:=na; if abs(ph^[n2+na]) > abs(ph^[n1+en]) then begin ph^[n1+na]:=-(ph^[n2+en]-p)/ph^[n2+na]; ph^[n1+en]:=-q/ph^[n2+na]; end else cdiv(-ph^[n1+en], 0, ph^[n1+na]-p, q, ph^[n1+na], ph^[n1+en]); ph^[n2+na]:=1; ph^[n2+en]:=0; for i:=na-1 downto 1 do begin ii:=(i-1)*rwidthh; i1:=ii+rwidthh; w:=ph^[ii+i]-p; ra:=ph^[ii+en]; sa:=0; for j:=m to na do begin ra:=ra+ph^[ii+j]*ph^[(j-1)*rwidthh+na]; sa:=sa+ph^[ii+j]*ph^[(j-1)*rwidthh+en] end; if plam^[i].imag < 0 then begin z:=w; r:=ra; s:=sa end else begin m:=i; if plam^[i].imag=0 then cdiv(-ra, -sa, w, q, ph^[ii+na], ph^[ii+en]) else begin x:=ph^[ii+i+1]; y:=ph^[i1+i]; vr:=sqr(plam^[i].xreal-p)+sqr(plam^[i].imag)-q*q; vi:=(plam^[i].xreal-p)*q*2; if (vr=0) and (vi=0) then vr:=meps*norm*(abs(w)+abs(q)+abs(x)+ abs(y)+abs(z)); cdiv(x*r-z*ra+q*sa, x*s-z*sa-q*ra, vr, vi, ph^[ii+na], ph^[ii+en]); if abs(x)>abs(z)+abs(q) then begin ph^[i1+na]:=(-ra-w*ph^[ii+na]+q*ph^[ii+en])/x; ph^[i1+en]:=(-sa-w*ph^[ii+en]-q*ph^[ii+na])/x end else cdiv(-r-y*ph^[ii+na], -s-y*ph^[ii+en], z, q, ph^[i1+na], ph^[i1+en]) end {plam^[i].imag > 0} end {plam^[i].imag >= 0} end {i} end end {backsubst}; for j:=n downto 1 do begin m:=j; l:=j-1; if plam^[j].imag < 0 then begin for i:=1 to n do begin ii:=(i-1)*rwidthv; y:=0; z:=0; for k:=1 to m do begin kk:=(k-1)*rwidthh; y:=y+pv^[ii+k]*ph^[kk+l]; z:=z+pv^[ii+k]*ph^[kk+j] end; pv^[ii+l]:=y; pv^[ii+j]:=z end {i} end else if plam^[j].imag=0 then for i:=1 to n do begin z:=0; ii:=(i-1)*rwidthv; for k:=1 to m do z:=z+pv^[ii+k]*ph^[(k-1)*rwidthh+j]; pv^[ii+j]:=z; end {i} end {j} end {term=1} end; {hessvec} procedure normeer(var lam: complex; n: ArbInt; var v: ArbFloat; rwidthv: ArbInt); var i, j, k, ii, kk: ArbInt; max, s, t, vr, vi: ArbFloat; pv: ^arfloat1; plam: ^arcomp1; begin plam:=@lam; pv:=@v; j:=1; while j<=n do if plam^[j].imag=0 then begin s:=0; for i:=1 to n do s:=s+sqr(pv^[(i-1)*rwidthv+j]); s:=sqrt(s); for i:=1 to n do pv^[(i-1)*rwidthv+j]:=pv^[(i-1)*rwidthv+j]/s; j:=j+1 end else begin max:=0; s:=0; for i:=1 to n do begin ii:=(i-1)*rwidthv; t:=sqr(pv^[ii+j])+sqr(pv^[ii+j+1]); s:=s+t; if t>max then begin max:=t; k:=i end end; kk:=(k-1)*rwidthv; s:=sqrt(max/s); t:=pv^[kk+j+1]/s; s:=pv^[kk+j]/s; for i:=1 to n do begin ii:=(i-1)*rwidthv; vr:=pv^[ii+j]; vi:=pv^[ii+j+1]; cdiv(vr, vi, s, t, pv^[ii+j], pv^[ii+j+1]); end; pv^[kk+j+1]:=0; j:=j+2; end end; {normeer} procedure transx(var v: ArbFloat; n, rwidthv: ArbInt; var lam, x: complex; rwidthx: ArbInt); var i, j, ix, iv : ArbInt; pv : ^arfloat1; plam, px : ^arcomp1; begin pv:=@v; plam:=@lam; px:=@x; for i:=1 to n do if plam^[i].imag > 0 then for j:=1 to n do begin iv:=(j-1)*rwidthv+i; ix:=(j-1)*rwidthx+i; px^[ix].xreal:=pv^[iv]; px^[ix].imag:=pv^[iv+1] end else if plam^[i].imag < 0 then for j:=1 to n do begin iv:=(j-1)*rwidthv+i; ix:=(j-1)*rwidthx+i; px^[ix].xreal:=pv^[iv-1]; px^[ix].imag:=-pv^[iv] end else for j:=1 to n do begin iv:=(j-1)*rwidthv+i; ix:=(j-1)*rwidthx+i; px^[ix].xreal:=pv^[iv]; px^[ix].imag:=0 end end; {transx} procedure reduc1(var a: ArbFloat; n, rwidtha: ArbInt; var b: ArbFloat; rwidthb: ArbInt; var term: ArbInt); var i, j, k, ia, ja, ib, jb : ArbInt; x, y : ArbFloat; pa, pb : ^arfloat1; begin pa:=@a; pb:=@b; term:=1; i:=0; while (i