function [ x, error, total_iters, it_hist ] = ...
                     pcgsol ( x0, b, atv, params, pcv )

% Preconditioned Conjugate-Gradient solver
%
%  Disclaimer:
%
%    This code comes with no guarantee or warranty of any kind.
%
%  Author:
%
%    Tim Kelley
%
%  Reference:
%
%    Tim Kelley,
%    Iterative Methods for Linear and Nonlinear Equations,
%    SIAM, 2004,
%    ISBN: 0898713528,
%    LC: QA297.8.K45.
%
% Input:        x0=initial iterate
%               b=right hand side
%               atv, a matrix-vector product routine
%			atv must return Ax when x is input
%			the format for atv is
%                       function ax = atv(x)
%               params = two dimensional vector to control iteration
%                        params(1) = relative residual reduction factor
%                        params(2) = max number of iterations
%               pcv, a routine to apply the preconditioner
%                       if omitted, the identity is used.
%                       The format for pcv is
%                       function px = pcv(x).
%
% Output:       x=solution
%               error = vector of iteration residual norms
%               total_iters = number of iterations
%               it_hist (optional) = matrix of all iterations
%			useful for movies
%
% 
%

%
% initialization
%
  if ( nargout == 4 )
    it_hist = [];
   end

  n = length(b);
  errtol = params(1);
  maxiters = params(2);
  error = [];
  x = x0;

  if ( nargout == 4 )
    it_hist = [it_hist, x];
  end

  r = b - feval ( atv, x );

  if ( nargin == 4 )
    z = r;
  else
    z = feval ( pcv, r );
  end

  rho = z'*r;
  tst = norm(r);
  terminate = errtol*norm(b);
  error = [error,tst];
  it = 1;

  while ((tst > terminate) && (it <= maxiters))

    if ( it == 1 ) 
	p = z;
    else
	beta = rho/rhoold;
	p = z + beta*p;
    end

    w = feval(atv, p);
    alpha = p'*w;
%
% Test here to make sure the linear transformation is positive definite.
% A non-positive value of alpha is a very bad sign.
%
    if ( alpha <= 0.0 )
      [alpha, rho, it]
      error(' negative curvature ') 
    end

    alpha = rho/alpha;
    x = x+alpha*p;

    if ( nargout == 4 )
      it_hist = [it_hist, x]; 
    end

    r = r - alpha*w;
    tst = norm(r);
    rhoold = rho;

    if ( nargin == 4 )
      z = r;
    else
      z = feval(pcv, r);
    end

    rho = z'*r;
    it = it+1;
    error = [error,tst];
    total_iters = it - 1;

  end
