// ---------------------------------------------------------------------
//
// Copyright (C) 2002 - 2018 by the deal.II authors
//
// This file is part of the deal.II library.
//
// The deal.II library is free software; you can use it, 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.
// The full text of the license can be found in the file LICENSE at
// the top level of the deal.II distribution.
//
// ---------------------------------------------------------------------

#ifndef dealii_mg_coarse_h
#define dealii_mg_coarse_h


#include <deal.II/lac/full_matrix.h>
#include <deal.II/lac/householder.h>
#include <deal.II/lac/linear_operator.h>
#include <deal.II/lac/matrix_lib.h>
#include <deal.II/multigrid/mg_base.h>

DEAL_II_NAMESPACE_OPEN

/*!@addtogroup mg */
/*@{*/

/**
 * Coarse grid solver using smoother only. This is a little wrapper,
 * transforming a smoother into a coarse grid solver.
 *
 * @author Denis Davydov, 2016.
 */
template <class VectorType = Vector<double> >
class MGCoarseGridApplySmoother :  public MGCoarseGridBase<VectorType>
{
public:
  /**
   * Default constructor.
   */
  MGCoarseGridApplySmoother ();

  /**
   * Constructor. Store a pointer to the smoother for later use.
   */
  MGCoarseGridApplySmoother (const MGSmootherBase<VectorType> &coarse_smooth);

  /**
   * Clear the pointer.
   */
  void clear ();

  /**
   * Initialize new data.
   */
  void initialize (const MGSmootherBase<VectorType> &coarse_smooth);

  /**
   * Implementation of the abstract function.
   */
  void operator() (const unsigned int level,
                   VectorType         &dst,
                   const VectorType   &src) const;

private:
  /**
   * Reference to the smoother.
   */
  SmartPointer<const MGSmootherBase<VectorType>, MGCoarseGridApplySmoother<VectorType> > coarse_smooth;
};


/**
 * Coarse grid solver using LAC iterative methods. This is a little wrapper,
 * transforming a triplet of iterative solver, matrix and preconditioner into
 * a coarse grid solver.
 *
 * The type of the matrix (i.e. the template parameter @p MatrixType) should
 * be derived from @p Subscriptor to allow for the use of a smart pointer to
 * it.
 *
 * @deprecated Use MGCoarseGridIterativeSolver instead.
 *
 * @author Guido Kanschat, 1999, Ralf Hartmann, 2002.
 */
template <typename SolverType, class VectorType = Vector<double> >
class DEAL_II_DEPRECATED MGCoarseGridLACIteration :  public MGCoarseGridBase<VectorType>
{
public:
  /**
   * Default constructor.
   */
  MGCoarseGridLACIteration ();

  /**
   * Constructor. Store solver, matrix and preconditioning method for later
   * use.
   */
  template <typename MatrixType, typename PreconditionerType>
  MGCoarseGridLACIteration (SolverType &,
                            const MatrixType &,
                            const PreconditionerType &);

  /**
   * Destructor freeing the pointers.
   */
  ~MGCoarseGridLACIteration ();

  /**
   * Initialize new data.
   */
  template <typename MatrixType, typename PreconditionerType>
  void initialize (SolverType &,
                   const MatrixType &,
                   const PreconditionerType &);

  /**
   * Clear all pointers.
   */
  void clear ();

  /**
   * Implementation of the abstract function. Calls the solver method with
   * matrix, vectors and preconditioner.
   */
  void operator() (const unsigned int level,
                   VectorType         &dst,
                   const VectorType   &src) const;

  /**
   * Set the matrix. This gives the possibility to replace the matrix that
   * was given to the constructor by a new matrix.
   */
  template <typename MatrixType>
  void set_matrix (const MatrixType &);

private:
  /**
   * Reference to the solver.
   */
  SmartPointer<SolverType,MGCoarseGridLACIteration<SolverType,VectorType> > solver;

  /**
   * LinearOperator wrapping a reference to the matrix.
   */
  LinearOperator<VectorType> matrix;

