From 14e965b3f90ecf885f078b04019c5c73e2a1d02d Mon Sep 17 00:00:00 2001
From: Christophe Geuzaine <cgeuzaine@ulg.ac.be>
Date: Tue, 14 Dec 2004 20:05:44 +0000
Subject: [PATCH] - Replaced the general eigenvalue solver with a version from
 Laurent's MatLib code (a bit more stable thanks to floating point checks with
 adjustable tolerance)

- Added a solver for symmetric matrices (again from Laurent. Thanks, dude!)

- Added a small driver for 3x3 matrices, that selects the appropriate
solver (sym/nonsym) automatically
---
 Numeric/EigSolve.cpp    | 1606 +++++++++++++++++++--------------------
 Numeric/Makefile        |    6 +-
 Numeric/Numeric.h       |    3 -
 Plugin/Eigenvectors.cpp |   35 +-
 Plugin/Makefile         |    5 +-
 doc/CREDITS             |   10 +-
 6 files changed, 820 insertions(+), 845 deletions(-)

diff --git a/Numeric/EigSolve.cpp b/Numeric/EigSolve.cpp
index 4b73c31c5f..76ecc0ddf3 100644
--- a/Numeric/EigSolve.cpp
+++ b/Numeric/EigSolve.cpp
@@ -1,4 +1,4 @@
-// $Id: EigSolve.cpp,v 1.2 2004-12-08 16:44:33 geuzaine Exp $
+// $Id: EigSolve.cpp,v 1.3 2004-12-14 20:05:43 geuzaine Exp $
 //
 // Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
 //
@@ -18,565 +18,809 @@
 // USA.
 // 
 // Please report all bugs and problems to <gmsh@geuz.org>.
-
-// This file contains a routine that numerically finds the eigenvalues
-// and eigenvectors of a N X N Real Matrix. It is a C++ translation of
-// a Javascript translation of Fortran routines from EISPACK. The
-// Javascript implementation comes from http://www.akiti.ca/Eig3Solv.html
-//
-// References:
 //
-// Smith, B.T.; J.M. Boyle; J.J. Dongarra; B.S. Garbow; Y. Ikebe;
-// V.C. Klema; and C.B. Moler.
-//           "Matrix Eigensystem Routines--(EISPACK) Guide"
-//           Springer-Verlag, Berlin.
-//           1976
-//
-// Garbow, B.S.; J.M. Boyle; J.J. Dongarra; and C.B. Moler.
-//           "Matrix Eigensystem Routines--(EISPACK) Guide Extension"
-//           Springer-Verlag, Berlin.
-//           1977
+// Contributor(s):
+//   Laurent Stainier
+
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define PREC  1.e-16
+
+// Solve eigenproblem for a real general matrix (based on a C++
+// reimplementation of lapack routines from Laurent Stainier)
 //
-// IMPORTANT! How to use the output:
+// IMPORTANT! How to use the output of EigSolve():
 //
-// If the i-th eigenvalue is real, the i-th COLUMN of the eigenvector
-// Matrix contains the corresponding eigenvector.
+// 1) If the i-th eigenvalue is real, the i-th COLUMN of the
+// eigenvector Matrix contains the corresponding eigenvector.
 //
-// If the i-th eigenvalue is complex with positive imaginary part,
+// 2) If the i-th eigenvalue is complex with positive imaginary part,
 // COLUMNS i and (i + 1) contain the real and imaginary parts of the
 // corresponding eigenvector. The conjugate of this vector is the
 // eigenvector for the conjugate eigenvalue.
 //
-// Note the Error Code. If it does not equal -1, some eigenvalues and
+// Note the return value. If it does not equal 1, some eigenvalues and
 // all eigenvectors are meaningless.
 
-#include "Gmsh.h"
-#include "Numeric.h"
+static void swap(double *a,int inca,double *b,int incb,int n)
+{
+  double tmp;
+  for (int i=0; i < n; i++, a+=inca, b+=incb) {
+    tmp = (*a);
+    (*a) = (*b);
+    (*b) = tmp;
+  }
+}
 
