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

namespace Seldon
{

 
//! Implements BiConjugate Gradient Stabilized (BICG-STAB(l))
 
/*!
    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 G L.G. Sleijpen, D. R. Fokkema
    BICGSTAB(l) For Linear Equations Involving Unsymmetric Matrices With
    Complex Spectrum
   
    \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 BiCgStabl(Matrix1& A, Vector1& x, const Vector1& b,
               
Preconditioner& M, Iteration<Titer> & iter)
 
{
   
const int N = A.GetM();
   
if (N <= 0)
     
return 0;
   
   
typedef typename Vector1::value_type Complexe;
   
int m = iter.GetRestart();
   
int l = m;
   
Complexe rho_0, rho_1, alpha, beta, omega, sigma, zero, unity;
    zero
= 0.0; unity = 1.0;
   
   
// q temporary vector before preconditioning, r0 initial residual
   
Vector1 q(b), r0(b);
   
Vector<Complexe> gamma(l+1), gamma_prime(l+1), gamma_twice(l+1);
   
Matrix<Complexe, General, RowMajor> tau(l+1,l+1);
   
// history of u and residual r
    std
::vector<Vector1> r(l+1, b), u(l+1, b);
   
for (int i = 0; i <= l; i++)
     
{
        r
[i].Zero();
        u
[i].Zero();
     
}
    tau
.Zero(); gamma.Zero(); gamma_prime.Zero(); gamma_twice.Zero();
   
   
// we compute the residual r = (b - Ax)
   
Copy(b, r[0]);
   
if (!iter.IsInitGuess_Null())
     
MltAdd(Complexe(-1), A, x, Complexe(1), r[0]);
   
else
      x
.Zero();
   
   
// we initialize iter
   
int success_init = iter.Init(b);
   
if (success_init !=0 )
     
return iter.ErrorCode();
   
   
Copy(r[0], r0); // we keep the first residual
   
   
// we initialize constants
    rho_0
= unity; alpha = zero; omega = unity; tau.Zero();
   
    iter
.SetNumberIteration(0);
   
// Loop until the stopping criteria are satisfied
   
while (! iter.Finished(r[0]))
     
{
        rho_0
*= -omega;
       
       
// Bi-CG Part
       
for (int j = 0; j < l; j++)
         
{
            rho_1
= DotProd(r[j], r0);
           
if (rho_0 == zero)
             
{
                iter
.Fail(1, "Bicgstabl breakdown #1");
               
break;
             
}
            beta
= alpha*(rho_1/rho_0);
            rho_0
= rho_1;
           
for (int i = 0; i <= j; i++)
             
{
               
Mlt(-beta, u[i]); Add(unity, r[i], u[i]);
             
}
            M
.Solve(A, u[j], q); // preconditioning
           
Mlt(A, q, u[j+1]); // product Matrix Vector
           
           
++iter;
            sigma
= DotProd(u[j+1], r0);
           
if (sigma == zero)
             
{
                iter
.Fail(2, "Bicgstabl Breakdown #2");
               
break;
             
}
            alpha
= rho_1/sigma;
           
Add(alpha, u[0], x);
           
for (int i = 0; i <= j; i++)
             
Add(-alpha, u[i+1], r[i]);
           
            M
.Solve(A, r[j], q); // preconditioning
           
Mlt(A, q, r[j+1]); // product matrix vector
           
           
++iter;
         
}
         
       
// MR Part  modified Gram-Schmidt
       
for (int j = 1; j <= l; j++)
         
{
           
for (int i = 1; i < j; i++)
             
{
               
if (gamma(i) != zero)
                 
{
                    tau
(i,j) = DotProd(r[j], r[i])/gamma(i);
                   
Add(-tau(i,j), r[i], r[j]);
                 
}
             
}
            gamma
(j) = DotProd(r[j], r[j]);
           
if (gamma(j) != zero)
              gamma_prime
(j) = DotProd(r[0], r[j])/gamma(j);
         
}
       
       
// gamma = tau-1 * gamma_prime
        gamma
(l) = gamma_prime(l); omega = gamma(l);
       
for (int j = l-1; j >= 1; j--)
         
{
            sigma
= zero;
           
for (int i = j+1; i <= l; i++)
              sigma
+= tau(j,i)*gamma(i);
             
            gamma
(j) = gamma_prime(j)-sigma;
         
}
         
       
// gamma_twice=T*S*gamma
       
for (int j = 1; j <= l-1; j++)
         
{
            sigma
= zero;
           
for (int i = j+1; i <= l-1; i++)
              sigma
+= tau(j,i)*gamma(i+1);
             
            gamma_twice
(j) = gamma(j+1)+sigma;
         
}
         
       
// update
       
Add(gamma(1), r[0], x);
       
Add(-gamma_prime(l), r[l], r[0]);
       
Add(-gamma(l), u[l], u[0]);
       
for (int j = 1;j <= l-1; j++)
         
{
           
Add(-gamma(j), u[j], u[0]);
           
Add(gamma_twice(j), r[j], x);
           
Add(-gamma_prime(j), r[j], r[0]);
         
}
     
}
   
// change of coordinates (right preconditioning)
   
Copy(x,q); M.Solve(A, q, x);
   
return iter.ErrorCode();
 
}
 
}

#define SELDON_FILE_ITERATIVE_BICGSTABL_CXX
#endif