// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2016 Rasmus Munk Larsen <rmlarsen@google.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#ifndef EIGEN_COMPLETEORTHOGONALDECOMPOSITION_H
#define EIGEN_COMPLETEORTHOGONALDECOMPOSITION_H

namespace Eigen {

namespace internal {
template <typename _MatrixType>
struct traits<CompleteOrthogonalDecomposition<_MatrixType> >
    : traits<_MatrixType>
{
    enum { Flags = 0 };
};

}  // end namespace internal

/** \ingroup QR_Module
  *
  * \class CompleteOrthogonalDecomposition
  *
  * \brief Complete orthogonal decomposition (COD) of a matrix.
  *
  * \param MatrixType the type of the matrix of which we are computing the COD.
  *
  * This class performs a rank-revealing complete orthogonal decomposition of a
  * matrix  \b A into matrices \b P, \b Q, \b T, and \b Z such that
  * \f[
  *  \mathbf{A} \, \mathbf{P} = \mathbf{Q} \,
  *                     \begin{bmatrix} \mathbf{T} &  \mathbf{0} \\
  *                                     \mathbf{0} & \mathbf{0} \end{bmatrix} \, \mathbf{Z}
  * \f]
  * by using Householder transformations. Here, \b P is a permutation matrix,
  * \b Q and \b Z are unitary matrices and \b T an upper triangular matrix of
  * size rank-by-rank. \b A may be rank deficient.
  *
  * This class supports the \link InplaceDecomposition inplace decomposition \endlink mechanism.
  *
  * \sa MatrixBase::completeOrthogonalDecomposition()
  */
template <typename _MatrixType>
class CompleteOrthogonalDecomposition
{
  public:
    typedef _MatrixType MatrixType;
    enum
    {
        RowsAtCompileTime = MatrixType::RowsAtCompileTime,
        ColsAtCompileTime = MatrixType::ColsAtCompileTime,
        MaxRowsAtCompileTime = MatrixType::MaxRowsAtCompileTime,
        MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime
    };
    typedef typename MatrixType::Scalar Scalar;
    typedef typename MatrixType::RealScalar RealScalar;
    typedef typename MatrixType::StorageIndex StorageIndex;
    typedef typename internal::plain_diag_type<MatrixType>::type HCoeffsType;
    typedef PermutationMatrix<ColsAtCompileTime, MaxColsAtCompileTime>
    PermutationType;
    typedef typename internal::plain_row_type<MatrixType, Index>::type
    IntRowVectorType;
    typedef typename internal::plain_row_type<MatrixType>::type RowVectorType;
    typedef typename internal::plain_row_type<MatrixType, RealScalar>::type
    RealRowVectorType;
    typedef HouseholderSequence<
    MatrixType, typename internal::remove_all<
    typename HCoeffsType::ConjugateReturnType>::type>
    HouseholderSequenceType;
    typedef typename MatrixType::PlainObject PlainObject;

  private:
    typedef typename PermutationType::Index PermIndexType;

  public:
    /**
     * \brief Default Constructor.
     *
     * The default constructor is useful in cases in which the user intends to
     * perform decompositions via
     * \c CompleteOrthogonalDecomposition::compute(const* MatrixType&).
     */
    CompleteOrthogonalDecomposition() : m_cpqr(), m_zCoeffs(), m_temp() {}

    /** \brief Default Constructor with memory preallocation
     *
     * Like the default constructor but with preallocation of the internal data
     * according to the specified problem \a size.
     * \sa CompleteOrthogonalDecomposition()
     */
    CompleteOrthogonalDecomposition(Index rows, Index cols)
        : m_cpqr(rows, cols), m_zCoeffs((std::min)(rows, cols)), m_temp(cols) {}

    /** \brief Constructs a complete orthogonal decomposition from a given
     * matrix.
     *
     * This constructor computes the complete orthogonal decomposition of the
     * matrix \a matrix by calling the method compute(). The default
     * threshold for rank determination will be used. It is a short cut for:
     *
     * \code
     * CompleteOrthogonalDecomposition<MatrixType> cod(matrix.rows(),
     *                                                 matrix.cols());
     * cod.setThreshold(Default);
     * cod.compute(matrix);
     * \endcode
     *
     * \sa compute()
     */
    template <typename InputType>
    explicit
    CompleteOrthogonalDecomposition(const EigenBase<InputType>& matrix)
        : m_cpqr(matrix.rows(), matrix.cols()),
          m_zCoeffs((std::min)(matrix.rows(), matrix.cols())),
          m_temp(matrix.cols())
    {
        compute(matrix.derived());
    }

