#include "gpk_std.h"
#include <stdio.h>
#include <assert.h>
#include <math.h>
#include "f2c.h"
#include "gpklib.h"

extern "C"
int dsyev_(char *jobz, char *uplo, integer *n, doublereal *a,
	 integer *lda, doublereal *w, doublereal *work, integer *lwork, 
	integer *info);

/* Returns eigenvalues, leaves eigenvectors in a. */
/* On return, a*v*a_transpose == the matrix you passed in. */
/* More specifically, input[i][j] = a[i][k]*v[k]*a[j][k]. */
/* That means that the kth eigenvalue of the input is in column k, a[*][k]. */
double *
calc_eigenvectors(double **a, int n)
{
	assert(n >= 1);
	double *aa = double1alloc(n*n);
	assert(aa);
	long lda = n, lwork=30*n, info;
	double *ev = double1alloc(n);
	double *wks = double1alloc(lwork);
	assert(ev && wks);

	int i;
	for(i=0; i<n; i++) {
		int j;
		for(j=0; j<n; j++)
			aa[i+j*lda] = a[i][j];
	}

	long nl = n;
	dsyev_("V", "U", &nl, aa,
	 	&lda, ev, wks, &lwork, 
		&info);

	assert(info == 0);
	double1free(wks);

	for(i=0; i<n; i++) {
		int j;
		for(j=0; j<n; j++)
			a[i][j] = aa[i+j*lda];
	}

	double1free(aa);


	return ev;
}


#if 0

main()
{
	{
	C int n = 2;
	double **a = double2alloc(n, n);
	a[0][1] = a[1][0] = 0;
	a[1][1] = a[0][0] = 1;
	double *val = calc_eigenvectors(a, n);
	int i;
	for(i=0; i<n; i++) {
		printf("eigenvalue %d = %g\n", i, val[i]);
		int j;
		for(j=0; j<n; j++)
			printf("%g ", a[i][j]);
		printf("\n");
	}
	double2free(a);
	}
	{
	C int n = 2;
	double **a = double2alloc(n, n);
	a[0][1] = a[1][0] = 0;
	a[1][1] = 0.01;
	a[0][0] = 1;
	double *val = calc_eigenvectors(a, n);
	int i;
	for(i=0; i<n; i++) {
		printf("eigenvalue %d = %g\n", i, val[i]);
		int j;
		for(j=0; j<n; j++)
			printf("%g ", a[i][j]);
		printf("\n");
	}
	double2free(a);
	}
	{
	C int n = 10;
	double **a = double2alloc(n, n);
	double **a0 = double2alloc(n, n);
	int i;
	for(i=0; i<n; i++) {
		int j;
		for(j=0; j<i; j++) {
			a[i][j] = cos(i+cos(j))*(sin(i+1.445*j)+1+sin(0.1*i));
			a[j][i] = a[i][j];
			a0[i][j] = a[i][j];
			a0[j][i] = a[i][j];
		}
	}
	double *val = calc_eigenvectors(a, n);
	double **tmp = double2alloc(n, n);
	double **t = double2alloc(n, n);
	for(i=0; i<n; i++) {
		int j;
		for(j=0; j<n; j++) {
			tmp[i][j] = a[i][j]*val[j];
			t[i][j] = 0;
		}
	}
	for(i=0; i<n; i++) {
		int j;
		for(j=0; j<n; j++) {
			int k;
			for(k=0; k<n; k++)
				t[i][k] += tmp[i][j]*a[k][j];
		}
	}
	double err = 0;
	for(i=0; i<n; i++) {
		int j;
		for(j=0; j<n; j++) {
			err += fabs(t[i][j] - a0[i][j]);
		}
	}
	printf("err=%g\n", err);
	double2free(t);
	double2free(tmp);
	double2free(a);
	double2free(a0);
	}
}

#endif