  /**
   * LinearOperator wrapping a reference to the preconditioner.
   */
  LinearOperator<VectorType> precondition;

};



/**
 * Coarse grid multigrid operator for an iterative solver.
 *
 * This class provides a wrapper for a deal.II iterative solver with a given
 * matrix and preconditioner as a coarse grid operator.
 */
template <class VectorType,
          class SolverType,
          class MatrixType,
          class PreconditionerType>
class MGCoarseGridIterativeSolver :  public MGCoarseGridBase<VectorType>
{
public:
  /**
   * Default constructor.
   */
  MGCoarseGridIterativeSolver ();

  /**
   * Constructor. Only a reference to these objects is stored, so
   * their lifetime needs to exceed the usage in this class.
   */
  MGCoarseGridIterativeSolver (SolverType &solver,
                               const MatrixType &matrix,
                               const PreconditionerType &precondition);

  /**
   * Initialize with new data, see the corresponding constructor for more
   * details.
   */
  void initialize (SolverType &solver,
                   const MatrixType &matrix,
                   const PreconditionerType &precondition);

  /**
   * Clear all pointers.
   */
  void clear ();

  /**
   * Implementation of the abstract function. Calls the solve method of the
   * given solver with matrix, vectors, and preconditioner.
   */
  virtual void operator() (const unsigned int level,
                           VectorType         &dst,
                           const VectorType   &src) const;

private:
  /**
   * Reference to the solver.
   */
  SmartPointer<SolverType,MGCoarseGridIterativeSolver<VectorType,
               SolverType,
               MatrixType,
               PreconditionerType> > solver;

  /**
   * Reference to the matrix.
   */
  SmartPointer<const MatrixType,MGCoarseGridIterativeSolver<VectorType,
               SolverType,
               MatrixType,
               PreconditionerType> > matrix;

  /**
   * Reference to the preconditioner.
   */
  SmartPointer<const PreconditionerType,MGCoarseGridIterativeSolver<VectorType,
               SolverType,
               MatrixType,
               PreconditionerType> > preconditioner;

};




/**
 * Coarse grid solver by QR factorization implemented in the class
 * Householder.
 *
 * Upon initialization, the QR decomposition of the matrix is computed. then,
 * the operator() uses Householder::least_squares() to compute the action of
 * the inverse.
 *
 * @author Guido Kanschat, 2003, 2012
 */
template <typename number = double, class VectorType = Vector<number> >
class MGCoarseGridHouseholder : public MGCoarseGridBase<VectorType>
{
public:
  /**
   * Constructor, taking the coarse grid matrix.
   */
  MGCoarseGridHouseholder (const FullMatrix<number> *A = nullptr);

  /**
   * Initialize for a new matrix.
   */
  void initialize (const FullMatrix<number> &A);

  void operator() (const unsigned int level,
                   VectorType         &dst,
                   const VectorType   &src) const;

private:
  /**
   * Matrix for QR-factorization.
   */
  Householder<number> householder;
};

/**
 * Coarse grid solver using singular value decomposition of LAPACK matrices.
 *
 * Upon initialization, the singular value decomposition of the matrix is
 * computed. then, the operator() uses
 *
 * @author Guido Kanschat, 2003, 2012
 */
template <typename number = double, class VectorType = Vector<number> >
class MGCoarseGridSVD : public MGCoarseGridBase<VectorType>
{
public:
  /**
   * Constructor leaving an uninitialized object.
   */
  MGCoarseGridSVD () = default;

  /**
   * Initialize for a new matrix. This resets the dimensions to the
   */
  void initialize (const FullMatrix<number> &A, const double threshold = 0);

  void operator() (const unsigned int level,
                   VectorType         &dst,
                   const VectorType   &src) const;

  /**
   * Write the singular values to @p deallog.
   */
  void log () const;

private:

  /**
   * Matrix for singular value decomposition.
   */
  LAPACKFullMatrix<number> matrix;
};

/*@}*/