    /** \brief Constructs a complete orthogonal decomposition from a given matrix
      *
      * This overloaded constructor is provided for \link InplaceDecomposition inplace decomposition \endlink when \c MatrixType is a Eigen::Ref.
      *
      * \sa CompleteOrthogonalDecomposition(const EigenBase&)
      */
    template<typename InputType>
    explicit
    CompleteOrthogonalDecomposition(EigenBase<InputType>& matrix)
        : m_cpqr(matrix.derived()),
          m_zCoeffs((std::min)(matrix.rows(), matrix.cols())),
          m_temp(matrix.cols())
    {
        computeInPlace();
    }


    /** This method computes the minimum-norm solution X to a least squares
     * problem \f[\mathrm{minimize} \|A X - B\|, \f] where \b A is the matrix of
     * which \c *this is the complete orthogonal decomposition.
     *
     * \param b the right-hand sides of the problem to solve.
     *
     * \returns a solution.
     *
     */
    template <typename Rhs>
    inline const Solve<CompleteOrthogonalDecomposition, Rhs>
    solve(
        const MatrixBase<Rhs>& b) const
    {
        eigen_assert(m_cpqr.m_isInitialized &&
                     "CompleteOrthogonalDecomposition is not initialized.");
        return Solve<CompleteOrthogonalDecomposition, Rhs>(*this, b.derived());
    }

    HouseholderSequenceType
    householderQ(void) const;
    HouseholderSequenceType
    matrixQ(void) const
    {
        return m_cpqr.householderQ();
    }

    /** \returns the matrix \b Z.
     */
    MatrixType
    matrixZ() const
    {
        MatrixType Z = MatrixType::Identity(m_cpqr.cols(), m_cpqr.cols());
        applyZAdjointOnTheLeftInPlace(Z);
        return Z.adjoint();
    }

    /** \returns a reference to the matrix where the complete orthogonal
     * decomposition is stored
     */
    const MatrixType&
    matrixQTZ() const
    {
        return m_cpqr.matrixQR();
    }

    /** \returns a reference to the matrix where the complete orthogonal
     * decomposition is stored.
     * \warning The strict lower part and \code cols() - rank() \endcode right
     * columns of this matrix contains internal values.
     * Only the upper triangular part should be referenced. To get it, use
     * \code matrixT().template triangularView<Upper>() \endcode
     * For rank-deficient matrices, use
     * \code
     * matrixR().topLeftCorner(rank(), rank()).template triangularView<Upper>()
     * \endcode
     */
    const MatrixType&
    matrixT() const
    {
        return m_cpqr.matrixQR();
    }

    template <typename InputType>
    CompleteOrthogonalDecomposition&
    compute(const EigenBase<InputType>& matrix)
    {
        // Compute the column pivoted QR factorization A P = Q R.
        m_cpqr.compute(matrix);
        computeInPlace();
        return *this;
    }

    /** \returns a const reference to the column permutation matrix */
    const PermutationType&
    colsPermutation() const
    {
        return m_cpqr.colsPermutation();
    }

    /** \returns the absolute value of the determinant of the matrix of which
     * *this is the complete orthogonal decomposition. It has only linear
     * complexity (that is, O(n) where n is the dimension of the square matrix)
     * as the complete orthogonal decomposition has already been computed.
     *
     * \note This is only for square matrices.
     *
     * \warning a determinant can be very big or small, so for matrices
     * of large enough dimension, there is a risk of overflow/underflow.
     * One way to work around that is to use logAbsDeterminant() instead.
     *
     * \sa logAbsDeterminant(), MatrixBase::determinant()
     */
    typename MatrixType::RealScalar
    absDeterminant() const;

    /** \returns the natural log of the absolute value of the determinant of the
     * matrix of which *this is the complete orthogonal decomposition. It has
     * only linear complexity (that is, O(n) where n is the dimension of the
     * square matrix) as the complete orthogonal decomposition has already been
     * computed.
     *
     * \note This is only for square matrices.
     *
     * \note This method is useful to work around the risk of overflow/underflow
     * that's inherent to determinant computation.
     *
     * \sa absDeterminant(), MatrixBase::determinant()
     */
    typename MatrixType::RealScalar
    logAbsDeterminant() const;

    /** \returns the rank of the matrix of which *this is the complete orthogonal
     * decomposition.
     *
     * \note This method has to determine which pivots should be considered
     * nonzero. For that, it uses the threshold value that you can control by
     * calling setThreshold(const RealScalar&).
     */
    inline Index
    rank() const
    {
        return m_cpqr.rank();
    }