-static void cdivA(int N, double ar, double ai, double br, double bi, 
-		  double **A, int in1, int in2, int in3)
+static void cdiv(double ar,double ai,double br,double bi,double& cr,double& ci) 
 {
-  // Division routine for dividing one complex number into another:
-  // This routine does (ar + ai)/(br + bi) and returns the results in
-  // the specified elements of the A matrix.
-  
-  double s, ars, ais, brs, bis;
-  
-  s = fabs(br) + fabs(bi);
-  ars = ar/s;
-  ais = ai/s;
-  brs = br/s;
-  bis = bi/s;
-  s = brs*brs + bis*bis;
-  A[in1][in2] = (ars*brs + ais*bis)/s;
-  A[in1][in3] = (-(ars*bis) + ais*brs)/s;
-  return;
-} // End cdivA
-
-static void hqr2(int N, double **A, double **B, int low, int igh, 
-		 double *wi, double *wr, int ierr)
+  double s = fabs(br)+fabs(bi);
+  double si = 1.e0/s;
+  double ars,ais,brs,bis;
+  ars = ar*si;
+  ais = ai*si;
+  brs = br*si;
+  bis = bi*si;
+  s = brs*brs+bis*bis;
+  cr = (ars*brs+ais*bis)/s;
+  ci = (ais*brs-ars*bis)/s;
+}
+
+// Balancing operations
+
+static void balanc(int nm,int n,double *A,int& low,int& high,double *scale) 
 {
-  // Computes the eigenvalues and eigenvectors of a real
-  // upper-Hessenberg Matrix using the QR method.
-  
-  double norm = 0.0, p = 0.0, q = 0.0, ra, s = 0.0, sa, t = 0.0;
-  double tst1, tst2, vi, vr, w, x, y, zz = 0.0, r = 0.0;
-  int k = 0, l = 0, m, mp2, en = igh, incrFlag = 1, its = 0;
-  int itn = 30*N, enm2 = 0, na = 0, notlas;
+  static const double RADIX = 16.e0;
+
+  double b2 = RADIX*RADIX;
+  int i,j,ij,ji,k=0,l=n-1;
+  bool last = false;
   
-  for (int i = 0; i < N; i++){ // Store eigenvalues already isolated
-			       // and compute matrix norm.
-    for (int j = k; j < N; j++)
-      norm += fabs(A[i][j]);
-    k = i;
-    if ((i < low) || (i > igh)){
-      wi[i] = 0.0;
-      wr[i] = A[i][i];
-    } //End if (i < low or i > igh)
-  }  // End for i
-
-  // Search next eigenvalues
-  while (en >= low){
-
-    if (incrFlag) { // Skip this part if incrFlag is set to 0 at very
-		    // end of while loop
-      its = 0;
-      na = en - 1;
-      enm2 = na - 1;
-    } //End if (incrFlag)
+  // search for rows isolating an eigenvalue and push them down
+ ROWS:
+    // check rows
+    for (i=l; i >= 0; i--) {
+      for (j=0, ij=i*nm; j <= l; j++, ij++) {
+        if (i == j) continue;
+        if (A[ij] != 0.e0) break;
+      }
+      if (j > l) {
+        scale[l] = i;
+        if (i != l) { // exchange columns and rows
+          swap(A+i,nm,A+l,nm,l+1);
+          swap(A+i*nm+k,1,A+l*nm+k,1,n-k);
+        }
+        if (l == 0) goto END;
+        l--;
+        goto ROWS;
+      }
+    }
+
+  // search for columns isolating an eigenvalue and push them left
+ COLUMNS:
+    // check column
+    for (j=k; j <= l; j++) {
+      for (i=k, ij=k*nm+j; i <= l; i++, ij+=nm) {
+        if (i == j) continue;
+        if (A[ij] != 0.e0) break;
+      }
+      if (i > l) { // exchange columns and rows
+        scale[k] = j;
+        if (j != k) {
+          swap(A+j,nm,A+k,nm,l+1);
+          swap(A+j*nm+k,1,A+k*nm+k,1,n-k);
+        }
+        k++;
+        goto COLUMNS;
+      }
+    }
+
+  // balance the submatrix in rows/columns k to l
+  for (i=k; i <= l; i++) scale[i] = 1.e0;
+
+  // iterative loop for norm reduction
+  while (!last) {
+    last = true;
+
+    for (i=k; i <= l; i++) {
+
+      // calculate row and column norm
+      double c=0.e0,r=0.e0;
+      for (j=k, ij=i*nm+k, ji=k*nm+i; j <=l ; j++, ij++, ji+=nm) {
+        if (i == j) continue;
+        c += fabs(A[ji]);
+        r += fabs(A[ij]);
+      }
+
+      // if both are non-zero
+      double g,f,s;
+      if ((c > PREC) && (r > PREC)) {
+
+        g = r/RADIX;
+        f = 1.e0;
+        s = c+r;
+        while (c < g) {
+          f *= RADIX;
+          c *= b2;
+        }
+
+        g = r*RADIX;
+        while (c > g) {
+          f /= RADIX;
+          c /= b2;
+        }
+
+        // balance
+        if ((c+r)/f < 0.95*s) {
+          g = 1.e0/f;
+          scale[i] *= f;
+          last = false;
+
+          // apply similarity transformation
+          for (j=k, ij=i*nm+k; j < n; j++, ij++) A[ij] *= g;
+          for (j=0, ji=i  ; j <= l; j++, ji+=nm) A[ji] *= f;
+        }
+      }
+    }
+  }
+
+ END:
+   low = k;
+   high = l;
+}
+
+static void balbak(int nm,int n,int low,int high,double scale[],int m,double *v) 
+{
+  int i,j,k,ii,ij;
+
+  // quick return
+  if (m == 0) return;
+
+  // step 1
+  if (low != high) {
+    for (i=low; i <= high; i++) {
+      double s = scale[i];
+      for (j=0,ij=i; j < m; j++, ij+=nm) v[ij] *= s;
+    }
+  }
+
+  // step 2
+  for (ii=0; ii < n; ii++) {
+    if (ii >= low && ii <= high) continue;
+    if (ii < low)
+      i = low-ii;
     else
-      incrFlag = 1;
-    
-    // Look for single small sub-diagonal element for l = en step -1
-    // until low
-    
-    for (int i = low; i <= en; i++){
-      l = en + low - i;
-      if (l == low)
-	break;
-      s = fabs(A[l - 1][l - 1]) + fabs(A[l][l]);
-      if (s == 0.0)
-	s = norm;
-      tst1 = s;
-      tst2 = tst1 + fabs(A[l][l - 1]);
-      if (tst2 == tst1)
-	break;
-    } //End for i
-    
-    x = A[en][en];
-    
-    if (l == en){  //One root found
-      wr[en] = A[en][en] = x + t;
-      wi[en] = 0.0;
-      en--;
-      continue;
-    } //End if (l == en)
-    
-    y = A[na][na];
-    w = A[en][na]*A[na][en];
-    
-    if (l == na){  //Two roots found
-      p = (-x + y)/2;
-      q = p*p + w;
-      zz = sqrt(fabs(q));
-      x = A[en][en] = x + t;
-      A[na][na] = y + t;
-      if (q >= 0.0){//Real Pair
-	zz = ((p < 0.0) ? (-zz + p) : (p + zz));
-	wr[en] = wr[na] = x + zz;
-	if (zz != 0.0)
-	  wr[en] = -(w/zz) + x;
-	wi[en] = wi[na] = 0.0;
-	x = A[en][na];
-	s = fabs(x) + fabs(zz);
-	p = x/s;
-	q = zz/s;
-	r = sqrt(p*p + q*q);
-	p /= r;
-	q /= r;
-	for (int j = na; j < N; j++){ //Row modification
-	  zz = A[na][j];
-	  A[na][j] = q*zz + p*A[en][j];
-	  A[en][j] = -(p*zz) + q*A[en][j];
-	}//End for j
-	for (int j = 0; j <= en; j++){ // Column modification
-	  zz = A[j][na];
-	  A[j][na] = q*zz + p*A[j][en];
-	  A[j][en] = -(p*zz) + q*A[j][en] ;
-	}//End for j
-	for (int j = low; j <= igh; j++){//Accumulate transformations
-	  zz = B[j][na];
-	  B[j][na] = q*zz + p*B[j][en];
-	  B[j][en] = -(p*zz) + q*B[j][en];
-	}//End for j
-      } //End if (q >= 0.0)
-      else {//else q < 0.0
-	wr[en] = wr[na] = x + p;
-	wi[na] = zz;
-	wi[en] = -zz;
-      } //End else
-      en--;
-      en--;
-      continue;
-    }//End if (l == na)
-    
-    if (itn == 0){ //Set error; all eigenvalues have not converged
-		   //after 30 iterations.
-      ierr = en + 1;
-      return;
-    }//End if (itn == 0)
-    
-    if ((its == 10) || (its == 20)){ //Form exceptional shift
-      t += x;
-      for (int i = low; i <= en; i++)
-	A[i][i] += -x;
-      s = fabs(A[en][na]) + fabs(A[na][enm2]);
-      y = x = 0.75*s;
-      w = -(0.4375*s*s);
-    } //End if (its equals 10 or 20)
-    
-    its++;
-    itn--;
-    
-    // Look for two consecutive small sub-diagonal elements. Do m = en
-    // - 2 to l in increments of -1
-    
-    for (m = enm2; m >= l; m--){
-      zz = A[m][m];
-      r = -zz + x;
-      s = -zz + y;
-      p = (-w + r*s)/A[m + 1][m] + A[m][m + 1];
-      q = -(zz + r + s) + A[m + 1][m + 1];
-      r = A[m + 2][m + 1];
-      s = fabs(p) + fabs(q) + fabs(r);
-      p /= s;
-      q /= s;
-      r /= s;
-      if (m == l)
-	break;
-      tst1 = fabs(p) * (fabs(A[m - 1][m - 1]) + fabs(zz) + fabs(A[m + 1][m + 1]));
-      tst2 = tst1 + fabs(A[m][m - 1]) * (fabs(q) + fabs(r));
-      if (tst1 == tst2)
-	break;
-    }//End for m
-    
-    mp2 = m + 2;
-    
-    for (int i = mp2; i <= en; i++){
-      A[i][i - 2] = 0.0;
-      if (i == mp2)
-	continue;
-      A[i][i - 3] = 0.0;
-    }//End for i
-    
-    // Double qr step involving rows l to en and columns m to en.
-    
-    for (int i = m; i <= na; i++){
-      notlas = ((i != na) ? 1 : 0);
-      if (i != m){
-	p = A[i][i - 1];
-	q = A[i + 1][i - 1];
-	r = ((notlas) ? A[i + 2][i - 1] : 0.0);
-	x = fabs(p) + fabs(q) + fabs(r);
-	if (x == 0.0)      //Drop through rest of for i loop
-	  continue;
-	p /= x;
-	q /= x;
-	r /= x;
-      } //End if (i != m)
-      
-      s = sqrt(p*p + q*q + r*r);
-      if (p < 0.0)
-	s = -s;
-      
-      if (i != m)
-	A[i][i - 1] = -(s*x);
-      else {
-	if (l != m)
-	  A[i][i - 1] = -A[i][i - 1];
+      i = ii;
+
+    k = (int) scale[i];
+    if (k == i) continue;
+    swap(v+i,nm,v+k,nm,m);
+  }
+}
+
+// Transformation of a general matrix to upper Hessenberg form
+
+static void elmhes(int nm,int n,int low,int high,double *A,int work[]) 
+{
+  int i,j,k,l,m,ij,ji,im1,jm1,jm,mj;
+
+  k = low+1;
+  l = high-1;
+  if (l < k) return;
+
+  for (m=k; m <= l; m++) {
+    int m1 = m-1;
+    double x = 0.e0;
+
+    // find the pivot
+    i = m;
+    for (j=m, jm1=m*nm+m-1; j <= high; j++, jm1+=nm) {
+      if (fabs(A[jm1]) < fabs(x)) continue;
+      x = A[jm1];
+      i = j;
+    }
+    work[m] = i;
+
+    // interchange rows and columns
+    if (i != m) {
+      swap(A+i*nm+m1,1,A+m*nm+m1,1,n-m1);
+      swap(A+i,nm,A+m,nm,high+1);
+    }
+
+    // carry out elimination
+    if (fabs(x) > PREC) {
+      for (i=m+1, im1=(m+1)*nm+m-1; i <= high; i++, im1+=nm) {
+        double y = A[im1];
+        if (y != 0.e0) {
+          y /= x;
+          A[im1] = y;
+          for (j=m, ij=i*nm+m, mj=m*nm+m; j < n; j++, ij++, mj++) A[ij] -= y*A[mj];
+          for (j=0, ji=i, jm=m; j <= high; j++, ji+=nm, jm+=nm)   A[jm] += y*A[ji];
+        }
+      }
+    }
+  }
+}
+
+static void eltran(int nm,int n,int low,int high,double *A,int work[],double *v) 
+{
+  int i,j,m,ij,im,im1,mj;
+
+  // initialize v to identity matrix
+  for (j=0; j < n; j++) {
+    for (i=0, ij=j*nm; i < n; i++, ij++) v[ij] = 0.e0;
+    v[j+j*nm] = 1.e0;
+  }
+
+  // loop from high-1 to low+1
+  for (m=high-1; m > low; m--) {
+    int m1 = m+1;
+    for (i=m1, im=m1*nm+m, im1=m1+m*nm; i <= high; i++, im+=nm, im1++) 
+      v[im1] = A[im-1];
+
+    i = work[m];
+    if (i == m) continue;
+
+    for (j=m, mj=m+m*nm, ij=i+m*nm; j <= high; j++, mj+=nm, ij+=nm) {
+      v[mj] = v[ij];
+      v[ij] = 0.e0;
+    }
+
+    v[i+m*nm] = 1.e0;
+  }
+}
+
+// Find eigenvalues of a real upper Hessenberg matrix by the QR method
+
+static int hqr(int nm,int n,int low,int high,double *H,double wr[],double wi[],double *vv)
+{
+  int i,j,k,l,m,ij,ij1,ik,ik1,ik2,in,in1,jn,jn1,kj,kj1,kj2,nj,nj1,mmin;
+  double p=0.0e0,q=0.0e0,r=0.0e0,s=0.0e0,x,y,z=0.0e0,u,v,w;
+
+  // store roots isolated by balanc, and compute matrix norm
+  k=0;
+  double norm = 0.e0;
+  for (i=0; i < n; i++) {
+    for (j=k, ij=i*nm+k; j < n; j++, ij++) norm += fabs(H[ij]);
+
+    k = i;
+    if (i >= low && i <= high) continue;
+    wr[i] = H[i*nm+i];
+    wi[i] = 0.e0;
+  }
+
+  // search for next eigenvalues
+  int nn=high,nn1;
+  double t=0.e0;
+  while (nn >= low) {
+
+    int iter=0;
+    do {
+
+      // look for single small subdiagonal element
+      for (l=nn; l > low; l--) {
+        s = fabs(H[(l-1)*(nm+1)])+fabs(H[l*(nm+1)]);
+        if (s == 0.e0) s = norm;
+        if ((fabs(H[l*nm+l-1])+s) == s) break;
+      }
+
+      // form shift
+      x = H[nn*nm+nn];
+      if (l == nn) { // ... one root found
+        wr[nn] = x+t;
+        wi[nn] = 0.e0;
+        H[nn*nm+nn] = wr[nn];
+        nn--;
       }
-      
-      p += s;
-      x = p/s;
-      y = q/s;
-      zz = r/s;
-      q /= p;
-      r /= p;
-      k = ((i + 3 < en) ? i + 3 : en);
-      
-      if (notlas){ //Do row modification
-	for (int j = i; j < N; j++) {
-	  p = A[i][j] + q*A[i + 1][j] + r*A[i + 2][j];
-	  A[i][j] += -(p*x);
-	  A[i + 1][j] += -(p*y);
-	  A[i + 2][j] += -(p*zz);
-	}//End for j
-	
-	for (int j = 0; j <= k; j++) {//Do column modification
-	  p = x*A[j][i] + y*A[j][i + 1] + zz*A[j][i + 2];
-	  A[j][i] += -p;
-	  A[j][i + 1] += -(p*q);
-	  A[j][i + 2] += -(p*r);
-	}//End for j
-	
-	for (int j = low; j <= igh; j++) {//Accumulate transformations
-	  p = x*B[j][i] + y*B[j][i + 1] + zz*B[j][i + 2];
-	  B[j][i] += -p;
-	  B[j][i + 1] += -(p*q);
-	  B[j][i + 2] += -(p*r);
-	} // End for j
-      }//End if notlas
-      
       else {
-	for (int j = i; j < N; j++) {//Row modification
-	  p = A[i][j] + q*A[i + 1][j];
-	  A[i][j] += -(p*x);
-	  A[i + 1][j] += -(p*y);
-	}//End for j
-	
-	for (int j = 0; j <= k; j++){//Column modification
-	  p = x*A[j][i] + y*A[j][i +1];
-	  A[j][i] += -p;
-	  A[j][i + 1] += -(p*q);
-	}//End for j
-	
-	for (int j = low; j <= igh; j++){//Accumulate transformations
-	  p = x*B[j][i] + y*B[j][i +1];
-	  B[j][i] += -p;
-	  B[j][i + 1] += -(p*q);
-	}//End for j
-	
-      } //End else if notlas
-    }//End for i
-    incrFlag = 0;
-  }//End while (en >= low)
-
-  if (norm == 0.0)
-    return;
-  
-  //Step from (N - 1) to 0 in steps of -1
-  
-  for (en = (N - 1); en >= 0; en--){
-    p = wr[en];
-    q = wi[en];
-    na = en - 1;
-    
-    if (q > 0.0)
-      continue;
-    
-    if (q == 0.0){//Real vector
-      m = en;
-      A[en][en] = 1.0;
-      
-      for (int j = na; j >= 0; j--){
-	w = -p + A[j][j];
-	r = 0.0;
-	for (int ii = m; ii <= en; ii++)
-	  r += A[j][ii]*A[ii][en];
-	
-	if (wi[j] < 0.0){
-	  zz = w;
-	  s = r;
-	}//End wi[j] < 0.0
-	else {//wi[j] >= 0.0
-	  m = j;
-	  if (wi[j] == 0.0){
-	    t = w;
-	    if (t == 0.0){
-	      t = tst1 = norm;
-	      do {
-		t *= 0.01;
-		tst2 = norm + t;
-	      } while (tst2 > tst1);
-	    } //End if t == 0.0
-	    A[j][en] = -(r/t);
-	  }//End if wi[j] == 0.0
-	  else { //wi[j] > 0.0; Solve real equations
-	    x = A[j][j + 1];
-	    y = A[j + 1][j];
-	    q = (-p + wr[j])*(-p + wr[j]) + wi[j]*wi[j];
-	    A[j][en] = t = (-(zz*r) + x*s)/q;
-	    A[j + 1][en] = ((fabs(x) > fabs(zz)) ? -(r + w*t)/x : -(s + y*t)/zz);
-	  }//End  else wi[j] > 0.0
-	  
-	  // Overflow control
-	  t = fabs(A[j][en]);
-	  if (t == 0.0)
-	    continue; //go up to top of for j loop
-	  tst1 = t;
-	  tst2 = tst1 + 1.0/tst1;
-	  if (tst2 > tst1)
-	    continue; //go up to top of for j loop
-	  for (int ii = j; ii <= en; ii++)
-	    A[ii][en] /= t;
-	  
-	}//End else wi[j] >= 0.0
-	
-      }//End for j
-      
-    }      //End q == 0.0
-    
-    else {//else q < 0.0, complex vector
-      //Last vector component chosen imaginary so that eigenvector
-      //matrix is triangular
-      m = na;
-      
-      if (fabs(A[en][na]) <= fabs(A[na][en]))
-	cdivA(N, 0.0, -A[na][en], -p + A[na][na], q, A, na, na, en);
+        nn1 = nn-1;
+        y = H[nn1*nm+nn1];
+        w = H[nn*nm+nn1]*H[nn1*nm+nn];
+        if (l == nn1) { // ... two roots found
+          p = 0.5*(y-x);
+          q = p*p+w;
+          z = sqrt(fabs(q));
+          H[nn*nm+nn] = x+t;
+          x += t;
+          H[nn1*nm+nn1] = y+t;
+          if (q >= 0.e0) { // ... a real pair
+            z = p+((p > 0.e0)?(z):(-z));
+            wr[nn1] = wr[nn] = x+z;
+            if (fabs(z) > PREC) wr[nn] = x-w/z;
+            wi[nn1] = wi[nn] = 0.e0;
+
+            x = H[nn*nm+nn1];
+            s = fabs(x)+fabs(z);
+            p = x/s;
+            q = z/s;
+            r = sqrt(p*p+q*q);
+            p /= r;
+            q /= r;
+            // row modification
+            for (j=nn1, nj=nn*nm+nn1, nj1=nn1*nm+nn1; j < n; j++, nj++, nj1++) {
+              z = H[nj1];
+              H[nj1] = q*z+p*H[nj];
+              H[nj]  = q*H[nj]-p*z;
+            }
+            // column modification
+            for (i=0, in=nn, in1=nn1; i <= nn; i++, in+=nm, in1+=nm) {
+              z = H[in1];
+              H[in1] = q*z+p*H[in];
+              H[in]  = q*H[in]-p*z;
+            }
+            // accumulate transformations
+            for (i=low, in=low+nn*nm, in1=low+nn1*nm; i <= high; i++, in++, in1++) {
+              z = vv[in1];
+              vv[in1] = q*z+p*vv[in];
+              vv[in]  = q*vv[in]-p*z;
+            }
+          }
+          else {           // ... a complex pair
+            wr[nn1] = wr[nn] = x+p;
+            wi[nn1] = z;
+            wi[nn] = -z;
+          }
+          nn -= 2;
+        }
+        else {          // ... no roots found. Continue iterations.
+          if (iter == 30) return nn+1;
+          if (iter == 10 || iter == 20) { // form exceptional shift
+            t += x;
+            for (i=low; i <=nn; i++) H[i*nm+i] -= x;
+            s = fabs(H[nn*nm+nn1])+fabs(H[nn1*nm+nn1-1]);
+            y = x = 0.75*s;
+            w = -0.4375*s*s;
+          }
+          iter++;
+
+          // look for two consecutive small sub-diagonal elements
+          for (m=nn-2; m >= l; m--) {
+            z = H[m*nm+m];
+            r = x-z;
+            s = y-z;
+            p = (r*s-w)/H[(m+1)*nm+m]+H[m*nm+m+1];
+            q = H[(m+1)*(nm+1)]-z-r-s;
+            r = H[(m+2)*nm+m+1];
+            s = fabs(p)+fabs(q)+fabs(r);
+            p /= s;
+            q /= s;
+            r /= s;
+            if (m == l) break;
+            u = fabs(p)*(fabs(H[(m-1)*(nm+1)])
+                              +fabs(z)+fabs(H[(m+1)*(nm+1)]));
+            v = u+fabs(H[m*nm+m-1])*(fabs(q)+fabs(r));
+            if (u == v) break;
+          }
+          for (i=m+2; i <= nn; i++) {
+            H[i*nm+i-2] = 0.e0;
+            if (i != (m+2)) H[i*nm+i-3] = 0.e0;
+          }
+
+          // double QR step on rows l to nn and columns m to nn
+          for (k=m; k <= nn1; k++) {
+
+            if (k != m) { // begin setup of Householder vector
+              p = H[k*nm+k-1];
+              q = H[(k+1)*nm+k-1];
+              r = 0.e0;
+              if (k != nn1) r = H[(k+2)*nm+k-1];
+              x = fabs(p)+fabs(q)+fabs(r);
+              if (x < PREC) continue;
+              // scale to prevent underflow or overflow
+              p /= x;
+              q /= x;
+              r /= x;
+            }
+
+            s = sqrt(p*p+q*q+r*r);
+            if (p < 0.e0) s = -s;
+            if (k == m) {
+              if (l != m) H[k*nm+k-1] = -H[k*nm+k-1];
+            }
+            else 
+              H[k*nm+k-1] = -s*x;
+            p += s;
+            x = p/s;
+            y = q/s;
+            z = r/s;
+            q /= p;
+            r /= p;
+
+            // row modification
+            kj = k*nm+k; kj1 = kj+nm; kj2 = kj1+nm;
+            for (j=k; j <= nn; j++, kj++, kj1++,kj2++) {
+              p = H[kj]+q*H[kj1];
+              if (k != nn1) {
+                p += r*H[kj2];
+                H[kj2] -= p*z;
+              }
+              H[kj1] -= p*y;
+              H[kj]  -= p*x;
+            }
+
+            // column modification
+            mmin = (nn < k+3) ? nn : k+3;
+            ik = l*nm+k; ik1 = ik+1; ik2 = ik1+1;
+            for (i=l; i <= mmin; i++, ik+=nm, ik1+=nm, ik2+=nm) {
+              p = x*H[ik]+y*H[ik1];
+              if (k != nn1) {
+                p += z*H[ik2];
+                H[ik2] -= p*r;
+              }
+              H[ik1] -= p*q;
+              H[ik]  -= p;
+            }
+
+            // accumulate transformations
+            ik = low+k*nm; ik1 = ik+nm; ik2 = ik1+nm;
+            for (i=low; i <= high; i++, ik++, ik1++, ik2++) {
+              p = x*vv[ik]+y*vv[ik1];
+              if (k != nn1) {
+                p += z*vv[ik2];
+                vv[ik2] -= p*r;
+              }
+              vv[ik1] -= p*q;
+              vv[ik]  -= p;
+            }
+          }
+        }
+      }
+
+    } while (l < nn-1);
+  }
+
+  // Backsubstitute to find vectors of upper triangular form
+  for (nn=n-1; nn >= 0; nn--) {
+    p = wr[nn];
+    q = wi[nn];
+    nn1 = nn-1;
+    if (q == 0.e0) { // real vector
+      m = nn;
+      H[nn*nm+nn] = 1.e0;
+      for (i=nn1; i >= 0; i--) {
+        in  = i*nm+nn;
+        in1 = i*nm+nn1;
+        w = H[i*nm+i]-p;
+
+        r = 0.e0;
+        for (j=m, ij=i*nm+m, jn=m*nm+nn; j <= nn; j++, ij++, jn+=nm) 
+          r += H[ij]*H[jn];
+
+        if (wi[i] < 0.e0) {
+          z = w;
+          s = r;
+          continue;
+        }
+
+        m = i;
+        if (wi[i] == 0.e0) {
+          t = w;
+          if (t == 0.e0) {
+            u = norm;
+            t = u;
+            do {
+              t *= 0.01;
+              v = norm+t;
+            } while (v > u);
+          }
+          H[in] = -r/t;
+        }
+        else { // solve real equation
+          x = H[i*nm+i+1];
+          y = H[(i+1)*nm+i];
+          q = (wr[i]-p)*(wr[i]-p)+wi[i]*wi[i];
+          t = (x*s-z*r)/q;
+          H[i*nm+nn] = t;
+          if (fabs(x) <= fabs(z))
+            H[in+nm] = (-s-y*t)/z;
+          else
+            H[in+nm] = (-r-w*t)/x;
+        }
+
+        // overflow control
+        t = fabs(H[in]);
+        if (t != 0.e0) {
+          u = t;
+          v = t+1.e0/t;
+          if (u >= v)
+            for (j=i, jn=in; j <= nn; j++, jn+=nm) H[jn] /= t;
+        }
+      }
+    }
+    else if (q < 0.e0) { // complex vector
+      m = nn1;
+
+      // last vector component chosen imaginary, so that eigenvector matrix is triangular
+      if (fabs(H[nn*nm+nn1]) <= fabs(H[nn1*nm+nn]))
+        cdiv(0.e0,-H[nn1*nm+nn],H[nn1*nm+nn1]-p,q,H[nn1*nm+nn1],H[nn1*nm+nn]);
       else {
-	A[na][na] = q/A[en][na];
-	A[na][en] = -(-p + A[en][en])/A[en][na];
-      } //End else (fabs(A[en][na] > fabs(A[na][en])
-      
-      A[en][na] = 0.0;
-      A[en][en] = 1.0;
-      
-      for (int j = (na - 1); j >= 0; j--) {
-	w = -p + A[j][j];
-	sa = ra = 0.0;
-	
-	for (int ii = m; ii <= en; ii++) {
-	  ra += A[j][ii]*A[ii][na];
-	  sa += A[j][ii]*A[ii][en];
-	} //End for ii
-	
-	if (wi[j] < 0.0){
-	  zz = w;
-	  r = ra;
-	  s = sa;
-	  continue;
-	} //End if (wi[j] < 0.0)
-	
-	//else wi[j] >= 0.0
-	m = j;
-	if (wi[j] == 0.0)
-	  cdivA(N, -ra, -sa, w, q, A, j, na, en);
-	else {//wi[j] > 0.0; solve complex equations
-	  x = A[j][j + 1];
-	  y = A[j + 1][j];
-	  vr = -(q*q) + (-p + wr[j])*(-p + wr[j]) + wi[j]*wi[j];
-	  vi = (-p + wr[j])*2.0*q;
-	  if ((vr == 0.0) && (vi == 0.0)){
-	    tst1 = norm*(fabs(w) + fabs(q) + fabs(x) + fabs(y) + fabs(zz));
-	    vr = tst1;
-	    do {
-	      vr *= 0.01;
-	      tst2 = tst1 + vr;
-	    } while (tst2 > tst1);
-	  } //End if vr and vi == 0.0
-	  cdivA(N, -(zz*ra) + x*r + q*sa, -(zz*sa + q*ra) + x*s, vr, vi, A, j, na, en);
-	  
-	  if (fabs(x) > (fabs(zz) + fabs(q))){
-	    A[j + 1][na] = (-(ra + w*A[j][na]) + q*A[j][en])/x;
-	    A[j + 1][en] = -(sa + w*A[j][en] + q*A[j][na])/x;
-	  }//End if
-	  else
-	    cdivA(N, -(r + y*A[j][na]), -(s + y*A[j][en]), zz, q, A, j + 1, na, en);
-	  
-	}//End else wi[j] > 0.0
-	
-	t = ((fabs(A[j][na]) >= fabs(A[j][en])) ? fabs(A[j][na]) : fabs(A[j][en]));
-	
-	if (t == 0.0) continue; // go to top of for j loop
-	
-	tst1 = t;
-	tst2 = tst1 + 1.0/tst1;
-	if (tst2 > tst1) continue; //go to top of for j loop
-	
-	for (int ii = j; ii <= en; ii++){
-	  A[ii][na] /= t;
-	  A[ii][en] /= t;
-	} //End for ii loop
-	
-      } // End for j
-      
-    }//End else q < 0.0
-    
-  }//End for en
+        H[nn1*nm+nn1] = q/H[nn*nm+nn1];
+        H[nn1*nm+nn] = -(H[nn*nm+nn]-p)/H[nn*nm+nn1];
+      }
 
-  //End back substitution. Vectors of isolated roots.
+      for (i=nn-2; i >= 0; i--) {
+        in  = i*nm+nn;
+        in1 = i*nm+nn1;
+        w = H[i*nm+i]-p;
 
-  for (int i = 0; i < N; i++){
-    if ((i < low) || (i > igh)) {
-    for (int j = i; j < N; j++)
-      B[i][j] = A[i][j];
-    }//End if i
-  }//End for i
+        double ra = 0.e0;
+        double sa = 0.e0;
+        for (j=m, ij=i*nm+m, jn=m*nm+nn, jn1=m*nm+nn1; j <= nn; 
+             j++, ij++, jn+=nm, jn1+=nm) {
+          ra += H[ij]*H[jn1];
+          sa += H[ij]*H[jn];
+        }
 
-  // Multiply by transformation matrix to give vectors of original
-  // full matrix.
-  
-  //Step from (N - 1) to low in steps of -1.
+        if (wi[i] < 0.e0) {
+          z = w;
+          r = ra;
+          s = sa;
+          continue;
+        }
 
-  for (int i = (N - 1); i >= low; i--){
-    
-    m = ((i < igh) ? i : igh);
-    
-    for (int ii = low; ii <= igh; ii++){
-      zz = 0.0;
-      for (int jj = low; jj <= m; jj++)
-	zz += B[ii][jj]*A[jj][i];
-      B[ii][i] = zz;
-    }//End for ii
-  }//End of for i loop
-
-  return;
-} //End of function hqr2
-
-static void norVecC(int N, double **Z, double *wi)
+        m = i;
+        if (wi[i] == 0.e0) {
+          cdiv(-ra,-sa,w,q,H[in1],H[in]);
+        }
+        else { // solve complex equations
+          x = H[i*nm+i+1];
+          y = H[(i+1)*nm+i];
+          double vr = (wr[i]-p)*(wr[i]-p)+wi[i]*wi[i]-q*q;
+          double vi = 2*(wr[i]-p)*q;
+          if (vr == 0.e0 && vi == 0.e0) {
+            u = norm*(fabs(w)+fabs(q)+fabs(x)
+                      +fabs(y)+fabs(z));
+            vr = u;
+            do {
+              vr *= 0.01;
+              v = u+vr;
+            } while (v > u);
+          }
+          cdiv(x*r-z*ra+q*sa,x*s-z*sa-q*ra,vr,vi,H[in1],H[in]);
+          if (fabs(x) <= (fabs(z)+fabs(q)))
+            cdiv(-r-y*H[in1],-s-y*H[in],z,q,
+                 H[in1+nm],H[in+nm]);
+          else {
+            H[in1+nm] = (-ra-w*H[in1]+q*H[in])/x;
+            H[in+nm]  = (-sa-w*H[in]-q*H[in1])/x;
+          }
+        }
+
+        // overflow control
+        u = fabs(H[in1]); v = fabs(H[in]);
+        t = (u > v) ? u:v;
+        if (t != 0.e0) {
+          u = t;
+          v = t+1.e0/t;
+          if (u >= v) {
+            for (j=i, jn=in, jn1=in1; j < nn; j++, jn+=nm, jn1+=nm) {
+              H[jn1] /= t;
+              H[jn]  /= t;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  // vectors of isolated roots
+  for (i=0; i < n; i++) {
+    if (i >= low && i <= high) continue;
+    for (j=0, ij=i, ij1=i*nm; j < n; j++, ij+=nm, ij1++) 
+      vv[ij] = H[ij1];
+  }
+
+  // multiply by transformation matrix to give evctors of original full matrix
+  for (j=n-1; j >= 0; j--) {
+    m = (j < high) ? j:high;
+    for (i=low, ij=low+j*nm; i <= high; i++, ij++) {
+      z = 0.e0;
+      for (k=low, ik=i+low*nm, kj=low*nm+j; k <= m; k++, ik+=nm, kj+=nm) 
+        z += vv[ik]*H[kj];
+      vv[ij] = z;
+    }
+  }
+
+  return 0;
+}
+
+// Normalizes the eigenvectors. This subroutine is based on the
+// LINPACK routine SNRM2, written 25 October 1982, modified on 14
+// October 1993 by Sven Hammarling of Nag Ltd.
+
+static void normvec(int n, double *Z, double *wi)
 {
-  // Normalizes the eigenvectors
-
-  // This subroutine is based on the LINPACK routine SNRM2, written 25
-  // October 1982, modified on 14 October 1993 by Sven Hammarling of
-  // Nag Ltd.  I have further modified it for use in this Javascript
-  // routine, for use with a column of an array rather than a column
-  // vector.
-  //
-  // Z - eigenvector Matrix
-  // wi - eigenvalue vector
-  
-  double scale, ssq, absxi, dummy, norm;
-  
-  for (int j = 0; j < N; j++){ //Go through the columns of the vector
-			       //array
-    scale = 0.0;
-    ssq = 1.0;
-    
-    for (int i = 0; i < N; i++){
-      if (Z[i][j] != 0){
-	absxi = fabs(Z[i][j]);
-	dummy = scale/absxi;
+  for (int j = 0; j < n; j++){ // Go through the columns of the array
+    double scale = 0.0;
+    double ssq = 1.0;
+    
+    for (int i = 0; i < n; i++){
+      double absxi = fabs(Z[j*n+i]);
+      if (absxi > PREC){
+	double dummy = scale/absxi;
 	if (scale < absxi){
 	  ssq = 1.0 + ssq*dummy*dummy;
 	  scale = absxi;
-	}//End if (scale < absxi)
+	}
 	else
 	  ssq += 1.0/dummy/dummy;
-      }//End if (Z[i][j] != 0)
-    } //End for i
+      }
+    }
     
-    if (wi[j] != 0){// If complex eigenvalue, take into account
-		    // imaginary part of eigenvector
-      for (int i = 0; i < N; i++){
-	if (Z[i][j + 1] != 0){
-	  absxi = fabs(Z[i][j + 1]);
-	  dummy = scale/absxi;
+    if (fabs(wi[j]) > PREC){ 
+      // If complex eigenvalue, take into account imaginary part of
+      // eigenvector
+      for (int i = 0; i < n; i++){
+	double absxi = fabs(Z[(j + 1)*n+i]);
+	if (absxi > PREC){
+	  double dummy = scale/absxi;
 	  if (scale < absxi){
 	    ssq = 1.0 + ssq*dummy*dummy;
 	    scale = absxi;
-	  }//End if (scale < absxi)
+	  }
 	  else
 	    ssq += 1.0/dummy/dummy;
-	}//End if (Z[i][j + 1] != 0)
-      } //End for i
-    }//End if (wi[j] != 0)
+	}
+      }
+    }
     
-    norm = scale*sqrt(ssq); //This is the norm of the (possibly
-			    //complex) vector
+    // this is norm of the (possibly complex) vector
+    double norm = scale*sqrt(ssq); 
     
-    for (int i = 0; i < N; i++)
-      Z[i][j] /= norm;
+    for (int i = 0; i < n; i++)
+      Z[j*n+i] /= norm;
     
-    if (wi[j] != 0){// If complex eigenvalue, also scale imaginary
-		    // part of eigenvector
+    if (fabs(wi[j]) > PREC){
+      // If complex eigenvalue, also scale imaginary part of
+      // eigenvector
       j++;
-      for (int i = 0; i < N; i++)
-	Z[i][j] /= norm;
-    }//End if (wi[j] != 0)
-    
-  }// End for j
-  
-  return;
-} // End norVecC
+      for (int i = 0; i < n; i++)
+	Z[j*n+i] /= norm;
+    }
+  }
+}
 
-static void swap_elements(int N, double *v, int i, int k)
+int EigSolve(int nm,int n,double *A,double *wr,double *wi,
+	     double *v,int *work1,double *work2) 
 {
-  double tmp = v[k];
-  v[k] = v[i];
-  v[i] = tmp;
+  int is1,is2,ierr;
+
+  balanc(nm,n,A,is1,is2,work2);
+  elmhes(nm,n,is1,is2,A,work1);
+  eltran(nm,n,is1,is2,A,work1,v);
+  ierr = hqr(nm,nm,is1,is2,A,wr,wi,v);
+  if (ierr) return 0;
+  balbak(nm,n,is1,is2,work2,n,v);
+  normvec(n,v,wi);
+  return 1;
 }
 
-static void swap_columns(int N, double **v, int i, int k)
+// Solve eigen problem for a real, symmetric matrix using the Jacobi
+// algorithm (based on a routine from Laurent Stainier)
+
+int EigSolveSym(int n,int nm,double *A,double *d,double *V,
+		double *b,double *z)
 {
-  for(int n = 0; n < N; n++){
-    double tmp = v[n][k];
-    v[n][k] = v[n][i];
-    v[n][i] = tmp;
+  static const int NSWMAX = 50;
+
+  int i,j,k,ii,ij,ik,ki=0,kj=0,kki,kkj,ival,jval;
+  int nrot,nsweep;
+  double c,g,h,s,t,tau,theta,tresh,sum;
+
+  // initialize eigenvectors
+  for (j=0,jval=0; j < n; j++,jval+=nm) {
+    for (i=0; i < n; i++) V[jval+i] = 0.e0;
+    V[jval+j] = 1.e0;
   }
+
+  // initialize b and d to the diagonal of A
+  for (i=0, ii=0; i < n; i++, ii+=(i+1)) {
+    b[i] = d[i] = A[ii];
+    z[i] = 0.e0;
+  }
+
+  // begin sweeping process
+  nrot = 0;
+  for (nsweep=0; nsweep < NSWMAX; nsweep++) {
+
+    // Sum off-diagonal elements
+    sum = 0.e0;
+    for (i=0, ij=0; i < n; i++, ij++)
+      for (j=0; j < i; j++, ij++)
+        sum += fabs(A[ij]);
+
+    if (sum < PREC) break;
+
+    // compute a treshold on the first 3 sweeps
+    if (nsweep < 4)
+      tresh = 0.2*sum/(n*n);
+    else
+      tresh = 0.e0;
+
+    // browse the matrix
+    for (i=0, ij=0, ival=0; i < n; i++, ij++, ival+=i)
+      for (j=0, jval=0; j < i; j++, ij++, jval+=j) {
+
+        // test off-diagonal element
+        g = 100.*fabs(A[ij]);
+        if ((nsweep > 4) 
+            && (fabs(d[i]+g) == fabs(d[i])) 
+            && (fabs(d[j]+g) == fabs(d[j]))) {
+
+          A[ij] = 0.e0;
+        }
+        else if (fabs(A[ij]) > tresh) {
+
+          // compute the rotation
+          h = d[j]-d[i];
+          if ((fabs(h)+g) == fabs(h))
+            t = A[ij]/h;
+          else {
+            theta = 0.5*h/A[ij];
+            t = 1.e0/(fabs(theta)+sqrt(1.0+theta*theta));
+            if (theta < 0.e0) t = -t;
+          }
+          c = 1.e0/sqrt(1.+t*t);
+          s = t*c;
+          tau = s/(1.e0+c);
+
+          // rotate rows i and j
+          h = t*A[ij];
+          z[i] -= h;
+          z[j] += h;
+          d[i] -= h;
+          d[j] += h;
+          A[ij]  = 0.e0;
+
+          for (k=0,kki=i*nm,kkj=j*nm; k < n; k++, kki++, kkj++) {
+
+            g = V[kki];
+            h = V[kkj];
+            V[kki] = g-s*(h+g*tau);
+            V[kkj] = h+s*(g-h*tau);
+
+            ki = (k<=i)?(ival+k):(ki+k);
+            kj = (k<=j)?(jval+k):(kj+k);
+            if (k == i || k == j) continue;
+
+            g = A[ki];
+            h = A[kj];
+            A[ki] = g-s*(h+g*tau);
+            A[kj] = h+s*(g-h*tau);
+          }
+
+          nrot++;
+        }
+      }
+
+    // update d and reinitialize z
+    for (i=0; i < n; i++) {
+      b[i] += z[i];
+      d[i] = b[i];
+      z[i] = 0.e0;
+    }
+  }
+
+  if(nsweep >= NSWMAX)
+    return 0;
+  else
+    return nrot+1;
 }
 
-void EigSort(int N, double *wr, double *wi, double **B)
-{
-  // Sorts the eigenvalues/vectors in ascending order according to
-  // their real part. Warning: this might screw up the ordering of
-  // complex eigenvectors --- VERIFY THIS
+// Sort the eigenvalues/vectors in ascending order according to their
+// real part. Warning: this will screw up the ordering of complex
+// eigenvectors.
 
-  for (int i = 0; i < N - 1; i++){
+void EigSort(int n, double *wr, double *wi, double *B)
+{
+  for (int i = 0; i < n - 1; i++){
     int k = i;
     double ek = wr[i];
     // search for something to swap
-    for (int j = i + 1; j < N; j++){
+    for (int j = i + 1; j < n; j++){
       const double ej = wr[j];
       if(ej < ek){
 	k = j;
@@ -584,310 +828,60 @@ void EigSort(int N, double *wr, double *wi, double **B)
       }
     }
     if (k != i){
-      swap_elements(N, wr, i, k);
-      swap_elements(N, wi, i, k);
-      swap_columns(N, B, i, k);
+      swap(&wr[i], 1, &wr[k], 1, 1);
+      swap(&wi[i], 1, &wi[k], 1, 1);
+      swap(&B[n*i], 1, &B[n*k], 1, n);
     }
   }
 }
 
-int EigSolve(int N, double **A, double *wr, double *wi, double **B,
-	     int *itmp, double *dtmp)
-{
-  // Computes the N eigenvalues of the square, real matrix A. All
-  // vectors have dimension N and all matrices have dimension
-  // NxN. Warning: the matrix A gets modified during the
-  // computation.
-
-  // Balance the matrix to improve accuracy of
-  // eigenvalues. (Introduces no rounding errors, since it scales A by
-  // powers of the radix.)
-
-  double *scale = dtmp; //Contains information about transformations.
-  int *trace = itmp; //Records row and column interchanges
-
-  double radix = 2; //Base of machine floating point representation.
-  double c, f, g, r, s, b2 = radix*radix;
-  int ierr = -1, igh, low, k = 0, l = N - 1, noconv;
-  
-  //Search through rows, isolating eigenvalues and pushing them down.
-  
-  noconv = l;
-  
-  while (noconv >= 0){
-    r = 0;
-    
-    for (int j = 0; j <= l; j++) {
-      if (j == noconv) continue;
-      if (A[noconv][j] != 0.0){
-	r = 1;
-	break;
-      }
-    } //End for j
-    
-    if (r == 0){
-      scale[l] = noconv;
-      
-      if (noconv != l){
-	for (int i = 0; i <= l; i++){
-	  f = A[i][noconv];
-	  A[i][noconv] = A[i][l];
-	  A[i][l] = f;
-	}//End for i
-	for (int i = 0; i < N; i++){
-	  f = A[noconv][i];
-	  A[noconv][i] = A[l][i];
-	  A[l][i] = f;
-	}//End for i
-      }//End if (noconv != l)
-      
-      if (l == 0)
-	break;  //break out of while loop
-      
-      noconv = --l;
-      
-    }//End if (r == 0)
-    
-    else //else (r != 0)
-      noconv--;
-    
-  }//End while noconv
-  
-  if (l > 0) {  //Search through columns, isolating eigenvalues and
-		//pushing them left.
-    
-    noconv = 0;
-    
-    while (noconv <= l){  
-      c = 0;
-      
-      for (int i = k; i <= l; i++){
-	if (i == noconv) continue;
-	if (A[i][noconv] != 0.0){
-	  c = 1;
-	  break;
-	}
-      }//End for i
-      
-      if (c == 0){
-	scale[k] = noconv;
-	
-	if (noconv != k){
-	  for (int i = 0; i <= l; i++){
-	    f = A[i][noconv];
-	    A[i][noconv] = A[i][k];
-	    A[i][k] = f;
-	  }//End for i
-	  for (int i = k; i < N; i++){
-	    f = A[noconv][i];
-	    A[noconv][i] = A[k][i];
-	    A[k][i] = f;
-	  }//End for i
-	  
-	}//End if (noconv != k)
-	
-	noconv = ++k;
-      }//End if (c == 0)
-      
-      else  //else (c != 0)
-	noconv++;
-      
-    }//End while noconv
-    
-    //Balance the submatrix in rows k through l.
-    
-    for (int i = k; i <= l; i++)
-      scale[i] = 1.0;
-    
-    //Iterative loop for norm reduction
-    do {
-      noconv = 0;
-      for (int i = k; i <= l; i++) {
-	c = r = 0.0;
-	for (int j = k; j <= l; j++){
-	  if (j == i) continue;
-	  c += fabs(A[j][i]);
-	  r += fabs(A[i][j]);
-	} // End for j
-	//guard against zero c or r due to underflow:
-	if ((c == 0.0) || (r == 0.0)) continue; 
-	g = r/radix;
-	f = 1.0;
-	s = c + r;
-	while (c < g) {
-	  f *= radix;
-	  c *= b2;
-	} // End while (c < g)
-	g = r*radix;
-	while (c >= g) {
-	  f /= radix;
-	  c /= b2;
-	} // End while (c >= g)
-	
-	//Now balance
-	if ((c + r)/f < 0.95*s) {
-	  g = 1.0/f;
-	  scale[i] *= f;
-	  noconv = 1;
-	  for (int j = k; j < N; j++)
-	    A[i][j] *= g;
-	  for (int j = 0; j <= l; j++)
-	    A[j][i] *= f;
-	} //End if ((c + r)/f < 0.95*s)
-      } // End for i
-    } while (noconv);  // End of do-while loop.
-    
-  } //End if (l > 0)
-  
-  low = k;
-  igh = l;
-  
-  //End of balanc
-  
-  // Now reduce the real general Matrix to upper-Hessenberg form using
-  // stabilized elementary similarity transformations.
-
-  for (int i = (low + 1); i < igh; i++){
-    k = i;
-    c = 0.0;
-    
-    for (int j = i; j <= igh; j++){
-      if (fabs(A[j][i - 1]) > fabs(c)){
-	c = A[j][i - 1];
-	k = j;
-      }//End if
-    }//End for j
-    
-    trace[i] = k;
-    
-    if (k != i){
-      for (int j = (i - 1); j < N; j++){
-	r = A[k][j];
-	A[k][j] = A[i][j];
-	A[i][j] = r;
-      }//End for j
-      
-      for (int j = 0; j <= igh; j++){
-	r = A[j][k];
-	A[j][k] = A[j][i];
-	A[j][i] = r;
-      }//End for j
-    }//End if (k != i)
-    
-    if (c != 0.0){
-      for (int m = (i + 1); m <= igh; m++){
-	r = A[m][i - 1];
-	
-	if (r != 0.0){
-	  r = A[m][i - 1] = r/c;
-	  for (int j = i; j < N; j++)
-	    A[m][j] += -(r*A[i][j]);
-	  for (int j = 0; j <= igh; j++)
-	    A[j][i] += r*A[j][m];
-	}//End if (r != 0.0)
-      }//End for m
-    }//End if (c != 0)
-  }  //End for i.
-  
-  // Accumulate the stabilized elementary similarity transformations
-  // used in the reduction of A to upper-Hessenberg form. Introduces
-  // no rounding errors since it only transfers the multipliers used
-  // in the reduction process into the eigenvector matrix.
-  
-  for (int i = 0; i < N; i++){ //Initialize B to the identity Matrix.
-    for (int j = 0; j < N; j++)
-      B[i][j] = 0.0;
-    B[i][i] = 1.0;
-  } //End for i
-  
-  for (int i = (igh - 1); i >= (low + 1); i--){
-    k = trace[i];
-    for (int j = (i + 1); j <= igh; j++)
-      B[j][i] = A[j][i - 1];
-    
-    if (i == k)
-      continue;
-    
-    for (int j = i; j <= igh; j++){
-      B[i][j] = B[k][j];
-      B[k][j] = 0.0;
-    } //End for j
-    
-    B[k][i] = 1.0; 
-    
-  } // End for i
-  
-  hqr2(N, A, B, low, igh, wi, wr, ierr);
-  
-  if (ierr == -1){
-    
-    if (low != igh){
-      for (int i = low; i <= igh; i++){
-	s = scale[i];
-	for (int j = 0; j < N; j++)
-	  B[i][j] *= s;
-      }//End for i
-    }//End if (low != igh)
-    
-    for (int i = (low - 1); i >= 0; i--){
-      k = (int)scale[i];//FIXME: I added the cast
-      if (k != i){
-	for (int j = 0; j < N; j++){
-	  s = B[i][j];
-	  B[i][j] = B[k][j];
-	  B[k][j] = s;
-	}//End for j
-      }//End if k != i
-    }//End for i
-    
-    for (int i = (igh + 1); i < N; i++){
-      k = (int)scale[i];//FIXME: Iadded the casr
-      if (k != i){
-	for (int j = 0; j < N; j++){
-	  s = B[i][j];
-	  B[i][j] = B[k][j];
-	  B[k][j] = s;
-	}//End for j
-      }//End if k != i
-    }//End for i
-    
-    norVecC(N, B, wi);  //Normalize the eigenvectors
-    
-  }//End if ierr = -1
+// Small driver for 3x3 matrices, that does not modify the input
+// matrix
 
+int EigSolve3x3(const double A[9], double wr[3], double wi[3], double v[9]) 
+{
+  int ierr;
+  if(fabs(A[1]-A[3]) < PREC &&
+     fabs(A[2]-A[6]) < PREC &&
+     fabs(A[5]-A[7]) < PREC){
+    double work1[3], work2[3], S[6] = { A[0], 
+					A[1], A[4], 
+					A[2], A[5], A[8]};
+    ierr = EigSolveSym(3, 3, S, wr, v, work1, work2);
+    wi[0] = wi[1] = wi[2] = 0.0;
+  }
+  else{
+    int work1[3];
+    double work2[3], M[9] = { A[0], A[1], A[2], 
+			      A[3], A[4], A[5],
+			      A[6], A[7], A[8]};
+    ierr = EigSolve(3, 3, M, wr, wi, v, work1, work2);
+  }
+  EigSort(3, wr, wi, v);
   return ierr;
-}  //End of Eig3Solve
+}
 
 # if 0
-int main()
+int main ()
 {
-  const int N = 3;
-  double *wr = new double[N];
-  double *wi = new double[N];
-  double **A = new double*[N];
-  double **B = new double*[N];
-  for(int i = 0; i < N; i++){
-    A[i] = new double[N];
-    B[i] = new double[N];
-  }
-  double *dtmp = new double[N];
-  int *itmp = new int[N];
-
-  for(int i = 0; i < 1000000; i++){ // perf test
-    A[0][0] = 1.; A[0][1] = 2.; A[0][2] = 3.;
-    A[1][0] = 2.; A[1][1] = 4.; A[1][2] = 5.;
-    A[2][0] = 3.; A[2][1] = 5.; A[2][2] = 6.;
-    EigSolve(3, A, wr, wi, B, itmp, dtmp);
-  }
-  /*
-  printf("%g %g %g\n", A[0][0], A[0][1], A[0][2]);
-  printf("%g %g %g\n", A[1][0], A[1][1], A[1][2]);
-  printf("%g %g %g\n", A[2][0], A[2][1], A[2][2]);
-  */
-  printf("real= %g %g %g \n", wr[0], wr[1], wr[2]);
-  printf("imag= %g %g %g \n", wi[0], wi[1], wi[2]);
-  printf("eigvec1 = %g %g %g \n", B[0][0], B[1][0], B[2][0]);
-  printf("eigvec2 = %g %g %g \n", B[0][1], B[1][1], B[2][1]);
-  printf("eigvec3 = %g %g %g \n", B[0][2], B[1][2], B[2][2]);
+  //double A[9] = {-0.00299043,-8.67362e-19,0, 
+  //	 -8.67362e-19,-0.00299043,-1.73472e-18, 
+  //	 0,-1.73472e-18,0.01};
+  double A[9] = {1, 2, 3,   2, 4, 5,   3, 5, 6};
+  //double A[9] = {1, 2, 3,   1, 4, 5,   3, 5, 6};
+  double wr[3], wi[3], B[9];
+
+  for(int i = 0; i < 1; i++) // perf test
+    if(!EigSolve3x3(A, wr, wi, B))
+      printf("EigSolve did not converge!\n");
+
+  if(wi[0] != 0.0 || wi[1] != 0.0 || wi[2] != 0.0)
+    printf("imaginary eigenvalues\n");
+
+  printf ("real= %g %g %g \n", wr[0], wr[1], wr[2]);
+  printf ("imag= %.16g %.16g %.16g \n", wi[0], wi[1], wi[2]);
+  printf ("eigvec1 = %g %g %g \n", B[0], B[1], B[2]);
+  printf ("eigvec2 = %g %g %g \n", B[3], B[4], B[5]);
+  printf ("eigvec3 = %g %g %g \n", B[6], B[7], B[8]);
 }
 #endif
diff --git a/Numeric/Makefile b/Numeric/Makefile
index 66720267cd..aad54994c8 100644
--- a/Numeric/Makefile
+++ b/Numeric/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.16 2004-12-08 05:38:56 geuzaine Exp $
+# $Id: Makefile,v 1.17 2004-12-14 20:05:43 geuzaine Exp $
 #
 # Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
 #
@@ -56,9 +56,7 @@ depend:
 Numeric.o: Numeric.cpp ../Common/Gmsh.h ../Common/Message.h \
   ../DataStr/Malloc.h ../DataStr/List.h ../DataStr/Tree.h \
   ../DataStr/avl.h ../DataStr/Tools.h Numeric.h
-EigSolve.o: EigSolve.cpp ../Common/Gmsh.h ../Common/Message.h \
-  ../DataStr/Malloc.h ../DataStr/List.h ../DataStr/Tree.h \
-  ../DataStr/avl.h ../DataStr/Tools.h Numeric.h
+EigSolve.o: EigSolve.cpp
 gsl_newt.o: gsl_newt.cpp ../Common/Gmsh.h ../Common/Message.h \
   ../DataStr/Malloc.h ../DataStr/List.h ../DataStr/Tree.h \
   ../DataStr/avl.h ../DataStr/Tools.h Numeric.h
diff --git a/Numeric/Numeric.h b/Numeric/Numeric.h
index 4e59c3bb82..d065d004ba 100644
--- a/Numeric/Numeric.h
+++ b/Numeric/Numeric.h
@@ -73,9 +73,6 @@ double angle_02pi(double A3);
 void eigenvalue(double mat[3][3], double re[3]);
 void FindCubicRoots(const double coeff[4], double re[3], double im[3]);
 void eigsort(double d[3]);
-int EigSolve(int N, double **A, double *wr, double *wi, double **B, 
-	     int *itmp, double *dtmp);
-void EigSort(int N, double *wr, double *wi, double **B);
 
 double InterpolateIso(double *X, double *Y, double *Z, 
 		      double *Val, double V, int I1, int I2, 
diff --git a/Plugin/Eigenvectors.cpp b/Plugin/Eigenvectors.cpp
index 69bbd5a04c..9c50ae69b7 100644
--- a/Plugin/Eigenvectors.cpp
+++ b/Plugin/Eigenvectors.cpp
@@ -1,4 +1,4 @@
-// $Id: Eigenvectors.cpp,v 1.2 2004-12-10 03:42:29 geuzaine Exp $
+// $Id: Eigenvectors.cpp,v 1.3 2004-12-14 20:05:43 geuzaine Exp $
 //
 // Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
 //
@@ -26,6 +26,7 @@
 #include "Context.h"
 #include "Malloc.h"
 #include "Numeric.h"
+#include "EigSolve.h"
 
 extern Context_T CTX;
 
@@ -89,7 +90,7 @@ void GMSH_EigenvectorsPlugin::catchErrorMessage(char *errorMessage) const
 static int nonzero(double v[3])
 {
   for(int i = 0; i < 3; i++)
-    if(fabs(v[i]) > 1.e-15) return 1;
+    if(fabs(v[i]) > 1.e-16) return 1;
   return 0;
 }
 
@@ -101,14 +102,7 @@ static void eigenvectors(List_T *inList, int inNb,
 {
   if(!inNb) return;
 
-  int itmp[3], nbcomplex = 0;
-  double wr[3], wi[3], dtmp[3];
-  double **A = new double*[3];
-  double **B = new double*[3];
-  for(int i = 0; i < 3; i++){
-    A[i] = new double[3];
-    B[i] = new double[3];
-  }
+  int nbcomplex = 0;
 
   int nb = List_Nbr(inList) / inNb;
   for(int i = 0; i < List_Nbr(inList); i += nb) {
@@ -121,11 +115,9 @@ static void eigenvectors(List_T *inList, int inNb,
       for(int k = 0; k < nbNod; k++){
 	double *val = (double *)List_Pointer_Fast(inList, i + 3 * nbNod + 
 						  nbNod * 9 * j + 9 * k);
-	A[0][0] = val[0]; A[0][1] = val[1]; A[0][2] = val[2];
-	A[1][0] = val[3]; A[1][1] = val[4]; A[1][2] = val[5];
-	A[2][0] = val[6]; A[2][1] = val[7]; A[2][2] = val[8];
-	EigSolve(3, A, wr, wi, B, itmp, dtmp);
-	EigSort(3, wr, wi, B);
+	double wr[3], wi[3], B[9];
+	if(!EigSolve3x3(val, wr, wi, B))
+	  Msg(GERROR, "Eigensolver failed to converge");
 	nbcomplex += nonzero(wi); 
 	if(!scale)
 	  wr[0] = wr[1] = wr[2] = 1.;
@@ -133,9 +125,9 @@ static void eigenvectors(List_T *inList, int inNb,
 	  double res;
 	  // wrong if there are complex eigenvals (B contains both
 	  // real and imag parts: cf. explanation in EigSolve.cpp)
-	  res = wr[0] * B[l][0]; List_Add(minList, &res);
-	  res = wr[1] * B[l][1]; List_Add(midList, &res);
-	  res = wr[2] * B[l][2]; List_Add(maxList, &res);
+	  res = wr[0] * B[l]; List_Add(minList, &res);
+	  res = wr[1] * B[3+l]; List_Add(midList, &res);
+	  res = wr[2] * B[6+l]; List_Add(maxList, &res);
 	}
       }
     }
@@ -144,13 +136,6 @@ static void eigenvectors(List_T *inList, int inNb,
     (*maxNb)++;
   }
 
-  for(int i = 0; i < 3; i++){
-    delete [] A[i];
-    delete [] B[i];
-  }
-  delete [] A;
-  delete [] B;
-
   if(nbcomplex)
     Msg(GERROR, "%d tensors have complex eigenvalues/eigenvectors", nbcomplex);
 }
diff --git a/Plugin/Makefile b/Plugin/Makefile
index 06ff391c00..b8c9c2afc1 100644
--- a/Plugin/Makefile
+++ b/Plugin/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.64 2004-12-10 03:35:29 geuzaine Exp $
+# $Id: Makefile,v 1.65 2004-12-14 20:05:44 geuzaine Exp $
 #
 # Copyright (C) 1997-2004 C. Geuzaine, J.-F. Remacle
 #
@@ -133,7 +133,8 @@ Eigenvectors.o: Eigenvectors.cpp Plugin.h ../Common/Options.h \
   ../Common/Message.h ../Common/Views.h ../Common/ColorTable.h \
   ../DataStr/List.h ../Common/VertexArray.h ../Common/SmoothNormals.h \
   ../Common/GmshMatrix.h ../Common/AdaptiveViews.h Eigenvectors.h \
-  ../Common/Context.h ../DataStr/Malloc.h ../Numeric/Numeric.h
+  ../Common/Context.h ../DataStr/Malloc.h ../Numeric/Numeric.h \
+  ../Numeric/EigSolve.h
 Octree.o: Octree.cpp Octree.h OctreeInternals.h
 OctreeInternals.o: OctreeInternals.cpp ../Common/Message.h \
   OctreeInternals.h
diff --git a/doc/CREDITS b/doc/CREDITS
index 0cc576a75e..b46045990c 100644
--- a/doc/CREDITS
+++ b/doc/CREDITS
@@ -1,4 +1,4 @@
-$Id: CREDITS,v 1.21 2004-12-06 07:13:47 geuzaine Exp $
+$Id: CREDITS,v 1.22 2004-12-14 20:05:44 geuzaine Exp $
 
              Gmsh is copyright (C) 1997-2004 
 
@@ -17,10 +17,10 @@ to elementary geometry" interface, Netgen integration).
 Other code contributors include: David Colignon <David.Colignon at
 univ.u-3mrs.fr> for new colormaps; Patrick Dular <patrick.dular at
 ulg.ac.be> for transfinite mesh bug fixes; Laurent Stainier
-<l.stainier at ulg.ac.be> for help with the MacOS port and the tensor
-display code; Pierre Badel <badel at freesurf.fr> for help with the
-GSL integration; and Marc Ume <Marc.Ume at digitalgraphics.be> for the
-original list code.
+<l.stainier at ulg.ac.be> for the eigenvalue solvers and for help with
+the MacOS port and the tensor display code; Pierre Badel <badel at
+freesurf.fr> for help with the GSL integration; and Marc Ume <Marc.Ume
+at digitalgraphics.be> for the original list code.
 
 The AVL tree code (DataStr/avl.*) and the YUV image code
 (Graphics/gl2yuv.*) are copyright (C) 1988-1993, 1995 The Regents of
-- 
GitLab