// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2011 Gael Guennebaud <gael.guennebaud@inria.fr>
//
// 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_INCOMPLETE_LU_H
#define EIGEN_INCOMPLETE_LU_H

namespace Eigen {

template <typename _Scalar>
class IncompleteLU : public SparseSolverBase<IncompleteLU<_Scalar> >
{
  protected:
    typedef SparseSolverBase<IncompleteLU<_Scalar> > Base;
    using Base::m_isInitialized;

    typedef _Scalar Scalar;
    typedef Matrix<Scalar,Dynamic,1> Vector;
    typedef typename Vector::Index Index;
    typedef SparseMatrix<Scalar,RowMajor> FactorType;

  public:
    typedef Matrix<Scalar,Dynamic,Dynamic> MatrixType;

    IncompleteLU() {}

    template<typename MatrixType>
    IncompleteLU(const MatrixType& mat)
    {
        compute(mat);
    }

    Index
    rows() const
    {
        return m_lu.rows();
    }
    Index
    cols() const
    {
        return m_lu.cols();
    }

    template<typename MatrixType>
    IncompleteLU&
    compute(const MatrixType& mat)
    {
        m_lu = mat;
        int size = mat.cols();
        Vector diag(size);

        for (int i=0; i<size; ++i)
        {
            typename FactorType::InnerIterator k_it(m_lu,i);

            for (; k_it && k_it.index()<i; ++k_it)
            {
                int k = k_it.index();
                k_it.valueRef() /= diag(k);

                typename FactorType::InnerIterator j_it(k_it);
                typename FactorType::InnerIterator kj_it(m_lu, k);

                while (kj_it && kj_it.index()<=k)
                {
                    ++kj_it;
                }

                for (++j_it; j_it; )
                {
                    if (kj_it.index()==j_it.index())
                    {
                        j_it.valueRef() -= k_it.value() * kj_it.value();
                        ++j_it;
                        ++kj_it;
                    }

                    else if (kj_it.index()<j_it.index())
                    {
                        ++kj_it;
                    }

                    else
                    {
                        ++j_it;
                    }
                }
            }

            if (k_it && k_it.index()==i)
            {
                diag(i) = k_it.value();
            }

            else
            {
                diag(i) = 1;
            }
        }

        m_isInitialized = true;
        return *this;
    }

    template<typename Rhs, typename Dest>
    void
    _solve_impl(const Rhs& b, Dest& x) const
    {
        x = m_lu.template triangularView<UnitLower>().solve(b);
        x = m_lu.template triangularView<Upper>().solve(x);
    }

  protected:
    FactorType m_lu;
};

} // end namespace Eigen

#endif // EIGEN_INCOMPLETE_LU_H
