/******************************************************************************
 *
 *       ELMER, A Computational Fluid Dynamics Program.
 *
 *       Copyright 1st April 1995 - , Center for Scientific Computing,
 *                                    Finland.
 *
 *       All rights reserved. No part of this program may be used,
 *       reproduced or transmitted in any form or by any means
 *       without the written permission of CSC.
 *
 ******************************************************************************/

/*******************************************************************************
 *
 *     LU decomposition.
 *
 *******************************************************************************
 *
 *                     Author:       Juha Ruokolainen
 *
 *                    Address: Center for Scientific Computing
 *                                Tietotie 6, P.O. BOX 405
 *                                  02101 Espoo, Finland
 *                                  Tel. +358 0 457 2723
 *                                Telefax: +358 0 457 2302
 *                              EMail: Juha.Ruokolainen@csc.fi
 *
 *                       Date: 30 May 1996
 *
 *                Modified by:
 *
 *       Date of modification:
 *
 ******************************************************************************/
/***********************************************************************
|
|  LU.C - Last Edited 8. 8. 1988
|
***********************************************************************/

/*======================================================================
|Syntax of the manual pages:
|
|FUNCTION NAME(...) params ...
|
$  usage of the function and type of the parameters
?  explane the effects of the function
=  return value and the type of value if not of type int
@  globals effected directly by this routine
!  current known bugs or limitations
&  functions called by this function
~  these functions may interest you as an alternative function or
|  because they control this function somehow
^=====================================================================*/


/*
 * $Id: lu.c,v 1.1.1.1 2005/04/14 13:29:14 vierinen Exp $ 
 *
 * $Log: lu.c,v $
 * Revision 1.1.1.1  2005/04/14 13:29:14  vierinen
 * initial matc automake package
 *
 * Revision 1.2  1998/08/01 12:34:45  jpr
 *
 * Added Id, started Log.
 * 
 *
 */

#include "elmer/matc.h"

#define A(i,j) a[n * (i) + (j)]

VARIABLE *mtr_LUD(var)
     VARIABLE *var;
{
  VARIABLE *res;
  int i, n, *pivot;
  double *a;
  
  if (NCOL(var) != NROW(var))
  {        
    error("LUD: Matrix must be square.\n");
  }
  
  res = var_temp_copy(var); 
  a = MATR(res); n = NROW(res);

  pivot = (int *)ALLOCMEM(n * sizeof(int));
  
  LUDecomp(a, n, pivot);
  
  FREEMEM((char *)pivot);
  
  return res;
}

VARIABLE *mtr_det(var)
     VARIABLE *var;
{
  VARIABLE *tmp, *res;
  int i, n, *pivot;
  double det, *a;
  
  if (NCOL(var) != NROW(var))
  {        
    error("Det: Matrix must be square.\n");
  }
  
  tmp = var_temp_copy(var); 

  a = MATR(tmp); n = NROW(tmp);
  
  pivot = (int *)ALLOCMEM(n * sizeof(int));
  
  LUDecomp(a, n, pivot);
  
  det = 1.0;
  for(i = 0; i < n; i++)
  {
    det *= A(i,i);
    if (pivot[i] != i) det = -det;
  }
  
  FREEMEM((char *)pivot); var_delete_temp(tmp);
  
  res = var_temp_new(TYPE_DOUBLE,1,1);

  M(res,0,0) = det;
  
  return res;
}

VARIABLE *mtr_inv(var)
     VARIABLE *var;
{
  VARIABLE *ptr;

  int i, j , k, n, *pivot;
  double s, *a;
  
  if (NCOL(var) != NROW(var))
  {        
    error("Inv: Matrix must be square.\n");
  }

  ptr = var_temp_copy(var); 

  a = MATR(ptr); n = NROW(ptr);
  
  pivot = (int *)ALLOCMEM(n * sizeof(int));  

  /*
   *  AP = LU
   */
  LUDecomp(a, n, pivot);
  for(i = 0; i < n; i++)
  {
    if (A(i,i) == 0) 
      error("Inv: Matrix is singular.\n");
    A(i,i) = 1.0 / A(i,i);
  }

  /*  
   *  INV(U)
   */
  for(i = n - 2; i >= 0; i--)
    for(j = n - 1; j > i; j--)
    {
      s = 0.0;
      for(k = i+1; k <= j; k++)
	if (k != j)  
	  s = s - A(i,k) * A(k,j);
	else 
	  s = s - A(i,k);
      A(i,j) = s;
    }

  /*
   * INV(L)
   */
  for(i = n - 2; i >= 0; i--)
    for(j = n - 1; j > i; j--)
    {
      s = 0.0;
      for(k = i + 1; k <= j; k++) 
	s = s - A(j,k) * A(k,i);
      A(j,i) = A(i,i) * s;
    }
  
  /* 
   * A  = INV(AP)
   */
  for(i = 0; i < n; i++)
    for(j = 0; j < n; j++)
    {
      s = 0.0;
      for(k = max(i,j); k < n; k++)
	if (k != i)
	  s += A(i,k) * A(k,j);
	else 
	  s += A(k,j);
      A(i,j) = s;
    }

  /*
   * A = INV(A) (at last)
   */
  for(i = 0; i < n; i++)
    if (pivot[i] != i)
      for(j = 0; j < n; j++)
      {
	s = A(i,j);
	A(i,j) = A(pivot[i],j);
	A(pivot[i],j) = s;
      }
  
  FREEMEM((char *)pivot);

  return ptr;
}

/*
 * LU- decomposition by gaussian elimination. Row pivoting is used.
 * 
 * result : AP = L'U ; L' = LD; pivot[i] is the swapped column 
 * number for column i (for pivoting).
 *
 * Result is stored in place of original matrix.
 *
 */
void LUDecomp(a, n, pivot)
   double *a;
   int n, pivot[];
{
  double swap;
  int i, j, k, l;
  
  for (i = 0; i < n - 1; i++)
  {
    j = i;
    for(k = i + 1; k < n; k++)
      if (abs(A(i,k)) > abs(A(j,k))) j = k;
    
    if (A(i,j) == 0.0) 
    {
      error("LUDecomp: Matrix is singular.\n");
    }
    
    pivot[i] = j;
    
    if (j != i)
    {
      swap = A(i,i);
      A(i,i) = A(i,j);
      A(i,j) = swap;
    }
    
    for(k = i + 1; k < n; k++)
      A(i,k) = A(i,k) / A(i,i);
    
    for(k = i + 1; k < n; k++) 
    {
      if (j != i)
      {
	swap = A(k,i);
	A(k,i) = A(k,j); 
	A(k,j) = swap;
      }
      for(l = i + 1; l < n; l++)
	A(k,l) = A(k,l) -  A(i,l) * A(k,i);
    }
  }
  
  pivot[n - 1] = n - 1;
  if ( A(n-1,n-1) == 0.0)
  {
      error("LUDecomp: Matrix is singular.\n");
  }
}
