// 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_TFQMR_CXX

namespace Seldon
{
  
  //! Solves a linear system by using Transpose Free Quasi-Minimal Residual
  /*!
    Solves the unsymmetric linear system Ax = b using TFQMR.
    
    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: R. W. Freund, A Transpose-Free Quasi-Minimal Residual algorithm for
    non-Hermitian linear system. SIAM J. on Sci. Comp. 14(1993), pp. 470-482
    
    \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] iter Iteration parameters
  */
  template <class Titer, class Matrix1, class Vector1, class Preconditioner>
  int TfQmr(Matrix1& A, Vector1& x, const Vector1& b,
	    Preconditioner& M, Iteration<Titer> & iter)
  {
    const int N = A.GetM();
    if (N <= 0)
      return 0;
    
    Vector1 tmp(b), r0(b), v(b), h(b), w(b),
      y1(b), g(b), y0(b), rtilde(b), d(b);
    
    typedef typename Vector1::value_type Complexe;
    Complexe sigma, alpha, beta, eta, rho, rho0;
    Titer c, kappa, tau, theta;
    
    tmp.Zero();
    // x is initial value
    // 1. r0 = M^{-1} (b - A x)
    Copy(b, tmp);
    if (!iter.IsInitGuess_Null())
      MltAdd(Complexe(-1), A, x, Complexe(1), tmp);
    else
      x.Zero();
    
    M.Solve(A, tmp, r0);
    
    // we initialize iter
    int success_init = iter.Init(r0);
    if (success_init != 0)
      return iter.ErrorCode();
    
    // 2. w=y=r
    Copy(r0, w);
    Copy(r0, y1);
    
    // 3. g=v=M^{-1}Ay
    Copy(y1, v);
    Mlt(A, v, tmp);
    M.Solve(A, tmp, v);
    ++iter;
    
    Copy(v, g);
    
    // 4. d=0
    d.Zero();
    
    // 5. tau = ||r||
    tau = Norm2(r0);
    
    // 6. theta = eta = 0
    theta = 0.0;
    eta = 0.0;
    
    // 7. rtilde = r
    Copy(r0, rtilde);
    
    // 8. rho=dot(rtilde, r)
    rho = DotProd(rtilde, r0);
    rho0 = rho;
    
    iter.SetNumberIteration(0);
    // Loop until the stopping criteria are reached
    for (;;)
      {
	// 9. 10. 11.
	// sigma=dot(rtilde,v)
	// alpha=rho/sigma
	// y2k=y(2k-1)-alpha*v
	sigma = DotProd(rtilde, v);
	
	if (sigma == Complexe(0))
	  {
	    iter.Fail(1, "Tfqmr breakdown: sigma=0");
	    break;
	  }
	alpha = rho / sigma;
	
	//y0 = y1 - alpha * v;
	Copy(y1, y0);
	Add(-alpha, v, y0);
	
	// 12. h=M^{-1}*A*y
	Copy(y0, h);
	Mlt(A, h, tmp);
	M.Solve(A, tmp, h);
	//split the loop of "for m = 2k-1, 2k"
	
	//The first one
	// 13. w = w-alpha*M^{-1} A y0
	//w = w - alpha * g;
	Add(-alpha, g, w);
	// 18. d=y0+((theta0^2)*eta0/alpha)*d         //need check breakdown
	if (alpha == Complexe(0))
	  {
	    iter.Fail(2, "Tfqmr breakdown: alpha=0");
	    break;
	  }
	//d = y1 + ( theta * theta * eta / alpha ) * d;
	Mlt(theta * theta * eta / alpha,d);
	Add(Complexe(1), y1, d);
	
	// 14. theta=||w||_2/tau0       //need check breakdown
	if (tau == Titer(0))
	  {
	    iter.Fail(3, "Tfqmr breakdown: tau=0");
	    break;
	  }
	theta  = Norm2(w) / tau;
	
	// 15. c = 1/sqrt(1+theta^2)
	c = Titer(1) / sqrt(Titer(1) + theta * theta);
	
	// 16. tau = tau0*theta*c
	tau = tau * c * theta;
	
	// 17.  eta = (c^2)*alpha
	eta = c * c * alpha;
	
	// 19. x = x+eta*d
	Add(eta, d, x);
	// 20. kappa = tau*sqrt(m+1)
	kappa = tau * sqrt( 2.* (iter.GetNumberIteration()+1) );
	
	// 21. check stopping criterion
	if ( iter.Finished(kappa) )
	  {
	    break;
	  }
	++iter;
	// g = h;
	Copy(h, g);
	//The second one
	
	// 13. w = w-alpha*M^{-1} A y0
	// w = w - alpha * g;
	Add(-alpha,g,w);
	// 18. d = y0+((theta0^2)*eta0/alpha)*d
	Mlt(theta * theta * eta / alpha,d);
	Add(Complexe(1), y0, d);
	// 14. theta=||w||_2/tau0
	if (tau == Titer(0))
	  {
	    iter.Fail(4, "Tfqmr breakdown: tau=0");
	    break;
	  }
	theta = Norm2(w) / tau;
	
	// 15. c = 1/sqrt(1+theta^2)
	c = Titer(1) / sqrt(Titer(1) + theta * theta);
	
	// 16. tau = tau0*theta*c
	tau = tau * c * theta;
	
	// 17.  eta = (c^2)*alpha
	eta = c * c * alpha;
	
	// 19. x = x+eta*d
	Add(eta, d, x);
	
	// 20. kappa = tau*sqrt(m+1)
	kappa = tau * sqrt(2.* (iter.GetNumberIteration()+1)  + 1.);
	
	// 21. check stopping criterion
	if ( iter.Finished(kappa) )
	  {
	    break;
	  }
	
	// 22. rho = dot(rtilde,w)
	// 23. beta = rho/rho0                     //need check breakdown
	
	rho0 = rho;
	rho = DotProd(rtilde, w);
	if (rho0 == Complexe(0))
	  {
	    iter.Fail(5, "tfqmr breakdown: beta=0");
	    break;
	  }
	beta = rho/rho0;
	
	// 24. y = w+beta*y0
	Copy(w, y1);
	Add(beta, y0, y1);
	
	// 25. g=M^{-1} A y
	Copy(y1, g);
	Mlt(A, g, tmp);
	M.Solve(A, tmp, g);
	
	// 26. v = M^{-1}A y + beta*( M^{-1} A y0 + beta*v)
	
	Mlt(beta*beta, v);
	Add(beta, h, v);
	Add(Complexe(1), g, v);
	
	++iter;
      }
    
    return iter.ErrorCode();
  }

} // end namespace

#define SELDON_FILE_ITERATIVE_TFQMR_CXX
#endif