subroutine zbcg2 (print_resid,l,n,x,nonzero_x,rhs,matrix,toler, &
                  mxmatvec,work,info)

! subroutine zbcg2 (print_resid,l,n,x,nonzero_x,rhs,matvec,precond,toler, &
!                   mxmatvec,work,info)
!
! Improved "vanilla" BiCGStab(2) iterative method
!
! Copyright (c) 2004 by M.A.Botchev (http://www.math.utwente.nl/~botchevma/),
!                       University of Twente
! Permission to copy all or part of this work is granted,
! provided that the copies are not made or distributed
! for resale, and that the copyright notice and this
! notice are retained.
!
! This is the "vanilla" version of BiCGstab(\ell) as described
! in PhD thesis of D.R.Fokkema, Chapter 3 (also available as
! Preprint 976, Dept. of Mathematics, Utrecht University, URL
! http://www.math.uu.nl/publications/).  It includes two enhancements 
! to BiCGstab(\ell) proposed by G.Sleijpen and H.van der Vorst in
! 1) G.Sleijpen and H.van der Vorst "Maintaining convergence 
!    properties of BiCGstab methods in finite precision arithmetic",
!    Numerical Algorithms, 10, 1995, pp.203-223
! 2) G.Sleijpen and H.van der Vorst "Reliable updated residuals in
!    hybrid BiCG methods", Computing, 56, 1996, pp.141-163
!
! {{ This code based on original work of D.R.Fokkema:
!
! subroutine zbistbl v1.1 1998    
! Copyright (c) 1995-1998 by D.R. Fokkema.
! Permission to copy all or part of this work is granted, 
! provided that the copies are not made or distributed 
! for resale, and that the copyright notice and this 
! notice are retained.
!
! }}
!
! Your bug reports, comments, etc. are welcome: 
! m.a.botchev@math.utwente.nl
!
! ------------------------------
! Description of the parameters:
! ------------------------------
!
! print_resid (input) LOGICAL. If print_resid=.true. the number of 
!            matrix-vector multiplications done so far and residual norm will
!            be printed to the standard output each iteration
!
! l          (input) INTEGER the dimension \ell of BiCGstab(\ell)
!            in this simple version it is required that l <= 2
!            l=2 is often useful for systems with nonsymmetric matrices
!
! n          (input) INTEGER size of the linear system to solve 
!
! x          (input/output) COMPLEX*16 array dimension n
!            initial guess on input, solution on output
!
! rhs        (input) COMPLEX*16 array dimension n
!            the right-hand side (r.h.s.) vector
!
! matvec     (input) EXTERNAL name of matrix vector subroutine
!            to deliver y:=A*x by CALL matvec(n,x,y)
!
! nonzero_x  (input) LOGICAL tells
!            BiCGstab(\ell) if the initial guess x is zero or not. 
!            If nonzero_x is .FALSE., initial residual equals r.h.s. vector
!            and one MATVEC call is avoided
!
! toler      (input/output) DOUBLE PRECISION tolerance: the iterations are 
!            stopped as soon as || residual ||/|| initial residual|| <= toler,
!            the norm is Euclidean.  On output, if info>=0, the value of 
!            toler is set to the actually achieved residual reduction
!
! mxmatvec   (input/output) INTEGER.  On input: maximum number of matrix 
!            vector multiplications allowed to be done.  On output: 
!            if info>=0, mxmatvec is set to the actual number of matrix 
!            vector multiplications done
!
! work       (workspace) COMPLEX*16 array of dimension (n,2*l+5)
!
! info       (output) INTEGER.  info = 0 in case of succesful computations
!            and 
!            info = -m (<0) - means paramater number m has an illegal value
!            info = 1 - means no convergence achieved (stopping criterion
!            is not fulfilled)
!            info = 2 - means breakdown of the algorithm (taking a larger
!            value of parameter l usually helps)
!
! WARNING: If the iterations are ended normally (info=0 or info=1),
! the true residual norm is computed and returned as an output value 
! of the parameter toler.  The true residual norm can be slightly larger
! than the projected residual norm used by the algorithm to stop the
! iterations.  It may thus happen that on output info=0 but the value
! of toler is (slightly) larger than tolerance prescribed on input.
! ----------------------------------------------------------
implicit none