    /** \returns the dimension of the kernel of the matrix of which *this is the
     * complete orthogonal decomposition.
     *
     * \note This method has to determine which pivots should be considered
     * nonzero. For that, it uses the threshold value that you can control by
     * calling setThreshold(const RealScalar&).
     */
    inline Index
    dimensionOfKernel() const
    {
        return m_cpqr.dimensionOfKernel();
    }

    /** \returns true if the matrix of which *this is the decomposition represents
     * an injective linear map, i.e. has trivial kernel; false otherwise.
     *
     * \note This method has to determine which pivots should be considered
     * nonzero. For that, it uses the threshold value that you can control by
     * calling setThreshold(const RealScalar&).
     */
    inline bool
    isInjective() const
    {
        return m_cpqr.isInjective();
    }

    /** \returns true if the matrix of which *this is the decomposition represents
     * a surjective linear map; false otherwise.
     *
     * \note This method has to determine which pivots should be considered
     * nonzero. For that, it uses the threshold value that you can control by
     * calling setThreshold(const RealScalar&).
     */
    inline bool
    isSurjective() const
    {
        return m_cpqr.isSurjective();
    }

    /** \returns true if the matrix of which *this is the complete orthogonal
     * decomposition is invertible.
     *
     * \note This method has to determine which pivots should be considered
     * nonzero. For that, it uses the threshold value that you can control by
     * calling setThreshold(const RealScalar&).
     */
    inline bool
    isInvertible() const
    {
        return m_cpqr.isInvertible();
    }

    /** \returns the pseudo-inverse of the matrix of which *this is the complete
     * orthogonal decomposition.
     * \warning: Do not compute \c this->pseudoInverse()*rhs to solve a linear systems.
     * It is more efficient and numerically stable to call \c this->solve(rhs).
     */
    inline const Inverse<CompleteOrthogonalDecomposition>
    pseudoInverse() const
    {
        return Inverse<CompleteOrthogonalDecomposition>(*this);
    }

    inline Index
    rows() const
    {
        return m_cpqr.rows();
    }
    inline Index
    cols() const
    {
        return m_cpqr.cols();
    }

    /** \returns a const reference to the vector of Householder coefficients used
     * to represent the factor \c Q.
     *
     * For advanced uses only.
     */
    inline const HCoeffsType&
    hCoeffs() const
    {
        return m_cpqr.hCoeffs();
    }

    /** \returns a const reference to the vector of Householder coefficients
     * used to represent the factor \c Z.
     *
     * For advanced uses only.
     */
    const HCoeffsType&
    zCoeffs() const
    {
        return m_zCoeffs;
    }

    /** Allows to prescribe a threshold to be used by certain methods, such as
     * rank(), who need to determine when pivots are to be considered nonzero.
     * Most be called before calling compute().
     *
     * When it needs to get the threshold value, Eigen calls threshold(). By
     * default, this uses a formula to automatically determine a reasonable
     * threshold. Once you have called the present method
     * setThreshold(const RealScalar&), your value is used instead.
     *
     * \param threshold The new value to use as the threshold.
     *
     * A pivot will be considered nonzero if its absolute value is strictly
     * greater than
     *  \f$ \vert pivot \vert \leqslant threshold \times \vert maxpivot \vert \f$
     * where maxpivot is the biggest pivot.
     *
     * If you want to come back to the default behavior, call
     * setThreshold(Default_t)
     */
    CompleteOrthogonalDecomposition&
    setThreshold(const RealScalar& threshold)
    {
        m_cpqr.setThreshold(threshold);
        return *this;
    }

    /** Allows to come back to the default behavior, letting Eigen use its default
     * formula for determining the threshold.
     *
     * You should pass the special object Eigen::Default as parameter here.
     * \code qr.setThreshold(Eigen::Default); \endcode
     *
     * See the documentation of setThreshold(const RealScalar&).
     */
    CompleteOrthogonalDecomposition&
    setThreshold(Default_t)
    {
        m_cpqr.setThreshold(Default);
        return *this;
    }

    /** Returns the threshold that will be used by certain methods such as rank().
     *
     * See the documentation of setThreshold(const RealScalar&).
     */
    RealScalar
    threshold() const
    {
        return m_cpqr.threshold();
    }

