/*-=-=-=-=-=-=-=-=-=-=-=- RCCL/RCI Notice of Copyright -=-=-=-=-=-=-=-=-=-=-*
 *									    *
 *	Copyright (C) 1987, 1991 by John E. Lloyd.			    *
 *									    *
 *	Information on copyright permissions, as well as a complete	    *
 *	disclaimer of warranty, is given in the file COPYRIGHT.		    *
 *									    *
 *-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/

#include "../h/cdefs.h"
#include "matrix.h"

int matrixError;

/* 'mr' - pointer to preallocated result matrix (size must be OK)
   'm1' - pointer to source matrix 
*/

#define EPSILON 1.0e-14

matrix *invertMatrix (mr, m1)
matrix *mr, *m1;
{
	int n, i, j, k, imax;
	double *ar;
	double *ai;
	double det;
	double max;
	double val;

	n = m1->row;
	ai = (double *) malloc (n * n * sizeof(double));
	ar = (double *) malloc (n * n * sizeof(double));

	det = 1.0;
	for (i=0; i<n; i++)
	 { for (j=0; j<n; j++)
	    { if (i == j)
	       { ar [i*n+j] = 1.0;
	       }
	      else
	       { ar [i*n+j] = 0.0;
	       }
	      ai[i*n+j] = ELEMENT(m1, i, j);
	    }
	 }

	for (k=0; k<n; k++)
	 {
	   /* find max element in column k */

	   imax = k;
	   max = ai[imax*n+k];
	   for (i=k; i<n; i++)
	    { if (FABS(ai[i*n+k]) > max)
	       { imax = i;
		 max = FABS(ai[imax*n+k]);
	       }
	    }

	   val = ai[imax*n+k];

	   if (imax != k)
	    { /* interchange rows in both matrices */

	      double temp;

	      for (j=0; j<n; j++)
	       { temp = ai[imax*n+j];
		 ai[imax*n+j] = ai[k*n+j];
		 ai[k*n+j] = temp;
	       }
	      for (j=0; j<n; j++)
	       { temp = ar[imax*n+j];
		 ar[imax*n+j] = ar[k*n+j];
		 ar[k*n+j] = temp;
	       }
	      det = -det;
	    }
	   
	   if (max < EPSILON)
	    { matrixError = EMatSingular;
	      mr = NULL;
	      break;
	    }

	   /* reduce row k */
	  
	   for (j=0; j<n; j++)
	    { ai[k*n+j] /= val;
	      ar[k*n+j] /= val;
	    }
	   det *= val;
	   
	   /* reduce rows other than k */

	   for (i=0; i<n; i++)
	    { if (i != k)
	       { double mult = ai[i*n+k];
		 for (j=0; j<n; j++)
		  { ai[i*n+j] -= mult*ai[k*n+j];
		    ar[i*n+j] -= mult*ar[k*n+j];
		  }
	       }
	    }
	 }

	if (mr != NULL)
	 { for (i=0; i<n; i++)
	    { for (j=0; j<n; j++)
	       { mr->buf[i*n+j] = ar[i*n+j];
	       }
	    }
	 }
	free (ai);
	free (ar);
	return (mr);
}

/* returns the transpose of a matrix. Assumes that space passed down in
   mr is correct.

   'mr' - pointer to preallocated result matrix (size must be OK)
   'm1' - pointer to source matrix
*/

matrix *transposeMatrix (mr, m1)
register matrix *mr, *m1;
{
	register int i, j;
	double d;

	mr->row = m1->col;
	mr->col = m1->row;
	for (i=0; i<mr->row; i++)
 	 { for (j=0; j<mr->col; j++)
	    { ELEMENT (mr, i, j) = ELEMENT (m1, j, i);
	    }
	 }
}

matrix *mallocMatrix (row, col)
int row, col;
{
	matrix *m;

	m = (matrix *)malloc (sizeof(matrix) + row*col*sizeof(double));
	if (m == NULL)
	 { matrixError = EMatMemory;
	   return(NULL);
	 }
	m->row = row;
	m->col = col;
	m->buf = (double *)((char *)m + sizeof(matrix));
	bzero ((char *)(m->buf), row*col*sizeof(double));
	return (m);
}
