/*
      NEP routines related to the solution process.

   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   SLEPc - Scalable Library for Eigenvalue Problem Computations
   Copyright (c) 2002-2013, Universitat Politecnica de Valencia, Spain

   This file is part of SLEPc.

   SLEPc is free software: you can redistribute it and/or modify it under  the
   terms of version 3 of the GNU Lesser General Public License as published by
   the Free Software Foundation.

   SLEPc  is  distributed in the hope that it will be useful, but WITHOUT  ANY
   WARRANTY;  without even the implied warranty of MERCHANTABILITY or  FITNESS
   FOR  A  PARTICULAR PURPOSE. See the GNU Lesser General Public  License  for
   more details.

   You  should have received a copy of the GNU Lesser General  Public  License
   along with SLEPc. If not, see <http://www.gnu.org/licenses/>.
   - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/

#include <slepc-private/nepimpl.h>       /*I "slepcnep.h" I*/
#include <petscdraw.h>

#undef __FUNCT__
#define __FUNCT__ "NEPSolve"
/*@
   NEPSolve - Solves the nonlinear eigensystem.

   Collective on NEP

   Input Parameter:
.  nep - eigensolver context obtained from NEPCreate()

   Options Database Keys:
+  -nep_view - print information about the solver used
-  -nep_plot_eigs - plot computed eigenvalues

   Level: beginner

.seealso: NEPCreate(), NEPSetUp(), NEPDestroy(), NEPSetTolerances()
@*/
PetscErrorCode NEPSolve(NEP nep)
{
  PetscErrorCode    ierr;
  PetscInt          i;
  PetscReal         re,im;
  PetscBool         flg;
  PetscViewer       viewer;
  PetscViewerFormat format;
  PetscDraw         draw;
  PetscDrawSP       drawsp;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(nep,NEP_CLASSID,1);
  ierr = PetscLogEventBegin(NEP_Solve,nep,0,0,0);CHKERRQ(ierr);

  /* call setup */
  ierr = NEPSetUp(nep);CHKERRQ(ierr);
  nep->nconv = 0;
  nep->its = 0;
  for (i=0;i<nep->ncv;i++) {
    nep->eig[i]   = 0.0;
    nep->errest[i] = 0.0;
  }
  nep->ktol = 0.1;
  ierr = NEPMonitor(nep,nep->its,nep->nconv,nep->eig,nep->errest,nep->ncv);CHKERRQ(ierr);

  ierr = DSSetEigenvalueComparison(nep->ds,nep->comparison,nep->comparisonctx);CHKERRQ(ierr);

  ierr = (*nep->ops->solve)(nep);CHKERRQ(ierr);

  if (!nep->reason) SETERRQ(PetscObjectComm((PetscObject)nep),PETSC_ERR_PLIB,"Internal error, solver returned without setting converged reason");

  /* sort eigenvalues according to nep->which parameter */
  ierr = NEPSortEigenvalues(nep,nep->nconv,nep->eig,nep->perm);CHKERRQ(ierr);

  ierr = PetscLogEventEnd(NEP_Solve,nep,0,0,0);CHKERRQ(ierr);

  /* various viewers */
  ierr = PetscOptionsGetViewer(PetscObjectComm((PetscObject)nep),((PetscObject)nep)->prefix,"-nep_view",&viewer,&format,&flg);CHKERRQ(ierr);
  if (flg && !PetscPreLoadingOn) {
    ierr = PetscViewerPushFormat(viewer,format);CHKERRQ(ierr);
    ierr = NEPView(nep,viewer);CHKERRQ(ierr);
    ierr = PetscViewerPopFormat(viewer);CHKERRQ(ierr);
    ierr = PetscViewerDestroy(&viewer);CHKERRQ(ierr);
  }

  flg = PETSC_FALSE;
  ierr = PetscOptionsGetBool(((PetscObject)nep)->prefix,"-nep_plot_eigs",&flg,NULL);CHKERRQ(ierr);
  if (flg) {
    ierr = PetscViewerDrawOpen(PETSC_COMM_SELF,0,"Computed Eigenvalues",PETSC_DECIDE,PETSC_DECIDE,300,300,&viewer);CHKERRQ(ierr);
    ierr = PetscViewerDrawGetDraw(viewer,0,&draw);CHKERRQ(ierr);
    ierr = PetscDrawSPCreate(draw,1,&drawsp);CHKERRQ(ierr);
    for (i=0;i<nep->nconv;i++) {
      re = PetscRealPart(nep->eig[i]);
      im = PetscImaginaryPart(nep->eig[i]);
      ierr = PetscDrawSPAddPoint(drawsp,&re,&im);CHKERRQ(ierr);
    }
    ierr = PetscDrawSPDraw(drawsp,PETSC_TRUE);CHKERRQ(ierr);
    ierr = PetscDrawSPDestroy(&drawsp);CHKERRQ(ierr);
    ierr = PetscViewerDestroy(&viewer);CHKERRQ(ierr);
  }

  /* Remove the initial subspace */
  nep->nini = 0;
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "NEP_KSPSolve"
PetscErrorCode NEP_KSPSolve(NEP nep,Vec b,Vec x)
{
  PetscErrorCode ierr;
  PetscInt       lits;

  PetscFunctionBegin;
  ierr = KSPSolve(nep->ksp,b,x);CHKERRQ(ierr);
  ierr = KSPGetIterationNumber(nep->ksp,&lits);CHKERRQ(ierr);
  nep->linits += lits;
  ierr = PetscInfo2(nep,"iter=%D, linear solve iterations=%D\n",nep->its,lits);CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "NEPProjectOperator"
/*@
   NEPProjectOperator - Computes the projection of the nonlinear operator.

   Collective on NEP

   Input Parameters:
+  nep - the nonlinear eigensolver context
.  j0  - initial index
.  j1  - final index
-  f   - workspace vector

   Notes:
   This is available for split operator only.

   The nonlinear operator T(lambda) is projected onto span(V), where V is
   an orthonormal basis built internally by the solver. The projected
   operator is equal to sum_i V'*A_i*V*f_i(lambda), so this function
   computes all matrices Ei = V'*A_i*V, and stores them in the extra
   matrices inside DS. Only rows/columns in the range [j0,j1-1] are computed,
   the previous ones are assumed to be available already.

   Level: developer

.seealso: NEPSetSplitOperator()
@*/
PetscErrorCode NEPProjectOperator(NEP nep,PetscInt j0,PetscInt j1,Vec f)
{
  PetscErrorCode ierr;
  PetscInt       i,j,k,ld;
  PetscScalar    *G,val;
  Vec            *V = nep->V;
  PetscBool      isherm,set,flg;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(nep,NEP_CLASSID,1);
  PetscValidLogicalCollectiveInt(nep,j0,2);
  PetscValidLogicalCollectiveInt(nep,j1,3);
  PetscValidHeaderSpecific(f,VEC_CLASSID,4);
  if (!nep->split) SETERRQ(PetscObjectComm((PetscObject)nep),PETSC_ERR_ARG_WRONGSTATE,"This solver requires a split operator");
  ierr = DSGetLeadingDimension(nep->ds,&ld);CHKERRQ(ierr);
  for (k=0;k<nep->nt;k++) {
    ierr = DSGetArray(nep->ds,DSMatExtra[k],&G);CHKERRQ(ierr);
    ierr = MatIsHermitianKnown(nep->A[k],&set,&flg);CHKERRQ(ierr);
    isherm = set? flg: PETSC_FALSE;
    for (j=j0;j<j1;j++) {
      if (!isherm) {
        if (j>0) { ierr = MatMultHermitianTranspose(nep->A[k],V[j],f);CHKERRQ(ierr); }
        ierr = VecMDot(f,j,V,G+j*ld);CHKERRQ(ierr);
        for (i=0;i<j;i++)
          G[j+i*ld] = PetscConj(G[i+j*ld]);
      }
      ierr = MatMult(nep->A[k],V[j],f);CHKERRQ(ierr);
      ierr = VecDot(f,V[j],&val);CHKERRQ(ierr);
      G[j+j*ld] = val;
      ierr = VecMDot(f,j,V,G+j*ld);CHKERRQ(ierr);
      if (isherm) {
        for (i=0;i<j;i++)
          G[j+i*ld] = PetscConj(G[i+j*ld]);
      }
    }
    ierr = DSRestoreArray(nep->ds,DSMatExtra[k],&G);CHKERRQ(ierr);
  }
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "NEPApplyFunction"
/*@
   NEPApplyFunction - Applies the nonlinear function T(lambda) to a given vector.

   Collective on NEP

   Input Parameters:
+  nep    - the nonlinear eigensolver context
.  lambda - scalar argument
.  x      - vector to be multiplied against
-  v      - workspace vector

   Output Parameters:
+  y   - result vector
.  A   - Function matrix
.  B   - optional preconditioning matrix
-  flg - flag indicating matrix structure (see MatStructure enum)

   Note:
   If the nonlinear operator is represented in split form, the result 
   y = T(lambda)*x is computed without building T(lambda) explicitly. In
   that case, parameters A, B and flg are not used. Otherwise, the matrix
   T(lambda) is built and the effect is the same as a call to
   NEPComputeFunction() followed by a MatMult().

   Level: developer

.seealso: NEPSetSplitOperator(), NEPComputeFunction()
@*/
PetscErrorCode NEPApplyFunction(NEP nep,PetscScalar lambda,Vec x,Vec v,Vec y,Mat *A,Mat *B,MatStructure *flg)
{
  PetscErrorCode ierr;
  PetscInt       i;
  PetscScalar    alpha;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(nep,NEP_CLASSID,1);
  PetscValidLogicalCollectiveScalar(nep,lambda,2);
  PetscValidHeaderSpecific(x,VEC_CLASSID,3);
  PetscValidHeaderSpecific(y,VEC_CLASSID,4);
  PetscValidHeaderSpecific(y,VEC_CLASSID,5);
  if (nep->split) {
    ierr = VecZeroEntries(y);CHKERRQ(ierr);
    for (i=0;i<nep->nt;i++) {
      ierr = FNEvaluateFunction(nep->f[i],lambda,&alpha);CHKERRQ(ierr);
      ierr = MatMult(nep->A[i],x,v);CHKERRQ(ierr);
      ierr = VecAXPY(y,alpha,v);CHKERRQ(ierr);
    }
  } else {
    ierr = NEPComputeFunction(nep,lambda,A,B,flg);CHKERRQ(ierr);
    ierr = MatMult(*A,x,y);CHKERRQ(ierr);
  }
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "NEPApplyJacobian"
/*@
   NEPApplyJacobian - Applies the nonlinear Jacobian T'(lambda) to a given vector.

   Collective on NEP

   Input Parameters:
+  nep    - the nonlinear eigensolver context
.  lambda - scalar argument
.  x      - vector to be multiplied against
-  v      - workspace vector

   Output Parameters:
+  y   - result vector
.  A   - Jacobian matrix
-  flg - flag indicating matrix structure (see MatStructure enum)

   Note:
   If the nonlinear operator is represented in split form, the result 
   y = T'(lambda)*x is computed without building T'(lambda) explicitly. In
   that case, parameters A and flg are not used. Otherwise, the matrix
   T'(lambda) is built and the effect is the same as a call to
   NEPComputeJacobian() followed by a MatMult().

   Level: developer

.seealso: NEPSetSplitOperator(), NEPComputeJacobian()
@*/
PetscErrorCode NEPApplyJacobian(NEP nep,PetscScalar lambda,Vec x,Vec v,Vec y,Mat *A,MatStructure *flg)
{
  PetscErrorCode ierr;
  PetscInt       i;
  PetscScalar    alpha;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(nep,NEP_CLASSID,1);
  PetscValidLogicalCollectiveScalar(nep,lambda,2);
  PetscValidHeaderSpecific(x,VEC_CLASSID,3);
  PetscValidHeaderSpecific(y,VEC_CLASSID,4);
  PetscValidHeaderSpecific(y,VEC_CLASSID,5);
  if (nep->split) {
    ierr = VecZeroEntries(y);CHKERRQ(ierr);
    for (i=0;i<nep->nt;i++) {
      ierr = FNEvaluateDerivative(nep->f[i],lambda,&alpha);CHKERRQ(ierr);
      ierr = MatMult(nep->A[i],x,v);CHKERRQ(ierr);
      ierr = VecAXPY(y,alpha,v);CHKERRQ(ierr);
    }
  } else {
    ierr = NEPComputeJacobian(nep,lambda,A,flg);CHKERRQ(ierr);
    ierr = MatMult(*A,x,y);CHKERRQ(ierr);
  }
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "NEPGetIterationNumber"
/*@
   NEPGetIterationNumber - Gets the current iteration number. If the
   call to NEPSolve() is complete, then it returns the number of iterations
   carried out by the solution method.

   Not Collective

   Input Parameter:
.  nep - the nonlinear eigensolver context

   Output Parameter:
.  its - number of iterations

   Level: intermediate

   Note:
   During the i-th iteration this call returns i-1. If NEPSolve() is
   complete, then parameter "its" contains either the iteration number at
   which convergence was successfully reached, or failure was detected.
   Call NEPGetConvergedReason() to determine if the solver converged or
   failed and why.

.seealso: NEPGetConvergedReason(), NEPSetTolerances()
@*/
PetscErrorCode NEPGetIterationNumber(NEP nep,PetscInt *its)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(nep,NEP_CLASSID,1);
  PetscValidIntPointer(its,2);
  *its = nep->its;
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "NEPGetConverged"
/*@
   NEPGetConverged - Gets the number of converged eigenpairs.

   Not Collective

   Input Parameter:
.  nep - the nonlinear eigensolver context

   Output Parameter:
.  nconv - number of converged eigenpairs

   Note:
   This function should be called after NEPSolve() has finished.

   Level: beginner

.seealso: NEPSetDimensions(), NEPSolve()
@*/
PetscErrorCode NEPGetConverged(NEP nep,PetscInt *nconv)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(nep,NEP_CLASSID,1);
  PetscValidIntPointer(nconv,2);
  *nconv = nep->nconv;
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "NEPGetConvergedReason"
/*@C
   NEPGetConvergedReason - Gets the reason why the NEPSolve() iteration was
   stopped.

   Not Collective

   Input Parameter:
.  nep - the nonlinear eigensolver context

   Output Parameter:
.  reason - negative value indicates diverged, positive value converged

   Possible values for reason:
+  NEP_CONVERGED_FNORM_ABS - function norm satisfied absolute tolerance
.  NEP_CONVERGED_FNORM_RELATIVE - function norm satisfied relative tolerance
.  NEP_CONVERGED_SNORM_RELATIVE - step norm satisfied relative tolerance
.  NEP_DIVERGED_LINEAR_SOLVE - inner linear solve failed
.  NEP_DIVERGED_FUNCTION_COUNT - reached maximum allowed function evaluations
.  NEP_DIVERGED_MAX_IT - required more than its to reach convergence
.  NEP_DIVERGED_BREAKDOWN - generic breakdown in method
-  NEP_DIVERGED_FNORM_NAN - Inf or NaN detected in function evaluation

   Note:
   Can only be called after the call to NEPSolve() is complete.

   Level: intermediate

.seealso: NEPSetTolerances(), NEPSolve(), NEPConvergedReason
@*/
PetscErrorCode NEPGetConvergedReason(NEP nep,NEPConvergedReason *reason)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(nep,NEP_CLASSID,1);
  PetscValidPointer(reason,2);
  *reason = nep->reason;
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "NEPGetEigenpair"
/*@
   NEPGetEigenpair - Gets the i-th solution of the eigenproblem as computed by
   NEPSolve(). The solution consists in both the eigenvalue and the eigenvector.

   Logically Collective on NEP

   Input Parameters:
+  nep - nonlinear eigensolver context
-  i   - index of the solution

   Output Parameters:
+  eig - eigenvalue
-  V   - eigenvector

   Notes:
   If PETSc is configured with real scalars, then complex eigenpairs cannot
   be obtained. Users should use a complex-scalar configuration. This
   behaviour is different to other SLEPc solvers such as EPS.

   The index i should be a value between 0 and nconv-1 (see NEPGetConverged()).
   Eigenpairs are indexed according to the ordering criterion established
   with NEPSetWhichEigenpairs().

   Level: beginner

.seealso: NEPSolve(), NEPGetConverged(), NEPSetWhichEigenpairs()
@*/
PetscErrorCode NEPGetEigenpair(NEP nep,PetscInt i,PetscScalar *eig,Vec V)
{
  PetscInt       k;
  PetscErrorCode ierr;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(nep,NEP_CLASSID,1);
  PetscValidLogicalCollectiveInt(nep,i,2);
  if (V) { PetscValidHeaderSpecific(V,VEC_CLASSID,4); PetscCheckSameComm(nep,1,V,4); }
  if (!nep->eig || !nep->V) SETERRQ(PetscObjectComm((PetscObject)nep),PETSC_ERR_ARG_WRONGSTATE,"NEPSolve must be called first");
  if (i<0 || i>=nep->nconv) SETERRQ(PetscObjectComm((PetscObject)nep),PETSC_ERR_ARG_OUTOFRANGE,"Argument 2 out of range");

  if (!nep->perm) k = i;
  else k = nep->perm[i];

  if (eig) *eig = nep->eig[k];
  if (V) { ierr = VecCopy(nep->V[k],V);CHKERRQ(ierr); }
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "NEPGetErrorEstimate"
/*@
   NEPGetErrorEstimate - Returns the error estimate associated to the i-th
   computed eigenpair.

   Not Collective

   Input Parameter:
+  nep - nonlinear eigensolver context
-  i   - index of eigenpair

   Output Parameter:
.  errest - the error estimate

   Notes:
   This is the error estimate used internally by the eigensolver. The actual
   error bound can be computed with NEPComputeRelativeError().

   Level: advanced

.seealso: NEPComputeRelativeError()
@*/
PetscErrorCode NEPGetErrorEstimate(NEP nep,PetscInt i,PetscReal *errest)
{
  PetscFunctionBegin;
  PetscValidHeaderSpecific(nep,NEP_CLASSID,1);
  PetscValidPointer(errest,3);
  if (!nep->eig) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_WRONGSTATE,"NEPSolve must be called first");
  if (i<0 || i>=nep->nconv) SETERRQ(PETSC_COMM_SELF,PETSC_ERR_ARG_OUTOFRANGE,"Argument 2 out of range");
  if (nep->perm) i = nep->perm[i];
  if (errest) *errest = nep->errest[i];
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "NEPComputeResidualNorm_Private"
/*
   NEPComputeResidualNorm_Private - Computes the norm of the residual vector
   associated with an eigenpair.
*/
PetscErrorCode NEPComputeResidualNorm_Private(NEP nep,PetscScalar lambda,Vec x,PetscReal *norm)
{
  PetscErrorCode ierr;
  Vec            u;
  Mat            T=nep->function;
  MatStructure   mats;

  PetscFunctionBegin;
  ierr = VecDuplicate(nep->V[0],&u);CHKERRQ(ierr);
  ierr = NEPComputeFunction(nep,lambda,&T,&T,&mats);CHKERRQ(ierr);
  ierr = MatMult(T,x,u);CHKERRQ(ierr);
  ierr = VecNorm(u,NORM_2,norm);CHKERRQ(ierr);
  ierr = VecDestroy(&u);CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "NEPComputeResidualNorm"
/*@
   NEPComputeResidualNorm - Computes the norm of the residual vector associated with
   the i-th computed eigenpair.

   Collective on NEP

   Input Parameter:
+  nep - the nonlinear eigensolver context
-  i   - the solution index

   Output Parameter:
.  norm - the residual norm, computed as ||T(lambda)x||_2 where lambda is the
   eigenvalue and x is the eigenvector.

   Notes:
   The index i should be a value between 0 and nconv-1 (see NEPGetConverged()).
   Eigenpairs are indexed according to the ordering criterion established
   with NEPSetWhichEigenpairs().

   Level: beginner

.seealso: NEPSolve(), NEPGetConverged(), NEPSetWhichEigenpairs()
@*/
PetscErrorCode NEPComputeResidualNorm(NEP nep,PetscInt i,PetscReal *norm)
{
  PetscErrorCode ierr;
  Vec            x;
  PetscScalar    lambda;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(nep,NEP_CLASSID,1);
  PetscValidLogicalCollectiveInt(nep,i,2);
  PetscValidPointer(norm,3);
  ierr = VecDuplicate(nep->V[0],&x);CHKERRQ(ierr);
  ierr = NEPGetEigenpair(nep,i,&lambda,x);CHKERRQ(ierr);
  ierr = NEPComputeResidualNorm_Private(nep,lambda,x,norm);CHKERRQ(ierr);
  ierr = VecDestroy(&x);CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "NEPComputeRelativeError_Private"
/*
   NEPComputeRelativeError_Private - Computes the relative error bound
   associated with an eigenpair.
*/
PetscErrorCode NEPComputeRelativeError_Private(NEP nep,PetscScalar lambda,Vec x,PetscReal *error)
{
  PetscErrorCode ierr;
  PetscReal      norm,er;

  PetscFunctionBegin;
  ierr = NEPComputeResidualNorm_Private(nep,lambda,x,&norm);CHKERRQ(ierr);
  ierr = VecNorm(x,NORM_2,&er);CHKERRQ(ierr);
  if (PetscAbsScalar(lambda) > norm) {
    *error = norm/(PetscAbsScalar(lambda)*er);
  } else {
    *error = norm/er;
  }
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "NEPComputeRelativeError"
/*@
   NEPComputeRelativeError - Computes the relative error bound associated
   with the i-th computed eigenpair.

   Collective on NEP

   Input Parameter:
+  nep - the nonlinear eigensolver context
-  i   - the solution index

   Output Parameter:
.  error - the relative error bound, computed as ||T(lambda)x||_2/||lambda*x||_2
   where lambda is the eigenvalue and x is the eigenvector.
   If lambda=0 the relative error is computed as ||T(lambda)x||_2/||x||_2.

   Level: beginner

.seealso: NEPSolve(), NEPComputeResidualNorm(), NEPGetErrorEstimate()
@*/
PetscErrorCode NEPComputeRelativeError(NEP nep,PetscInt i,PetscReal *error)
{
  PetscErrorCode ierr;
  Vec            x;
  PetscScalar    lambda;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(nep,NEP_CLASSID,1);
  PetscValidLogicalCollectiveInt(nep,i,2);
  PetscValidPointer(error,3);
  ierr = VecDuplicate(nep->V[0],&x);CHKERRQ(ierr);
  ierr = NEPGetEigenpair(nep,i,&lambda,x);CHKERRQ(ierr);
  ierr = NEPComputeRelativeError_Private(nep,lambda,x,error);CHKERRQ(ierr);
  ierr = VecDestroy(&x);CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "NEPSortEigenvalues"
/*@
   NEPSortEigenvalues - Sorts a list of eigenvalues according to the criterion
   specified via NEPSetWhichEigenpairs().

   Not Collective

   Input Parameters:
+  nep - the nonlinear eigensolver context
.  n   - number of eigenvalues in the list
-  eig - pointer to the array containing the eigenvalues

   Output Parameter:
.  perm - resulting permutation

   Note:
   The result is a list of indices in the original eigenvalue array
   corresponding to the first nev eigenvalues sorted in the specified
   criterion.

   Level: developer

.seealso: NEPSetWhichEigenpairs()
@*/
PetscErrorCode NEPSortEigenvalues(NEP nep,PetscInt n,PetscScalar *eig,PetscInt *perm)
{
  PetscErrorCode ierr;
  PetscInt       i,j,result,tmp;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(nep,NEP_CLASSID,1);
  PetscValidScalarPointer(eig,3);
  PetscValidIntPointer(perm,4);
  for (i=0;i<n;i++) perm[i] = i;
  /* insertion sort */
  for (i=n-1;i>=0;i--) {
    j = i + 1;
    while (j<n) {
      ierr = NEPCompareEigenvalues(nep,eig[perm[i]],eig[perm[j]],&result);CHKERRQ(ierr);
      if (result < 0) break;
      tmp = perm[j-1]; perm[j-1] = perm[j]; perm[j] = tmp;
      j++;
    }
  }
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "NEPCompareEigenvalues"
/*@
   NEPCompareEigenvalues - Compares two eigenvalues according to a certain criterion.

   Not Collective

   Input Parameters:
+  nep - the nonlinear eigensolver context
.  a   - the 1st eigenvalue
-  b   - the 2nd eigenvalue

   Output Parameter:
.  res - result of comparison

   Notes:
   Returns an integer less than, equal to, or greater than zero if the first
   eigenvalue is considered to be respectively less than, equal to, or greater
   than the second one.

   The criterion of comparison is related to the 'which' parameter set with
   NEPSetWhichEigenpairs().

   Level: developer

.seealso: NEPSortEigenvalues(), NEPSetWhichEigenpairs()
@*/
PetscErrorCode NEPCompareEigenvalues(NEP nep,PetscScalar a,PetscScalar b,PetscInt *result)
{
  PetscErrorCode ierr;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(nep,NEP_CLASSID,1);
  PetscValidIntPointer(result,4);
  if (!nep->comparison) SETERRQ(PETSC_COMM_SELF,1,"Undefined eigenvalue comparison function");
  ierr = (*nep->comparison)(a,0.0,b,0.0,result,nep->comparisonctx);CHKERRQ(ierr);
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "NEPGetOperationCounters"
/*@
   NEPGetOperationCounters - Gets the total number of function evaluations, dot
   products, and linear solve iterations used by the NEP object during the last
   NEPSolve() call.

   Not Collective

   Input Parameter:
.  nep - nonlinear eigensolver context

   Output Parameter:
+  nfuncs - number of function evaluations
.  dots   - number of dot product operations
-  lits   - number of linear iterations

   Notes:
   These counters are reset to zero at each successive call to NEPSolve().

   Level: intermediate

@*/
PetscErrorCode NEPGetOperationCounters(NEP nep,PetscInt* nfuncs,PetscInt* dots,PetscInt* lits)
{
  PetscErrorCode ierr;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(nep,NEP_CLASSID,1);
  if (nfuncs) *nfuncs = nep->nfuncs;
  if (dots) {
    if (!nep->ip) { ierr = NEPGetIP(nep,&nep->ip);CHKERRQ(ierr); }
    ierr = IPGetOperationCounters(nep->ip,dots);CHKERRQ(ierr);
  }
  if (lits) *lits = nep->linits;
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "NEPComputeFunction"
/*@
   NEPComputeFunction - Computes the function matrix T(lambda) that has been
   set with NEPSetFunction().

   Collective on NEP and Mat

   Input Parameters:
+  nep    - the NEP context
-  lambda - the scalar argument

   Output Parameters:
+  A   - Function matrix
.  B   - optional preconditioning matrix
-  flg - flag indicating matrix structure (see MatStructure enum)

   Notes:
   NEPComputeFunction() is typically used within nonlinear eigensolvers
   implementations, so most users would not generally call this routine
   themselves.

   Level: developer

.seealso: NEPSetFunction(), NEPGetFunction()
@*/
PetscErrorCode NEPComputeFunction(NEP nep,PetscScalar lambda,Mat *A,Mat *B,MatStructure *flg)
{
  PetscErrorCode ierr;
  PetscInt       i;
  PetscScalar    alpha;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(nep,NEP_CLASSID,1);
  PetscValidPointer(flg,5);

  if (nep->split) {

    ierr = MatZeroEntries(*A);CHKERRQ(ierr);
    for (i=0;i<nep->nt;i++) {
      ierr = FNEvaluateFunction(nep->f[i],lambda,&alpha);CHKERRQ(ierr);
      ierr = MatAXPY(*A,alpha,nep->A[i],nep->mstr);CHKERRQ(ierr);
    }
    if (*A != *B) SETERRQ(PetscObjectComm((PetscObject)nep),1,"Not implemented");

  } else {

    if (!nep->computefunction) SETERRQ(PetscObjectComm((PetscObject)nep),PETSC_ERR_USER,"Must call NEPSetFunction() first");

    *flg = DIFFERENT_NONZERO_PATTERN;
    ierr = PetscLogEventBegin(NEP_FunctionEval,nep,*A,*B,0);CHKERRQ(ierr);

    PetscStackPush("NEP user Function function");
    ierr = (*nep->computefunction)(nep,lambda,A,B,flg,nep->functionctx);CHKERRQ(ierr);
    PetscStackPop;

    ierr = PetscLogEventEnd(NEP_FunctionEval,nep,*A,*B,0);CHKERRQ(ierr);
    nep->nfuncs++;

  }
  PetscFunctionReturn(0);
}

#undef __FUNCT__
#define __FUNCT__ "NEPComputeJacobian"
/*@
   NEPComputeJacobian - Computes the Jacobian matrix T'(lambda) that has been
   set with NEPSetJacobian().

   Collective on NEP and Mat

   Input Parameters:
+  nep    - the NEP context
-  lambda - the scalar argument

   Output Parameters:
+  A   - Jacobian matrix
-  flg - flag indicating matrix structure (see MatStructure enum)

   Notes:
   Most users should not need to explicitly call this routine, as it
   is used internally within the nonlinear eigensolvers.

   Level: developer

.seealso: NEPSetJacobian(), NEPGetJacobian()
@*/
PetscErrorCode NEPComputeJacobian(NEP nep,PetscScalar lambda,Mat *A,MatStructure *flg)
{
  PetscErrorCode ierr;
  PetscInt       i;
  PetscScalar    alpha;

  PetscFunctionBegin;
  PetscValidHeaderSpecific(nep,NEP_CLASSID,1);
  PetscValidPointer(flg,4);

  if (nep->split) {

    ierr = MatZeroEntries(*A);CHKERRQ(ierr);
    for (i=0;i<nep->nt;i++) {
      ierr = FNEvaluateDerivative(nep->f[i],lambda,&alpha);CHKERRQ(ierr);
      ierr = MatAXPY(*A,alpha,nep->A[i],nep->mstr);CHKERRQ(ierr);
    }

  } else {

    if (!nep->computejacobian) SETERRQ(PetscObjectComm((PetscObject)nep),PETSC_ERR_USER,"Must call NEPSetJacobian() first");

    *flg = DIFFERENT_NONZERO_PATTERN;
    ierr = PetscLogEventBegin(NEP_JacobianEval,nep,*A,0,0);CHKERRQ(ierr);

    PetscStackPush("NEP user Jacobian function");
    ierr = (*nep->computejacobian)(nep,lambda,A,flg,nep->jacobianctx);CHKERRQ(ierr);
    PetscStackPop;

    ierr = PetscLogEventEnd(NEP_JacobianEval,nep,*A,0,0);CHKERRQ(ierr);

  }
  PetscFunctionReturn(0);
}