    /** \returns the number of nonzero pivots in the complete orthogonal
     * decomposition. Here nonzero is meant in the exact sense, not in a
     * fuzzy sense. So that notion isn't really intrinsically interesting,
     * but it is still useful when implementing algorithms.
     *
     * \sa rank()
     */
    inline Index
    nonzeroPivots() const
    {
        return m_cpqr.nonzeroPivots();
    }

    /** \returns the absolute value of the biggest pivot, i.e. the biggest
     *          diagonal coefficient of R.
     */
    inline RealScalar
    maxPivot() const
    {
        return m_cpqr.maxPivot();
    }

    /** \brief Reports whether the complete orthogonal decomposition was
     * succesful.
     *
     * \note This function always returns \c Success. It is provided for
     * compatibility
     * with other factorization routines.
     * \returns \c Success
     */
    ComputationInfo
    info() const
    {
        eigen_assert(m_cpqr.m_isInitialized && "Decomposition is not initialized.");
        return Success;
    }

#ifndef EIGEN_PARSED_BY_DOXYGEN
    template <typename RhsType, typename DstType>
    EIGEN_DEVICE_FUNC void
    _solve_impl(const RhsType& rhs, DstType& dst) const;
#endif

  protected:
    static void
    check_template_parameters()
    {
        EIGEN_STATIC_ASSERT_NON_INTEGER(Scalar);
    }

    void
    computeInPlace();

    /** Overwrites \b rhs with \f$ \mathbf{Z}^* * \mathbf{rhs} \f$.
     */
    template <typename Rhs>
    void
    applyZAdjointOnTheLeftInPlace(Rhs& rhs) const;