! Parameters:
logical,   intent(in)   :: print_resid,nonzero_x
integer,   intent(in)   :: l, n
integer,   intent(inout):: mxmatvec
integer,   intent(out)  :: info
complex*16,intent(inout):: x(n)
complex*16,intent(in)   :: rhs(n), matrix(n,n)
real*8,    intent(inout):: toler
complex*16,intent(out)  :: work(n,3+2*(l+1))
!!external                   matvec,precond

! Local variables:
complex*16 :: matrix_z(l+1,l+1),y0(l+1),yl(l+1),zy0(l+1),zyl(l+1)
logical    :: rcmp, xpdt
integer    :: i, j, k, nmatvec
complex*16 :: alpha, beta, omega, rho0, rho1, sigma
complex*16 :: varrho, hatgamma
real*8     :: rnrm0, rnrm
real*8     :: mxnrmx, mxnrmr
complex*16 :: kappa0, kappal

! Aliases for the parts of the work array:
integer          :: rr, r, u, xp, bp

! Constants:
real*8,    parameter :: delta = 1d-2
complex*16,parameter :: zzero = (0d0,0d0), zone = (1d0,0d0)

! Functions:
real*8     :: dnorm2_bcg
complex*16 :: zdot_bcg


info = 0

if (l<1 .or. l>2) info = -2
if (n<1) info = -3
if (toler<=0d0) info = -9
if (mxmatvec<0) info = -10

rr = 1
r = rr+1
u = r+(l+1)
xp = u+(l+1)
bp = xp+1

if (info/=0) return 

! Initialize first residual

if (nonzero_x) then
   call matvecmult (matrix, work(1:n,r), x, n)
   work(1:n,r) = rhs - work(1:n,r)
   nmatvec = 1
else
   work(1:n,r) = rhs
   nmatvec = 0
end if
!call precond (n, work(1:n,r))

! Initialize iteration loop

work(1:n,rr) = work(1:n,r)
work(1:n,bp) = work(1:n,r)
work(1:n,xp) = x
x = zzero

rnrm0 = dnorm2_bcg (n, work(1:n,r))
rnrm = rnrm0

mxnrmx = rnrm0
mxnrmr = rnrm0
rcmp = .false.
xpdt = .false.

alpha = zzero
omega = zone
sigma = zone
rho0  = zone

! Iterate

do while (rnrm > toler*rnrm0 .and. nmatvec < mxmatvec)

! =====================
! The BiCG part ---
! =====================

   rho0 = -omega*rho0
   do k=1,l
      rho1 = zdot_bcg (n, work(1:n,rr), work(1:n,r+k-1))
      if (rho0.eq.zzero) then
         info = 2
         toler = rnrm/rnrm0
         mxmatvec = nmatvec
         return
      endif
      beta = alpha*(rho1/rho0)
      rho0 = rho1
      do j=0,k-1
         work(1:n,u+j) = work(1:n,r+j) - beta*work(1:n,u+j)
      enddo
      call matvecmult (matrix, work(1:n,u+k), work(1:n,u+k-1), n )
!      call precond (n, work(1:n,u+k))
      nmatvec = nmatvec+1

      sigma = zdot_bcg (n, work(1:n,rr), work(1:n,u+k))
      if (sigma.eq.zzero) then
         info = 2
         toler = rnrm/rnrm0
         mxmatvec = nmatvec
         return
      endif
      alpha = rho1/sigma
      x(1:n) = alpha*work(1:n,u) + x(1:n)
      do j=0,k-1
         work(1:n,r+j) = -alpha*work(1:n,u+j+1) + work(1:n,r+j)
      enddo
      call matvecmult (matrix, work(1:n,r+k), work(1:n,r+k-1), n)
!      call precond (n, work(1:n,r+k))
      nmatvec = nmatvec+1
      rnrm = dnorm2_bcg (n, work(1:n,r))
      mxnrmx = max (mxnrmx, rnrm)
      mxnrmr = max (mxnrmr, rnrm)
   enddo

! ==================================
! The convex polynomial part ---
! ==================================

