// Copyright (C) 2003-2009 Marc Duruflé
// Copyright (C) 2001-2009 Vivien Mallet
//
// This file is part of the linear-algebra library Seldon,
// http://seldon.sourceforge.net/.
//
// Seldon is free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the Free
// Software Foundation; either version 2.1 of the License, or (at your option)
// any later version.
//
// Seldon 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 Seldon. If not, see http://www.gnu.org/licenses/.


#ifndef SELDON_FILE_ITERATIVE_GCR_CXX

namespace Seldon
{

  //! Solves a linear system by using Generalized Conjugate Residual (GCR)
  /*!
    Solves the linear system Ax = b with restarted Preconditioned
    Generalized Conjugate Residual Algorithm.
    
    return value of 0 indicates convergence within the
    maximum number of iterations (determined by the iter object).
    return value of 1 indicates a failure to converge.
    
    See: Y. Saad, Iterative Methods for Sparse Linear System, PWS Publishing
    Company, 1996

    \param[in] A  Complex General Matrix
    \param[in,out] x  Vector on input it is the initial guess
    on output it is the solution
    \param[in] b  Vector right hand side of the linear system
    \param[in] M Right preconditioner
    \param[in] outer Iteration parameters
  */
  template <class Titer, class Matrix1, class Vector1, class Preconditioner>
  int Gcr(Matrix1& A, Vector1& x, const Vector1& b,
	  Preconditioner& M, Iteration<Titer> & outer)
  {
    const int N = A.GetM();
    if (N <= 0)
      return 0;
    
    typedef typename Vector1::value_type Complexe;
    int m = outer.GetRestart();
    // we initialize outer
    int success_init = outer.Init(b);
    if (success_init != 0)
      return outer.ErrorCode();
    
    std::vector<Vector1> p(m+1, b), w(m+1, b);
    
    Vector<Complexe> beta(m+1);
    
    Vector1 r(b), q(b), u(b);
    
    for (int i = 0; i < (m+1); i++)
      {
	p[i].Zero();
	w[i].Zero();
      }

    // we compute initial residual
    Copy(b,u);
    if (!outer.IsInitGuess_Null())
      MltAdd(Complexe(-1), A, x, Complexe(1), u);
    else
      x.Zero();
    
    M.Solve(A, u, r);
    
    Complexe alpha,delta;
    
    Titer normr = Norm2(r);
    outer.SetNumberIteration(0);
    // Loop until the stopping criteria are satisfied
    while (! outer.Finished(r))
      {
	// m is the maximum number of inner iterations
	Iteration<Titer> inner(outer);
	inner.SetNumberIteration(outer.GetNumberIteration());
	inner.SetMaxNumberIteration(outer.GetNumberIteration()+m);
	Copy(r, p[0]);
	Mlt(Titer(1)/normr, p[0]);
	
	int j = 0;
	
	while (! inner.Finished(r) )
	  {
	    // product matrix vector u=A*p(j)
	    Mlt(A, p[j], u);
	    
	    // preconditioning
	    M.Solve(A, u, w[j]);
	    
	    beta(j) = DotProdConj(w[j], w[j]);
	    if (beta(j) == Complexe(0))
	      {
		outer.Fail(1, "Gcr breakdown #1");
		break;
	      }
	    
	    // new iterate x = x + alpha*p(j) new residual r = r - alpha*w(j)
	    // where alpha = (conj(r_j),A*p_j)/(A*p_j,A*p_j)
	    alpha = DotProdConj(w[j], r) / beta(j);
	    Add(alpha, p[j], x);
	    Add(-alpha, w[j], r);
	    
	    ++inner;
	    ++outer;
	    // product Matrix vector u = A*r
	    Mlt(A, r, u);
	    M.Solve(A, u, q);
	    
	    Copy(r, p[j+1]);
	    // we compute direction p(j+1) = r(j+1) +
	    // \sum_{i=0..j} ( -(A*r_j+1,A*p_i)/(A*p_i,A*p_i) p(i))
	    for (int i = 0; i <= j; i++)
	      {
		delta = -DotProdConj(w[i], q)/beta(i);
		Add(delta, p[i], p[j+1]);
	      }
	    
	    ++inner;
	    ++outer;
	    ++j;
	  }
	normr = Norm2(r);
      }
    
    return outer.ErrorCode();
  }
  
} // end namespace

#define SELDON_FILE_ITERATIVE_GCR_CXX
#endif