    ColPivHouseholderQR<MatrixType> m_cpqr;
    HCoeffsType m_zCoeffs;
    RowVectorType m_temp;
};

template <typename MatrixType>
typename MatrixType::RealScalar
CompleteOrthogonalDecomposition<MatrixType>::absDeterminant() const
{
    return m_cpqr.absDeterminant();
}

template <typename MatrixType>
typename MatrixType::RealScalar
CompleteOrthogonalDecomposition<MatrixType>::logAbsDeterminant() const
{
    return m_cpqr.logAbsDeterminant();
}

/** Performs the complete orthogonal decomposition of the given matrix \a
 * matrix. The result of the factorization is stored into \c *this, and a
 * reference to \c *this is returned.
 *
 * \sa class CompleteOrthogonalDecomposition,
 * CompleteOrthogonalDecomposition(const MatrixType&)
 */
template <typename MatrixType>
void
CompleteOrthogonalDecomposition<MatrixType>::computeInPlace()
{
    check_template_parameters();

    // the column permutation is stored as int indices, so just to be sure:
    eigen_assert(m_cpqr.cols() <= NumTraits<int>::highest());

    const Index rank = m_cpqr.rank();
    const Index cols = m_cpqr.cols();
    const Index rows = m_cpqr.rows();
    m_zCoeffs.resize((std::min)(rows, cols));
    m_temp.resize(cols);

    if (rank < cols)
    {
        // We have reduced the (permuted) matrix to the form
        //   [R11 R12]
        //   [ 0  R22]
        // where R11 is r-by-r (r = rank) upper triangular, R12 is
        // r-by-(n-r), and R22 is empty or the norm of R22 is negligible.
        // We now compute the complete orthogonal decomposition by applying
        // Householder transformations from the right to the upper trapezoidal
        // matrix X = [R11 R12] to zero out R12 and obtain the factorization
        // [R11 R12] = [T11 0] * Z, where T11 is r-by-r upper triangular and
        // Z = Z(0) * Z(1) ... Z(r-1) is an n-by-n orthogonal matrix.
        // We store the data representing Z in R12 and m_zCoeffs.
        for (Index k = rank - 1; k >= 0; --k)
        {
            if (k != rank - 1)
            {
                // Given the API for Householder reflectors, it is more convenient if
                // we swap the leading parts of columns k and r-1 (zero-based) to form
                // the matrix X_k = [X(0:k, k), X(0:k, r:n)]
                m_cpqr.m_qr.col(k).head(k + 1).swap(
                    m_cpqr.m_qr.col(rank - 1).head(k + 1));
            }

            // Construct Householder reflector Z(k) to zero out the last row of X_k,
            // i.e. choose Z(k) such that
            // [X(k, k), X(k, r:n)] * Z(k) = [beta, 0, .., 0].
            RealScalar beta;
            m_cpqr.m_qr.row(k)
            .tail(cols - rank + 1)
            .makeHouseholderInPlace(m_zCoeffs(k), beta);
            m_cpqr.m_qr(k, rank - 1) = beta;

            if (k > 0)
            {
                // Apply Z(k) to the first k rows of X_k
                m_cpqr.m_qr.topRightCorner(k, cols - rank + 1)
                .applyHouseholderOnTheRight(
                    m_cpqr.m_qr.row(k).tail(cols - rank).transpose(), m_zCoeffs(k),
                    &m_temp(0));
            }

            if (k != rank - 1)
            {
                // Swap X(0:k,k) back to its proper location.
                m_cpqr.m_qr.col(k).head(k + 1).swap(
                    m_cpqr.m_qr.col(rank - 1).head(k + 1));
            }
        }
    }
}

template <typename MatrixType>
template <typename Rhs>
void
CompleteOrthogonalDecomposition<MatrixType>::applyZAdjointOnTheLeftInPlace(
    Rhs& rhs) const
{
    const Index cols = this->cols();
    const Index nrhs = rhs.cols();
    const Index rank = this->rank();
    Matrix<typename MatrixType::Scalar, Dynamic, 1> temp((std::max)(cols, nrhs));

    for (Index k = 0; k < rank; ++k)
    {
        if (k != rank - 1)
        {
            rhs.row(k).swap(rhs.row(rank - 1));
        }

        rhs.middleRows(rank - 1, cols - rank + 1)
        .applyHouseholderOnTheLeft(
            matrixQTZ().row(k).tail(cols - rank).adjoint(), zCoeffs()(k),
            &temp(0));

        if (k != rank - 1)
        {
            rhs.row(k).swap(rhs.row(rank - 1));
        }
    }
}

#ifndef EIGEN_PARSED_BY_DOXYGEN
template <typename _MatrixType>
template <typename RhsType, typename DstType>
void
CompleteOrthogonalDecomposition<_MatrixType>::_solve_impl(
    const RhsType& rhs, DstType& dst) const
{
    eigen_assert(rhs.rows() == this->rows());

    const Index rank = this->rank();

    if (rank == 0)
    {
        dst.setZero();
        return;
    }

    // Compute c = Q^* * rhs
    // Note that the matrix Q = H_0^* H_1^*... so its inverse is
    // Q^* = (H_0 H_1 ...)^T
    typename RhsType::PlainObject c(rhs);
    c.applyOnTheLeft(
        householderSequence(matrixQTZ(), hCoeffs()).setLength(rank).transpose());

    // Solve T z = c(1:rank, :)
    dst.topRows(rank) = matrixT()
                        .topLeftCorner(rank, rank)
                        .template triangularView<Upper>()
                        .solve(c.topRows(rank));

    const Index cols = this->cols();

    if (rank < cols)
    {
        // Compute y = Z^* * [ z ]
        //                   [ 0 ]
        dst.bottomRows(cols - rank).setZero();
        applyZAdjointOnTheLeftInPlace(dst);
    }

    // Undo permutation to get x = P^{-1} * y.
    dst = colsPermutation() * dst;
}
#endif

namespace internal {

template<typename DstXprType, typename MatrixType>
struct Assignment<DstXprType, Inverse<CompleteOrthogonalDecomposition<MatrixType> >, internal::assign_op<typename DstXprType::Scalar,typename CompleteOrthogonalDecomposition<MatrixType>::Scalar>, Dense2Dense>
{
    typedef CompleteOrthogonalDecomposition<MatrixType> CodType;
    typedef Inverse<CodType> SrcXprType;
    static void
    run(DstXprType &dst, const SrcXprType &src, const internal::assign_op<typename DstXprType::Scalar,typename CodType::Scalar> &)
    {
        dst = src.nestedExpression().solve(MatrixType::Identity(src.rows(), src.rows()));
    }
};

} // end namespace internal

/** \returns the matrix Q as a sequence of householder transformations */
template <typename MatrixType>
typename CompleteOrthogonalDecomposition<MatrixType>::HouseholderSequenceType
CompleteOrthogonalDecomposition<MatrixType>::householderQ() const
{
    return m_cpqr.householderQ();
}

/** \return the complete orthogonal decomposition of \c *this.
  *
  * \sa class CompleteOrthogonalDecomposition
  */
template <typename Derived>
const CompleteOrthogonalDecomposition<typename MatrixBase<Derived>::PlainObject>
MatrixBase<Derived>::completeOrthogonalDecomposition() const
{
    return CompleteOrthogonalDecomposition<PlainObject>(eval());
}

}  // end namespace Eigen

#endif  // EIGEN_COMPLETEORTHOGONALDECOMPOSITION_H