!  --- Z = R'R

   do i=1,l+1
      do j=1,i
         matrix_z(i,j) = conjg(zdot_bcg( n, work(1:n,r+j-1), work(1:n,r+i-1) ))
      end do
   end do

!  lower triangular part of Z is computed; compute the rest knowing that Z^H=Z
   do j=2,l+1
      matrix_z(1:j-1,j) = conjg( matrix_z(j,1:j-1) )
   end do

!  small vectors y0 and yl

y0(1) = -zone
y0(2) =      ( matrix_z(2,1) / matrix_z(2,2) )   ! works only for l=2
y0(l+1) = zzero

yl(1) = zzero
yl(2) =      ( matrix_z(2,3) / matrix_z(2,2) )   ! works only for l=2
yl(l+1) = -zone

!  --- Convex combination

! compute Z*y0 and Z*yl
zy0 = zzero
zyl = zzero
do j=1,l+1
   zy0 = zy0 + matrix_z(:,j)*y0(j)
   zyl = zyl + matrix_z(:,j)*yl(j)
end do

kappa0 = sqrt( abs(zdot_bcg(l+1,y0,zy0)) )
kappal = sqrt( abs(zdot_bcg(l+1,yl,zyl)) )

varrho = zdot_bcg(l+1,yl,zy0) / (kappa0*kappal)

hatgamma = varrho/abs(varrho) * max( abs(varrho),7d-1 ) 

y0 = y0 - (hatgamma*kappa0/kappal)*yl


!  --- Update

omega = y0(l+1)

do j=1,l
   work(1:n,u) = work(1:n,u) - y0(j+1)*work(1:n,u+j)
   x(1:n)      = x(1:n)      + y0(j+1)*work(1:n,r+j-1)
   work(1:n,r) = work(1:n,r) - y0(j+1)*work(1:n,r+j)
enddo

! y0 has changed; compute Z*y0 once more
zy0 = zzero
do j=1,l+1
   zy0 = zy0 + matrix_z(:,j)*y0(j)
end do

rnrm = sqrt( abs(zdot_bcg(l+1,y0,zy0)) )

! ================================
! The reliable update part ---
! ================================

mxnrmx = max (mxnrmx, rnrm)
mxnrmr = max (mxnrmr, rnrm)
xpdt =  (rnrm < delta*rnrm0  .and. rnrm0 < mxnrmx)
rcmp = ((rnrm < delta*mxnrmr .and. rnrm0 < mxnrmr) .or. xpdt)
if (rcmp) then
   call matvecmult (matrix, work(1:n,r), x, n)
!   call precond (n, work(1:n,r))
   nmatvec = nmatvec + 1
   work(1:n,r) =  work(1:n,bp) - work(1:n,r)
   mxnrmr = rnrm
   if (xpdt) then

      work(1:n,xp) = x(1:n) + work(1:n,xp)
      x = zzero
      work(1:n,bp) = work(1:n,r)

      mxnrmx = rnrm
   endif
endif

if (print_resid) print *,nmatvec,' ',rnrm

enddo

! =========================
! End of iterations ---
! =========================

x(1:n) = x(1:n) + work(1:n,xp)

if (rnrm>toler*rnrm0) info = 1

! compute the true residual:

! --------------------- One matvec can be saved by commenting out this:
call matvecmult (matrix, work(1:n,r), x, n )
work(1:n,r) = rhs(1:n) - work(1:n,r)   
!call precond (n, work(1:n,r))
rnrm = dnorm2_bcg(n,work(1:n,r))
nmatvec = nmatvec+1
! --------------------- One matvec can be saved by commenting out this^

toler = rnrm/rnrm0
mxmatvec = nmatvec

end subroutine zbcg2


complex*16 function zdot_bcg(n,zx,zy)

! complex inner product function

implicit none
integer,       intent(in):: n
complex*16,intent(in):: zx(n),zy(n)

zdot_bcg = sum( conjg(zx) * zy )

end function zdot_bcg


real*8 function dnorm2_bcg(n,zx)

! l2 norm function

implicit none
integer,       intent(in):: n
complex*16,intent(in):: zx(n)
complex*16,external  :: zdot_bcg

dnorm2_bcg = sqrt( abs( zdot_bcg(n, zx, zx) ) )

end function dnorm2_bcg
