#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "./LIB/heads.h"
#include "./LIB/protos.h"
/*-----------------------------------------------------------------------*/
void  dscale(int , double*, double*, double*); 
p4ptr Lvsol2(double *x, int nlev, p4ptr levmat, ilutptr ilusch) ;
int   Uvsol2(double *x, int nlev, int n, p4ptr levmat,
	     ilutptr ilusch);
void  SchLsol(ilutptr ilusch, double *y) ;
void  SchUsol(ilutptr ilusch, double *y) ;

/*-----------------------------------------------------------------------*/

p4ptr Lvsol2(double *x, int nlev, p4ptr levmat, ilutptr ilusch) 
{
  /* Macro L-solve -- corresponds to left (L) part of arms
  |  preconditioning operation -- 
  |  on entry : 
  |   x =  right- hand side to be operated on by the preconditioner
  |  on return : x is overwritten
  |   x =  output result of operation 
  |  
  |  Note : in-place operation -- b and x can occupy the same space..
  | --------------------------------------------------------------------*/ 
/*-------------------- local variables  */
  int nloc=levmat->n, first, lenB;
  p4ptr last=levmat; 
/*-------------------- take care of  special cases :  nlev==0 --> lusol  */
  if (nlev == 0) {
    SchLsol(ilusch,x);
    return (last);
  }
  first = 0;
/*-------------------- descend                                      */
  while (levmat) { 
    nloc=levmat->n;
    lenB =levmat->nB;
/*-------------------- left scaling                                  */
    if (levmat->D1 !=  NULL) 
      dscale(nloc,levmat->D1, &x[first],  &x[first]); 
/*--------------------  RESTRICTION/ DESCENT OPERATION  */
    if (lenB) 
      descend (levmat, &x[first], &x[first]);
    first += lenB; 
    last = levmat;
    levmat = levmat->next;
/*---------------------------------------------------------------------
| next level 
+--------------------------------------------------------------------*/
   } 
  SchLsol(ilusch,&x[first]);
  return last; 
}

int Uvsol2(double *x, int nlev, int n, p4ptr levmat,
	   ilutptr ilusch) 
{
  /* Macro U-solve -- corresponds to left (L) part of arms
  |  preconditioning operation -- 
  |  on entry : 
  |  b  =  right- hand side to be operated on by the preconditioner
  |  on return  = x has been overwritten =
  |  x  =  output result of operation 
  |  
  |  Note : in-place operation -- b and x  can occupy the same space..
  | --------------------------------------------------------------------*/ 

/*-------------------- local variables  */
  int nloc, lenB, first; 
  /*-------------------- work array                                        */
  /*-------------------- take care of  special cases :  nlev==0 --> lusol  */
/*-------------------- case of zero levels                             */
  if (nlev == 0) { 
    SchUsol(ilusch, x);  
    return(0);
  }
/*-------------------- general case                               */
  nloc=levmat->n; 
  lenB=levmat->nB; 
  first = n - nloc; 
/*-------------------- last level                                 */
  first += lenB; 
  SchUsol(ilusch, &x[first]); 
/*-------------------- other levels                               */
  while (levmat) {
    nloc = levmat->n; 
    first -= levmat->nB;
    if (levmat->n) 
      ascend(levmat, &x[first],&x[first]);
/*-------------------- right scaling */
    if (levmat->D2 !=  NULL) 
      dscale(nloc, levmat->D2, &x[first], &x[first]) ;
    levmat = levmat->prev; 
  }
  return 0;
/*--------------------  PROLONGATION/ ASCENT OPERATION */
}

int armsol2(double *x,  arms Prec) 
{ /* combined preconditioning operation -- combines the
  |  left and right actions. 
  | 
  |  on entry : 
  |   x =  right- hand side to be operated on by the preconditioner
  |  on return : x is overwritten - 
  |   x =  output result of operation 
  |  
  |  Note : in-place operation -- b and x can occupy the same space..
  | --------------------------------------------------------------------*/ 
/*-------------------- local variables  */
  p4ptr levmat = Prec->levmat;
  ilutptr ilusch = Prec->ilus;
  int nlev = Prec->nlev;
  int n = levmat->n; 
  p4ptr last;
  if (nlev == 0) {
    n = ilusch->n;
    SchLsol(ilusch, x); 
    SchUsol(ilusch, x); 
    return 0;
  }
  last = Lvsol2(x, nlev, levmat, ilusch) ;
  Uvsol2(x, nlev, n, last, ilusch) ; 
  return 0; 
}

void SchLsol(ilutptr ilusch, double *y) 
{
/*---------------------------------------------------------------------
|  Forward solve for Schur complement part = 
|----------------------------------------------------------------------
| on entry:
| ilusch  = the LU matrix as provided from the ILU functions.
| y       = the right-hand-side vector
|
| on return
| y       = solution of LU x = y. [overwritten] 
|---------------------------------------------------------------------*/
/*-------------------- local variables                        */
  int n = ilusch->n, j, *perm = ilusch->rperm;
  double *work = ilusch->wk; 
/*-------------------- begin: right scaling                          */
   if (ilusch->D1 != NULL) 
     dscale(n, ilusch->D1, y, y); 
/*-------------------- ONE SIDED ROW PERMS */
   if (perm != NULL) { 
     for (j=0; j<n; j++)
       work[perm[j]] = y[j]; 
/*--------------------  L solve proper */
     Lsol(ilusch->L, work, y); 
   } else 
     Lsol(ilusch->L, y, y); 
/*---------------end of SchLsol---------------------------------------
----------------------------------------------------------------------*/
}

void SchUsol(ilutptr ilusch, double *y) 
{
/*---------------------------------------------------------------------
| U-solve for Schur complement  - 
|----------------------------------------------------------------------
| on entry:
| ilusch  = the LU matrix as provided from the ILU functions.
| y       = the right-hand-side vector
|
| on return 
| y       = solution of U x = y. [overwritten on y] 
|----------------------------------------------------------------------*/
  int n = ilusch->n, j,  *perm = ilusch->perm, *cperm;
  double *work = ilusch->wk; 
/* -------------------- begin by U-solving */
/*-------------------- CASE: column pivoting  used (as in ILUTP) */
  if (ilusch->perm2 != NULL) {
    Usol(ilusch->U, y, y);
    cperm = ilusch->perm2; 
    for (j=0; j<n; j++)
      work[cperm[j]] = y[j];
  }
  else
/*-------------------- CASE: no column pivoting  used                   */
    Usol(ilusch->U, y, work);
/*-------------------- generic permutation                              */
  if (perm != NULL) {
    for (j=0; j<n; j++)
      y[j] = work[perm[j]];
  } else
    memcpy(y, work,n*sizeof(double));
    
/*-------------------- case when diagonal scaling is done on columns    */ 
    if (ilusch->D2 !=NULL) 
      dscale(n, ilusch->D2, y, y);
}
/*---------------end of SchUsol---------------------------------------
----------------------------------------------------------------------*/