#ifndef DOXYGEN
/* ------------------ Functions for MGCoarseGridApplySmoother -----------*/
template <class VectorType>
MGCoarseGridApplySmoother<VectorType>::MGCoarseGridApplySmoother ()
  : coarse_smooth(nullptr)
{
}

template <class VectorType>
MGCoarseGridApplySmoother<VectorType>::MGCoarseGridApplySmoother (const MGSmootherBase<VectorType> &coarse_smooth)
  : coarse_smooth(nullptr)
{
  initialize(coarse_smooth);
}


template <class VectorType>
void
MGCoarseGridApplySmoother<VectorType>::initialize (const MGSmootherBase<VectorType> &coarse_smooth_)
{
  coarse_smooth =
    SmartPointer<const MGSmootherBase<VectorType>, MGCoarseGridApplySmoother<VectorType> >
    (&coarse_smooth_,typeid(*this).name());
}


template <class VectorType>
void
MGCoarseGridApplySmoother<VectorType>::clear()
{
  coarse_smooth = nullptr;
}


template <class VectorType>
void
MGCoarseGridApplySmoother<VectorType>::operator() (const unsigned int level,
                                                   VectorType         &dst,
                                                   const VectorType   &src) const
{
  coarse_smooth->smooth(level, dst, src);
}

/* ------------------ Functions for MGCoarseGridLACIteration ------------ */


template <typename SolverType, class VectorType>
MGCoarseGridLACIteration<SolverType, VectorType>
::MGCoarseGridLACIteration()
  :
  solver(0, typeid(*this).name()),
  matrix(0),
  precondition(0)
{}


template <typename SolverType, class VectorType>
template <typename MatrixType, typename PreconditionerType>
MGCoarseGridLACIteration<SolverType, VectorType>
::MGCoarseGridLACIteration (SolverType               &s,
                            const MatrixType         &m,
                            const PreconditionerType &p)
  :
  solver(&s, typeid(*this).name())
{
  // Workaround: Unfortunately, not every "m" object has a rich enough
  // interface to populate reinit_(domain|range)_vector. Thus, supply an
  // empty LinearOperator exemplar.
  matrix = linear_operator<VectorType>(LinearOperator<VectorType>(), m);
  precondition = linear_operator<VectorType>(matrix, p);
}


template <typename SolverType, class VectorType>
MGCoarseGridLACIteration<SolverType, VectorType>
::~MGCoarseGridLACIteration()
{
  clear();
}


template <typename SolverType, class VectorType>
template <typename MatrixType, typename PreconditionerType>
void
MGCoarseGridLACIteration<SolverType, VectorType>
::initialize (SolverType               &s,
              const MatrixType         &m,
              const PreconditionerType &p)
{
  solver = &s;
  // Workaround: Unfortunately, not every "m" object has a rich enough
  // interface to populate reinit_(domain|range)_vector. Thus, supply an
  // empty LinearOperator exemplar.
  matrix = linear_operator<VectorType>(LinearOperator<VectorType>(), m);
  precondition = linear_operator<VectorType>(matrix, p);
}


template <typename SolverType, class VectorType>
void
MGCoarseGridLACIteration<SolverType, VectorType>
::clear()
{
  solver = nullptr;
  matrix = LinearOperator<VectorType>();
  precondition = LinearOperator<VectorType>();

}


template <typename SolverType, class VectorType>
void
MGCoarseGridLACIteration<SolverType, VectorType>
::operator() (const unsigned int /* level */,
              VectorType         &dst,
              const VectorType   &src) const
{
  Assert(solver!=nullptr, ExcNotInitialized());
  solver->solve(matrix, dst, src, precondition);
}


template <typename SolverType, class VectorType>
template <typename MatrixType>
void
MGCoarseGridLACIteration<SolverType, VectorType>
::set_matrix(const MatrixType &m)
{
  // Workaround: Unfortunately, not every "m" object has a rich enough
  // interface to populate reinit_(domain|range)_vector. Thus, supply an
  // empty LinearOperator exemplar.
  matrix = linear_operator<VectorType>(LinearOperator<VectorType>(), m);
}



