#include "slalib.h"
#include "slamac.h"
void slaSmat ( int n, float *a, float *y, float *d, int *jf, int *iw )
/*
**  - - - - - - - -
**   s l a S m a t
**  - - - - - - - -
**
**  Matrix inversion & solution of simultaneous equations.
**
**  (single precision)
**
**  For the set of n simultaneous equations in n unknowns:
**     a.y = x
**
**  where:
**     a is a non-singular n x n matrix
**     y is the vector of n unknowns
**     x is the known vector
**
**  slaSmat computes:
**     the inverse of matrix a
**     the determinant of matrix a
**     the vector of n unknowns
**
**  Arguments:
**
**     symbol  type dimension           before              after
**
**       n      int                  no. of unknowns       unchanged
**       *a     float  [n][n]            matrix             inverse
**       *y     float   [n]              vector            solution
**       *d     float                      -              determinant
**    >  *jf    int                        -            singularity flag
**       *iw    int     [n]                -               workspace
**
**
**    > jf is the singularity flag.  If the matrix is non-singular,
**      jf=0 is returned.  If the matrix is singular, jf=-1 & d=0.0 are
**      returned.  In the latter case, the contents of array a on return
**      are undefined.
**
**  Algorithm:
**     Gaussian elimination with partial pivoting.
**
**  Speed:
**     Very fast.
**
**  Accuracy:
**     Fairly accurate - errors 1 to 4 times those of routines optimized
**     for accuracy.
**
**  Example call (note handling of "adjustable dimension" 2D array):
**
**     float a[MP][MP], v[MP], d;
**     int j, iw[MP];
**      :
**     slaSmat ( n, (float *) a, v, &d, &j, iw );
**
**  Last revision:   17 August 1999
**
**  Copyright P.T.Wallace.  All rights reserved.
*/

#define TINY 1e-20f

{
   int k, imx, i, j, ki;
   float amx, t, yk;

/* Pointers to beginnings of rows in matrix a[n][n] */

   float  *ak,      /* row k                        */
          *ai,      /* row i                        */
          *aimx;    /* row imx                      */

   *jf = 0;
   *d = 1.0f;

   for ( k = 0, ak = a; k < n; k++, ak += n ) {
      amx = (float) fabs ( (double) ak[k] );
      imx = k;
      aimx = ak;
      if ( k != n ) {
         for ( i = k + 1, ai = ak + n; i < n; i++, ai += n ) {
            t = (float) fabs ( (double) ai[k] );
            if ( t > amx ) {
               amx = t;
               imx = i;
               aimx = ai;
            }
         }
      }
      if ( amx < TINY ) {
         *jf = -1;
      } else {
         if ( imx != k ) {
            for ( j = 0; j < n; j++ ) {
               t = ak[j];
               ak[j] = aimx[j];
               aimx[j] = t;
            }
            t = y[k];
            y[k] = y[imx];
            y[imx] = t;
            *d = - *d;
         }
         iw[k] = imx;
         *d *= ak[k];
         if ( fabs ( *d ) < TINY ) {
            *jf = -1;
         } else {
            ak[k] = 1.0f / ak[k];
            for ( j = 0; j < n; j++ ) {
               if ( j != k ) {
                  ak[j] *= ak[k];
               }
            }
            yk = y[k] * ak[k];
            y[k] = yk;
            for ( i = 0, ai = a; i < n; i++ , ai += n ) {
               if ( i != k ) {
                  for ( j = 0; j < n; j++ ) {
                     if ( j != k ) {
                        ai[j] -= ai[k] * ak[j];
                     }
                  }
                  y[i] -= ai[k] * yk;
               }
            }
            for ( i = 0, ai = a; i < n; i++ , ai += n ) {
               if ( i != k )
                  ai[k] *= - ak[k];
            }
         }
      }
   }
   if ( *jf != 0 ) {
      *d = 0.0f;
   } else {
      for ( k = n;  k-- > 0; ) {
         ki = iw[k];
         if ( k != ki ) {
            for ( i = 0, ai = a; i < n; i++ , ai += n ) {
               t = ai[k];
               ai[k] = ai[ki];
               ai[ki] = t;
            }
         }
      }
   }
}
