// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2012 Désiré Nuentsa-Wakam <desire.nuentsa_wakam@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/.


/*

 * NOTE: This file is the modified version of sp_coletree.c file in SuperLU

 * -- SuperLU routine (version 3.1) --
 * Univ. of California Berkeley, Xerox Palo Alto Research Center,
 * and Lawrence Berkeley National Lab.
 * August 1, 2008
 *
 * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
 *
 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY
 * EXPRESSED OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
 *
 * Permission is hereby granted to use or copy this program for any
 * purpose, provided the above notices are retained on all copies.
 * Permission to modify the code and to distribute modified code is
 * granted, provided the above notices are retained, and a notice that
 * the code was modified is included with the above copyright notice.
 */
#ifndef SPARSE_COLETREE_H
#define SPARSE_COLETREE_H

namespace Eigen {

namespace internal {

/** Find the root of the tree/set containing the vertex i : Use Path halving */
template<typename Index, typename IndexVector>
Index
etree_find (Index i, IndexVector& pp)
{
    Index p = pp(i); // Parent
    Index gp = pp(p); // Grand parent

    while (gp != p)
    {
        pp(i) = gp; // Parent pointer on find path is changed to former grand parent
        i = gp;
        p = pp(i);
        gp = pp(p);
    }

    return p;
}

/** Compute the column elimination tree of a sparse matrix
  * \param mat The matrix in column-major format.
  * \param parent The elimination tree
  * \param firstRowElt The column index of the first element in each row
  * \param perm The permutation to apply to the column of \b mat
  */
template <typename MatrixType, typename IndexVector>
int
coletree(const MatrixType& mat, IndexVector& parent, IndexVector& firstRowElt, typename MatrixType::StorageIndex *perm=0)
{
    typedef typename MatrixType::StorageIndex StorageIndex;
    StorageIndex nc = convert_index<StorageIndex>(mat.cols()); // Number of columns
    StorageIndex m = convert_index<StorageIndex>(mat.rows());
    StorageIndex diagSize = (std::min)(nc,m);
    IndexVector root(nc); // root of subtree of etree
    root.setZero();
    IndexVector pp(nc); // disjoint sets
    pp.setZero(); // Initialize disjoint sets
    parent.resize(mat.cols());
    //Compute first nonzero column in each row
    firstRowElt.resize(m);
    firstRowElt.setConstant(nc);
    firstRowElt.segment(0, diagSize).setLinSpaced(diagSize, 0, diagSize-1);
    bool found_diag;

    for (StorageIndex col = 0; col < nc; col++)
    {
        StorageIndex pcol = col;

        if (perm)
        {
            pcol  = perm[col];
        }

        for (typename MatrixType::InnerIterator it(mat, pcol); it; ++it)
        {
            Index row = it.row();
            firstRowElt(row) = (std::min)(firstRowElt(row), col);
        }
    }

    /* Compute etree by Liu's algorithm for symmetric matrices,
            except use (firstRowElt[r],c) in place of an edge (r,c) of A.
      Thus each row clique in A'*A is replaced by a star
      centered at its first vertex, which has the same fill. */
    StorageIndex rset, cset, rroot;

    for (StorageIndex col = 0; col < nc; col++)
    {
        found_diag = col>=m;
        pp(col) = col;
        cset = col;
        root(cset) = col;
        parent(col) = nc;
        /* The diagonal element is treated here even if it does not exist in the matrix
         * hence the loop is executed once more */
        StorageIndex pcol = col;

        if (perm)
        {
            pcol  = perm[col];
        }

        for (typename MatrixType::InnerIterator it(mat, pcol); it||!found_diag; ++it)
        {
            //  A sequence of interleaved find and union is performed
            Index i = col;

            if (it)
            {
                i = it.index();
            }

            if (i == col)
            {
                found_diag = true;
            }

            StorageIndex row = firstRowElt(i);

            if (row >= col)
            {
                continue;
            }

            rset = internal::etree_find(row, pp); // Find the name of the set containing row
            rroot = root(rset);

            if (rroot != col)
            {
                parent(rroot) = col;
                pp(cset) = rset;
                cset = rset;
                root(cset) = col;
            }
        }
    }

    return 0;
}

/**
  * Depth-first search from vertex n.  No recursion.
  * This routine was contributed by Cédric Doucet, CEDRAT Group, Meylan, France.
*/
template <typename IndexVector>
void
nr_etdfs (typename IndexVector::Scalar n, IndexVector& parent, IndexVector& first_kid, IndexVector& next_kid, IndexVector& post, typename IndexVector::Scalar postnum)
{
    typedef typename IndexVector::Scalar StorageIndex;
    StorageIndex current = n, first, next;

    while (postnum != n)
    {
        // No kid for the current node
        first = first_kid(current);

        // no kid for the current node
        if (first == -1)
        {
            // Numbering this node because it has no kid
            post(current) = postnum++;

            // looking for the next kid
            next = next_kid(current);

            while (next == -1)
            {
                // No more kids : back to the parent node
                current = parent(current);
                // numbering the parent node
                post(current) = postnum++;

                // Get the next kid
                next = next_kid(current);
            }

            // stopping criterion
            if (postnum == n+1)
            {
                return;
            }

            // Updating current node
            current = next;
        }

        else
        {
            current = first;
        }
    }
}


/**
  * \brief Post order a tree
  * \param n the number of nodes
  * \param parent Input tree
  * \param post postordered tree
  */
template <typename IndexVector>
void
treePostorder(typename IndexVector::Scalar n, IndexVector& parent, IndexVector& post)
{
    typedef typename IndexVector::Scalar StorageIndex;
    IndexVector first_kid, next_kid; // Linked list of children
    StorageIndex postnum;
    // Allocate storage for working arrays and results
    first_kid.resize(n+1);
    next_kid.setZero(n+1);
    post.setZero(n+1);

    // Set up structure describing children
    first_kid.setConstant(-1);

    for (StorageIndex v = n-1; v >= 0; v--)
    {
        StorageIndex dad = parent(v);
        next_kid(v) = first_kid(dad);
        first_kid(dad) = v;
    }

    // Depth-first search from dummy root vertex #n
    postnum = 0;
    internal::nr_etdfs(n, parent, first_kid, next_kid, post, postnum);
}

} // end namespace internal

} // end namespace Eigen

#endif // SPARSE_COLETREE_H
