//
// This file is part of the book "Language C++ et calcul scientifique"
//
// Copyright (C) 2005-2012 Pierre Saramito <Pierre.Saramito@imag.fr>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
#include<iostream>
template<typename Mat, typename Vec,
  typename Precond, typename Real>
int cg(const Mat &A, Vec &x,
  const Vec &b, const Precond &M,
  int maxiter, Real tol)
{
  using namespace std;
  Real nb = norm(b);
  if (nb == 0) { x = 0; return 0; }
  Vec r = b - A*x;
  Vec z = M.solve(r);
  Vec p = z;
  Real rho = dot(r, z);
  Real rho_prev = rho;
  clog<<"pcg: 0 "<<norm(r)/nb<<endl;
  for (int i = 1; i < maxiter; i++) {
    Vec q = A*p;
    Real alpha = rho/dot(p, q);
    x += alpha*p;
    r -= alpha*q;
    Real nr = norm(r)/nb;
    clog<<"pcg: "<<i<<" "<<nr<<endl;
    if (nr <= tol) return 0;
    z = M.solve(r);
    rho = dot(r, z);
    Real beta = rho/rho_prev;
    rho_prev = rho;
    p = z + beta*p;
  }
  return 1;
}