/* ------------------ Functions for MGCoarseGridIterativeSolver ------------ */

template <class VectorType,
          class SolverType,
          class MatrixType,
          class PreconditionerType>
MGCoarseGridIterativeSolver<VectorType, SolverType, MatrixType, PreconditionerType>
::MGCoarseGridIterativeSolver ()
  :
  solver(0, typeid(*this).name()),
  matrix(0, typeid(*this).name()),
  preconditioner(0, typeid(*this).name())
{}



template <class VectorType,
          class SolverType,
          class MatrixType,
          class PreconditionerType>
MGCoarseGridIterativeSolver<VectorType, SolverType, MatrixType, PreconditionerType>
::MGCoarseGridIterativeSolver (SolverType &solver,
                               const MatrixType &matrix,
                               const PreconditionerType &preconditioner)
  :
  solver (&solver, typeid(*this).name()),
  matrix (&matrix, typeid(*this).name()),
  preconditioner (&preconditioner, typeid(*this).name())
{}



template <class VectorType,
          class SolverType,
          class MatrixType,
          class PreconditionerType>
void
MGCoarseGridIterativeSolver<VectorType, SolverType, MatrixType, PreconditionerType>
::initialize (SolverType &solver_,
              const MatrixType &matrix_,
              const PreconditionerType &preconditioner_)
{
  solver = &solver_;
  matrix = &matrix_;
  preconditioner = &preconditioner_;

}



template <class VectorType,
          class SolverType,
          class MatrixType,
          class PreconditionerType>
void
MGCoarseGridIterativeSolver<VectorType, SolverType, MatrixType, PreconditionerType>
::clear ()
{
  solver = 0;
  matrix = 0;
  preconditioner = 0;
}



template <class VectorType,
          class SolverType,
          class MatrixType,
          class PreconditionerType>
void
MGCoarseGridIterativeSolver<VectorType, SolverType, MatrixType, PreconditionerType>
::operator() (const unsigned int /*level*/,
              VectorType         &dst,
              const VectorType   &src) const
{
  Assert(solver!=nullptr, ExcNotInitialized());
  Assert(matrix!=nullptr, ExcNotInitialized());
  Assert(preconditioner!=nullptr, ExcNotInitialized());
  solver->solve(*matrix, dst, src, *preconditioner);
}



/* ------------------ Functions for MGCoarseGridHouseholder ------------ */

template <typename number, class VectorType>
MGCoarseGridHouseholder<number, VectorType>::MGCoarseGridHouseholder
(const FullMatrix<number> *A)
{
  if (A != nullptr) householder.initialize(*A);
}



template <typename number, class VectorType>
void
MGCoarseGridHouseholder<number, VectorType>::initialize(const FullMatrix<number> &A)
{
  householder.initialize(A);
}



template <typename number, class VectorType>
void
MGCoarseGridHouseholder<number, VectorType>::operator() (const unsigned int /*level*/,
                                                         VectorType         &dst,
                                                         const VectorType   &src) const
{
  householder.least_squares(dst, src);
}

//---------------------------------------------------------------------------



template <typename number, class VectorType>
void
MGCoarseGridSVD<number, VectorType>::initialize (const FullMatrix<number> &A,
                                                 double                    threshold)
{
  matrix.reinit(A.n_rows(), A.n_cols());
  matrix = A;
  matrix.compute_inverse_svd(threshold);
}


template <typename number, class VectorType>
void
MGCoarseGridSVD<number, VectorType>::operator() (
  const unsigned int /*level*/,
  VectorType         &dst,
  const VectorType   &src) const
{
  matrix.vmult(dst, src);
}


template <typename number, class VectorType>
void
MGCoarseGridSVD<number, VectorType>::log() const
{
  const unsigned int n = std::min(matrix.n_rows(), matrix.n_cols());

  for (unsigned int i=0; i<n; ++i)
    deallog << ' ' << matrix.singular_value(i);
  deallog << std::endl;
}


#endif // DOXYGEN

DEAL_II_NAMESPACE_CLOSE

#endif
