// Copyright (c) 2016, the SDSL Project Authors.  All rights reserved.
// Please see the AUTHORS file for details.  Use of this source code is governed
// by a BSD license that can be found in the LICENSE file.
/*!\file cst_sada.hpp
 * \brief cst_sada.hpp contains an implementation of Sadakane's CST.
 * \author Simon Gog, Uwe Baier
 */
#ifndef INCLUDED_SDSL_CST_SADA
#define INCLUDED_SDSL_CST_SADA

#include <cassert>
#include <iostream>
#include <stddef.h>
#include <string>
#include <type_traits>

#include <sdsl/bp_support_sada.hpp>
#include <sdsl/cereal.hpp>
#include <sdsl/config.hpp>
#include <sdsl/csa_sada.hpp> // for std initialization of cst_sada
#include <sdsl/cst_iterators.hpp>
#include <sdsl/int_vector.hpp>
#include <sdsl/int_vector_buffer.hpp>
#include <sdsl/io.hpp>
#include <sdsl/lcp.hpp>
#include <sdsl/lcp_support_sada.hpp>
#include <sdsl/memory_tracking.hpp>
#include <sdsl/rank_support_v5.hpp>
#include <sdsl/sdsl_concepts.hpp>
#include <sdsl/select_support_mcl.hpp>
#include <sdsl/sorted_stack_support.hpp>
#include <sdsl/structure_tree.hpp>
#include <sdsl/suffix_tree_helper.hpp>
#include <sdsl/util.hpp>

namespace sdsl
{

//! A class for the Compressed Suffix Tree (CST) proposed by Sadakane.
/*!
 * \tparam t_csa       Type of a CSA (member of this type is accessible via
 *                     member `csa`, default class is sdsl::csa_sada).
 * \tparam t_lcp       Type of a LCP structure (member is accessible via member
 *                     `lcp`, default class is sdsl::lcp_support_sada),
 * \tparam t_bp_support Type of a BPS structure (member accessible via member
 *                      `bp_support`, default class is sdsl::bp_support_sada),
 * \tparam t_rank_10    Type of a rank structure for the 2-bit pattern `10`
 *                      (accessible via member `bp_rank_10`, default class is
 *                      sdsl::rank_support_v5)
 * \tparam t_select_10  Type of a select structure for the 2-bit pattern `10`
 *                      (accessible via member \f$bp\_select\_10\f$, default
 *                      class is sdsl::select_support_mcl).
 *
 * It also contains a sdsl::bit_vector which represents the balanced
 * parentheses sequence of the suffix tree. This bit_vector can be accessed
 * via member `bp`.
 *
 * A node `v` of the `csa_sada` is represented by an integer `i` which
 * corresponds to the position of the opening parenthesis of the parentheses
 * pair \f$(i,\mu(i))\f$ that corresponds to `v` in `bp`.
 *
 * \par Reference
 *  Kunihiko Sadakane:
 *  Compressed Suffix Trees with Full Functionality.
 *  Theory Comput. Syst. 41(4): 589-607 (2007)
 *
 * @ingroup cst
 */
template <class t_csa = csa_sada<>,
          class t_lcp = lcp_support_sada<>,
          class t_bp_support = bp_support_sada<>,
          class t_rank_10 = rank_support_v5<10, 2>,
          class t_select_10 = select_support_mcl<10, 2>>
class cst_sada
{
    static_assert(std::is_same<typename index_tag<t_csa>::type, csa_tag>::value,
                  "First template argument has to be a compressed suffix array.");

public:
    typedef cst_dfs_const_forward_iterator<cst_sada> const_iterator;
    typedef cst_bottom_up_const_forward_iterator<cst_sada> const_bottom_up_iterator;
    typedef typename t_csa::size_type size_type;
    typedef ptrdiff_t difference_type;
    typedef t_csa csa_type;
    typedef typename t_lcp::template type<cst_sada> lcp_type;
    typedef typename t_csa::char_type char_type;
    typedef typename t_csa::string_type string_type;
    typedef size_type node_type; //!< Type for the nodes  in the tree.
    typedef t_bp_support bp_support_type;
    typedef t_rank_10 rank_10_type;
    typedef t_select_10 select_10_type;

    typedef typename t_csa::alphabet_type::comp_char_type comp_char_type;
    typedef typename t_csa::alphabet_type::sigma_type sigma_type;

    typedef typename t_csa::alphabet_category alphabet_category;
    typedef cst_tag index_category;

private:
    t_csa m_csa;                  // suffix array
    lcp_type m_lcp;               // lcp information
    bit_vector m_bp;              // balanced parentheses sequence for suffix tree
    bp_support_type m_bp_support; // support for the balanced parentheses sequence
    rank_10_type m_bp_rank10;     // rank_support for leaves, i.e. "10" bit pattern
    select_10_type m_bp_select10; // select_support for leaves, i.e. "10" bit pattern

    /* Get the number of leaves that are in the subtree rooted at the first child of v +
     * number of leafs in the subtrees rooted at the children of parent(v) which precede v in the tree.
     */
    size_type inorder(node_type v) const
    {
        return m_bp_rank10(m_bp_support.find_close(v + 1) + 1);
    }

public:
    t_csa const & csa = m_csa;
    lcp_type const & lcp = m_lcp;
    bit_vector const & bp = m_bp;
    bp_support_type const & bp_support = m_bp_support;
    rank_10_type const & bp_rank_10 = m_bp_rank10;
    select_10_type const & bp_select_10 = m_bp_select10;

    //! Default constructor
    cst_sada() = default;

    //! Copy constructor
    cst_sada(cst_sada const & cst) :
        m_csa(cst.m_csa),
        m_bp(cst.m_bp),
        m_bp_support(cst.m_bp_support),
        m_bp_rank10(cst.m_bp_rank10),
        m_bp_select10(cst.m_bp_select10)
    {
        copy_lcp(m_lcp, cst.m_lcp, *this);
        m_bp_support.set_vector(&m_bp);
        m_bp_rank10.set_vector(&m_bp);
        m_bp_select10.set_vector(&m_bp);
    }

    //! Move constructor
    cst_sada(cst_sada && cst) :
        m_csa(std::move(cst.m_csa)),
        m_bp(std::move(cst.m_bp)),
        m_bp_support(std::move(cst.m_bp_support)),
        m_bp_rank10(std::move(cst.m_bp_rank10)),
        m_bp_select10(std::move(cst.m_bp_select10))
    {
        move_lcp(m_lcp, cst.m_lcp, *this);
        m_bp_support.set_vector(&m_bp);
        m_bp_rank10.set_vector(&m_bp);
        m_bp_select10.set_vector(&m_bp);
    }

    //! Construct CST from file_map
    cst_sada(cache_config & config)
    {
        {
            auto event = memory_monitor::event("bps-dfs");
            int_vector_buffer<> lcp(cache_file_name(conf::KEY_LCP, config));

            bool const o_par = true;
            bool const c_par = !o_par;

            // trim bps to maximal size of tree
            m_bp.resize(4 * lcp.size());

            if (lcp.size() > 0)
            {
                // run from back to front of lcp, enumerate intervals and count
                // opening parentheses per position i
                sorted_stack_support stack(lcp.size() + 1);
                stack.push(0); // for lcp[n+1]
                size_type p = m_bp.size() - 1;
                for (size_type i = lcp.size() - 1; i > 0; --i)
                {
                    // compute number of opening parentheses at position i
                    size_type co = 1;         // for singleton interval
                    size_type x = lcp[i] + 1; // to indicate start and end of lcp-array
                    while (stack.top() > x)
                    {
                        stack.pop();
                        ++co;
                    }
                    if (stack.top() < x)
                    {
                        stack.push(x);
                    }
                    // encode number of opening parenthesis at i as unary number
                    m_bp[p--] = o_par;
                    while (--co > 0)
                        m_bp[p--] = c_par;
                }
                // handle last value lcp[0] separate, since it virtually is a -1, but in real is a 0
                m_bp[p--] = o_par; // code last number of opening parenthesis
                while (stack.size() > 1)
                { // remove all elements except the zero from stack for next run
                    stack.pop();
                    m_bp[p--] = c_par; // move k to first bit before unary number
                }

                // run from front to back of lcp, enumerate intervals,
                // write opening parentheses and leave out closing parentheses
                size_type q = 0;
                for (size_type i = 1; i < lcp.size(); ++i)
                {
                    // compute number of opening parentheses at position i-1 using
                    // the unary coding from the last step
                    size_type co = 0;
                    do
                    {
                        ++co;
                    }
                    while (m_bp[++p] == c_par);

                    // compute number of closing parentheses at position i-1
                    size_type cc = 1; // for singleton interval
                    size_type x = lcp[i] + 1;
                    while (stack.top() > x)
                    {
                        stack.pop();
                        ++cc;
                    }
                    if (stack.top() < x)
                    {
                        stack.push(x);
                    }
                    // write sequence for position i-1
                    while (co-- > 0)
                        m_bp[q++] = o_par;
                    while (cc-- > 0)
                        m_bp[q++] = c_par;
                }
                // handle last value lcp[n+1] separate
                m_bp[q++] = o_par;
                while (!stack.empty())
                {
                    m_bp[q++] = c_par;
                    stack.pop();
                }

                // trim bps to correct size and stop
                m_bp.resize(q);
            }
        }
        {
            auto event = memory_monitor::event("bpss-dfs");

            util::init_support(m_bp_support, &m_bp);
            util::init_support(m_bp_rank10, &m_bp);
            util::init_support(m_bp_select10, &m_bp);
        }
        {
            auto event = memory_monitor::event("clcp");
            cache_config tmp_config(false, config.dir, config.id, config.file_map);
            construct_lcp(m_lcp, *this, tmp_config);
            config.file_map = tmp_config.file_map;
        }
        {
            auto event = memory_monitor::event("load csa");
            load_from_cache(m_csa, std::string(conf::KEY_CSA) + "_" + util::class_to_hash(m_csa), config);
        }
    }

    //! Number of leaves in the suffix tree.
    /*! Required for the Container Concept of the STL.
     *  \sa max_size, empty
     */
    size_type size() const
    {
        return m_csa.size();
    }

    //! Returns the maximal lenght of text for that a suffix tree can be build.
    /*! Required for the Container Concept of the STL.
     *  \sa size
     */
    static size_type max_size()
    {
        return t_csa::max_size();
    }

    //! Returns if the data strucutre is empty.
    /*! Required for the Container Concept of the STL.
     * \sa size
     */
    bool empty() const
    {
        return m_csa.empty();
    }

    //! Returns a const_iterator to the first element.
    /*! Required for the STL Container Concept.
     *  \sa end
     */
    const_iterator begin() const
    {
        if (0 == m_bp.size()) // special case: tree is uninitialized
            return end();
        return const_iterator(this, root(), false, true);
    }

    //! Returns a const_iterator to the first element of a depth first traversal of the subtree rooted at node v.
    const_iterator begin(node_type const & v) const
    {
        if (0 == m_bp.size() and root() == v)
            return end();
        return const_iterator(this, v, false, true);
    }

    //! Returns a const_iterator to the element after the last element.
    /*! Required for the STL Container Concept.
     *  \sa begin.
     */
    const_iterator end() const
    {
        return const_iterator(this, root(), true, false);
    }

    //! Returns a const_iterator to the element past the end of a depth first traversal of the subtree rooted at node v.
    const_iterator end(node_type const & v) const
    {
        if (root() == v)
            return end();
        return ++const_iterator(this, v, true, true);
    }

    //! Returns an iterator to the first element of a bottom-up traversal of the tree.
    const_bottom_up_iterator begin_bottom_up() const
    {
        if (0 == m_bp.size()) // special case: tree is uninitialized
            return end_bottom_up();
        return const_bottom_up_iterator(this, leftmost_leaf(root()));
    }

    //! Returns an iterator to the element after the last element of a bottom-up traversal of the tree.
    const_bottom_up_iterator end_bottom_up() const
    {
        return const_bottom_up_iterator(this, root(), false);
    }

    //! Assignment Operator.
    /*!
     *    Required for the Assignable Concept of the STL.
     */
    cst_sada & operator=(cst_sada const & cst)
    {
        if (this != &cst)
        {
            cst_sada tmp(cst);
            *this = std::move(tmp);
        }
        return *this;
    }

    //! Move assignment Operator.
    /*!
     *    Required for the Assignable Concept of the STL.
     */
    cst_sada & operator=(cst_sada && cst)
    {
        if (this != &cst)
        {
            m_csa = std::move(cst.m_csa);
            move_lcp(m_lcp, cst.m_lcp, *this);
            m_bp = std::move(cst.m_bp);
            m_bp_support = std::move(cst.m_bp_support);
            m_bp_support.set_vector(&m_bp);
            m_bp_rank10 = std::move(cst.m_bp_rank10);
            m_bp_rank10.set_vector(&m_bp);
            m_bp_select10 = std::move(cst.m_bp_select10);
            m_bp_select10.set_vector(&m_bp);
        }
        return *this;
    }

    //! Equality operator.
    bool operator==(cst_sada const & other) const noexcept
    {
        return (m_csa == other.m_csa) && (m_lcp == other.m_lcp) && (m_bp == other.m_bp)
            && (m_bp_support == other.m_bp_support) && (m_bp_rank10 == other.m_bp_rank10)
            && (m_bp_select10 == other.m_bp_select10);
    }

    //! Inequality operator.
    bool operator!=(cst_sada const & other) const noexcept
    {
        return !(*this == other);
    }

    //! Serialize to a stream.
    /*!\param out Outstream to write the data structure.
     *  \return The number of written bytes.
     */
    size_type serialize(std::ostream & out, structure_tree_node * v = nullptr, std::string name = "") const
    {
        structure_tree_node * child = structure_tree::add_child(v, name, util::class_name(*this));
        size_type written_bytes = 0;
        written_bytes += m_csa.serialize(out, child, "csa");
        written_bytes += m_lcp.serialize(out, child, "lcp");
        written_bytes += m_bp.serialize(out, child, "bp");
        written_bytes += m_bp_support.serialize(out, child, "bp_support");
        written_bytes += m_bp_rank10.serialize(out, child, "bp_rank_10");
        written_bytes += m_bp_select10.serialize(out, child, "bp_select_10");
        structure_tree::add_size(child, written_bytes);
        return written_bytes;
    }

    //! Load from a stream.
    /*!\param in Inputstream to load the data structure from.
     */
    void load(std::istream & in)
    {
        m_csa.load(in);
        load_lcp(m_lcp, in, *this);
        m_bp.load(in);
        m_bp_support.load(in, &m_bp);
        m_bp_rank10.load(in, &m_bp);
        m_bp_select10.load(in, &m_bp);
    }

    template <typename archive_t>
    void CEREAL_SAVE_FUNCTION_NAME(archive_t & ar) const
    {
        ar(CEREAL_NVP(m_csa));
        ar(CEREAL_NVP(m_lcp));
        ar(CEREAL_NVP(m_bp));
        ar(CEREAL_NVP(m_bp_support));
        ar(CEREAL_NVP(m_bp_rank10));
        ar(CEREAL_NVP(m_bp_select10));
    }

    template <typename archive_t>
    void CEREAL_LOAD_FUNCTION_NAME(archive_t & ar)
    {
        ar(CEREAL_NVP(m_csa));
        ar(CEREAL_NVP(m_lcp));
        set_lcp_pointer(m_lcp, *this);
        ar(CEREAL_NVP(m_bp));
        ar(CEREAL_NVP(m_bp_support));
        m_bp_support.set_vector(&m_bp);
        ar(CEREAL_NVP(m_bp_rank10));
        m_bp_rank10.set_vector(&m_bp);
        ar(CEREAL_NVP(m_bp_select10));
        m_bp_select10.set_vector(&m_bp);
    }

    /*!\defgroup cst_sada_tree_methods Tree methods of cst_sada */
    /* @{ */

    //! Return the root of the suffix tree.
    /*!
     * \par Time complexity
     *   \f$ \Order{1} \f$
     */
    node_type root() const
    {
        return 0;
    }

    //! Decide if a node is a leaf in the suffix tree.
    /*!
     * \param v A valid node of a cst_sada.
     * \returns A boolean value indicating if v is a leaf.
     * \par Time complexity
     *      \f$ \Order{1} \f$
     */
    bool is_leaf(node_type v) const
    {
        assert(m_bp[v] == 1); // assert that v is a valid node of the suffix tree
        // if there is a closing parenthesis at position v+1, the node is a leaf
        return !m_bp[v + 1];
    }

    //! Return the i-th leaf (1-based from left to right) of the suffix tree.
    /*!
     * \param i 1-based position of the leaf. \f$1\leq i\leq csa.size()\f$.
     * \return The i-th leave.
     * \par Time complexity
     *     \f$ \Order{1} \f$
     * \pre \f$ 1 \leq i \leq csa.size() \f$
     */
    node_type select_leaf(size_type i) const
    {
        assert(i > 0 and i <= m_csa.size());
        // -1 as select(i) returns the postion of the 0 of pattern 10
        return m_bp_select10.select(i) - 1;
    }

    //! Returns the depth of node v.
    /*!
     * \param v A valid node of the suffix tree.
     * \return The depth of the node.
     * \par Time complexity
     *    \f$ \Order{\lcpaccess \vee \saaccess} \f$
     */
    size_type depth(node_type v) const
    {
        if (v == root()) // if v is the root
            return 0;

        if (is_leaf(v))
        {                                 // if v is a leave
            size_type i = m_bp_rank10(v); // get the index in the suffix array
            return m_csa.size() - m_csa[i];
        }
        assert(inorder(v) > 0);
        return m_lcp[inorder(v)];
    }

    //! Returns the node depth of node v.
    /*!
     * \param v A valid node of a cst_sada.
     * \return The node depth of node v.
     * \par Time complexity
     *   \f$ \Order{1} \f$
     */
    size_type node_depth(node_type v) const
    {
        // -2 as the root() we assign depth=0 to the root
        return (m_bp_support.rank(v) << 1) - v - 2;
    }

    //! Calculate the number of leaves in the subtree rooted at node v.
    /*!\param v A valid node of the suffix tree.
     *  \return The number of leaves in the subtree rooted at node v.
     *  \par Time complexity
     *      \f$ \Order{1} \f$
     *
     *  This method is used e.g. in the count method.
     */
    size_type size(node_type v) const
    {
        size_type r = m_bp_support.find_close(v);
        return m_bp_rank10(r + 1) - m_bp_rank10(v);
    }

    //! Calculates the leftmost leaf in the subtree rooted at node v.
    /*!\param v A valid node of the suffix tree.
     *  \return The leftmost leaf in the subtree rooted at node v.
     *  \par Time complexity
     *    \f$ \Order{1} \f$
     */
    node_type leftmost_leaf(const node_type v) const
    {
        return m_bp_select10(m_bp_rank10(v) + 1) - 1;
    }

    //! Calculates the rightmost leaf in the subtree rooted at node v.
    /*!\param v A valid node of the suffix tree.
     * \return The rightmost leaf in the subtree rooted at node v.
     * \par Time complexity
     *   \f$ \Order{1} \f$
     */
    node_type rightmost_leaf(const node_type v) const
    {
        size_type r = m_bp_support.find_close(v);
        return m_bp_select10(m_bp_rank10(r + 1)) - 1;
    }

    //! Calculates the index of the leftmost leaf in the corresponding suffix array.
    /*!\param v A valid node of the suffix tree.
     * \return The index of the leftmost leaf in the corresponding suffix array.
     * \par Time complexity
     *  \f$ \Order{1} \f$
     * \par Note
     * lb is an abbreviation for ,,left bound''
     */
    size_type lb(const node_type v) const
    {
        return m_bp_rank10(v);
    }

    //! Calculates the index of the rightmost leaf in the corresponding suffix array.
    /*!\param v A valid node of the suffix tree.
     *  \return The index of the rightmost leaf in the corresponding suffix array.
     *  \par Time complexity
     *    \f$ \Order{1} \f$
     *  \par Note
     *   rb is an abbreviation for ,,right bound''
     */
    size_type rb(const node_type v) const
    {
        size_type r = m_bp_support.find_close(v);
        return m_bp_rank10(r + 1) - 1;
    }

    //! Calculate the parent node of a node v.
    /*!\param v A valid node of the suffix tree.
     *  \return The parent node of v or root() if v equals root().
     *  \par Time complexity
     *       \f$ \Order{1} \f$
     */
    node_type parent(node_type v) const
    {
        assert(m_bp[v] == 1); // assert a valid node
        if (v == root())
            return root();
        else
        {
            return m_bp_support.enclose(v);
        }
    }

    //! Return a proxy object which allows iterating over the children of a node
    /*!\param v A valid node of the suffix tree.
     *  \return The proxy object of v containing all children
     *  \par Time complexity
     *     \f$ \Order{1}\f$
     */
    cst_node_child_proxy<cst_sada> children(node_type v) const
    {
        return cst_node_child_proxy<cst_sada>(this, v);
    }

    //! Returns the next sibling of node v.
    /*!
     * \param v A valid node v of the suffix tree.
     * \return The next (right) sibling of node v or root() if v has no next (right) sibling.
     * \par Time complexity
     *    \f$ \Order{1} \f$
     */
    node_type sibling(node_type v) const
    {
        if (v == root())
            return root();
        node_type sib = m_bp_support.find_close(v) + 1;
        if (m_bp[sib])
            return sib;
        else
            return root();
    }

    //! Get the child w of node v which edge label (v,w) starts with character c.
    /*
     * \param v A valid tree node of the cst.
     * \param c First character of the edge label from v to the desired child.
     * \param char_pos Reference which will hold the position (0-based) of the matching char c in the sorted text/suffix
     * array. \return The child node w which edge label (v,w) starts with c or root() if it does not exist. \par Time
     * complexity \f$ \Order( (\saaccess+\isaaccess) \cdot \sigma + \lcpaccess) \f$ \par Note With range median mimimum
     * queries (RMMQ) one can code this operation in \f$\log \sigma \f$ time
     */
    node_type child(node_type v, const char_type c, size_type & char_pos) const
    {
        if (is_leaf(v)) // if v is a leaf = (), v has no child
            return root();
        // else v = ( (     ))
        comp_char_type cc = m_csa.char2comp[c];
        if (cc == 0 and c != 0) // TODO: aendere char2comp so ab, dass man diesen sonderfall nicht braucht
            return root();
        size_type char_ex_max_pos = m_csa.C[cc + 1], char_inc_min_pos = m_csa.C[cc];

        size_type d = depth(v); // time complexity: \lcpaccess
        size_type res = v + 1;
        while (true)
        {
            if (is_leaf(res))
            {
                char_pos = get_char_pos(m_bp_rank10(res), d, m_csa);
            }
            else
            {
                char_pos = get_char_pos(inorder(res), d, m_csa);
            }
            if (char_pos >= char_ex_max_pos) // if the current char is lex. greater than the searched char: exit
                return root();
            if (char_pos >= char_inc_min_pos) // if the current char is lex. equal with the
                return res;
            res = m_bp_support.find_close(res) + 1;
            if (!m_bp[res]) // closing parenthesis: there exists no next child
                return root();
        }
    }

    //! Get the child w of node v which edge label (v,w) starts with character c.
    // \sa child(node_type v, const char_type c, size_type &char_pos)
    node_type child(node_type v, const char_type c) const
    {
        size_type char_pos;
        return child(v, c, char_pos);
    }

    //! Get the i-th child of a node v.
    /*!
     * \param v A valid tree node of the cst.
     * \param i 1-based Index of the child which should be returned. \f$i \geq 1\f$.
     * \return The i-th child node of v or root() if v has no i-th child.
     * \par Time complexity
     *   \f$ \Order{i} \f$ for \f$  i \leq \sigma \f$
     *  \pre \f$ 1 \leq i \leq degree(v) \f$
     */
    node_type select_child(node_type v, size_type i) const
    {
        if (is_leaf(v)) // if v is a leave, v has no child
            return root();
        size_type res = v + 1;
        while (i > 1)
        {
            res = m_bp_support.find_close(res) + 1;
            if (!m_bp[res])
            { // closing parenthesis: there exists no next child
                return root();
            }
            --i;
        }
        return res;
    }

    //! Returns the d-th character (1-based indexing) of the edge-label pointing to v.
    /*!\param v The node at which the edge path ends.
     * \param d The position (1-based indexing) of the requested character on the edge path from the root to v. \f$ d >
     * 0 \wedge d <= depth(v) \f$ \return The character at position d on the edge path from the root to v. \par Time
     * complexity \f$ \Order{ \log\sigma + (\saaccess+\isaaccess) } \f$ \pre \f$ 1 \leq d \leq depth(v)  \f$
     */
    char_type edge(node_type v, size_type d) const
    {
        assert(1 <= d);
        assert(d <= depth(v));
        size_type i = 0; // index of the first suffix in the subtree of v
        if (is_leaf(v))
        {                       // if v is a leave
            i = m_bp_rank10(v); // get the index in the suffix array
        }
        else
        {
            i = inorder(v);
        }
        size_type order = get_char_pos(i, d - 1, m_csa);
        size_type c_begin = 1, c_end = ((size_type)m_csa.sigma) + 1, mid;
        while (c_begin < c_end)
        {
            mid = (c_begin + c_end) >> 1;
            if (m_csa.C[mid] <= order)
            {
                c_begin = mid + 1;
            }
            else
            {
                c_end = mid;
            }
        }
        return m_csa.comp2char[c_begin - 1];
    }

    //! Calculate the lowest common ancestor (lca) of two nodes v and w of the suffix tree.
    /*!
     * \param v The first node for which the lca with the second node should be computed.
     * \param w The second node for which the lca with the first node should be computed.
     * \return A node that is the lowest common ancestor of v and w in the suffix tree.
     * \par Time complexity
     *    \f$ \Order{\rrenclose}\   \f$
     */
    node_type lca(node_type v, node_type w) const
    {
        assert(m_bp[v] == 1 and m_bp[w] == 1);
        if (v > w)
        {
            std::swap(v, w);
        }
        else if (v == w)
        {
            return v;
        }
        if (v == root())
            return root();
        return m_bp_support.double_enclose(v, w);
    }

    //! Compute the suffix link of node v.
    /*!
     * \param v A valid node of a cst_sada.
     * \return The suffix link of node v.
     * \par Time complexity
     *   \f$ \Order{ 1 } \f$
     */
    node_type sl(node_type v) const
    {
        if (v == root())
            return root();
        // get leftmost leaf in the tree rooted at v
        size_type left = m_bp_rank10(v);
        if (is_leaf(v))
        {
            return select_leaf(m_csa.psi[left] + 1);
        }
        // get the rightmost leaf in the tree rooted at v
        size_type right = m_bp_rank10(m_bp_support.find_close(v)) - 1;
        assert(left < right);
        node_type left_leaf = select_leaf(m_csa.psi[left] + 1);
        node_type right_leaf = select_leaf(m_csa.psi[right] + 1);
        return lca(left_leaf, right_leaf);
    }

    //! Compute the suffix link of node v applied a number of times consecutively.
    /*!
     * \param v A valid node of a cst_sada
     * \return The suffix link ^ i of node v.
     * \par Time complexity
     *   \f$ \Order{ i } \f$
     */
    node_type sl(node_type v, size_type i) const
    {
        if (v == root())
            return root();
        // get leftmost leaf in the tree rooted at v
        size_type left = m_bp_rank10(v);
        if (is_leaf(v))
        {
            return select_leaf(get_char_pos(left, i, m_csa) + 1);
        }
        // get the rightmost leaf in the tree rooted at v
        size_type right = m_bp_rank10(m_bp_support.find_close(v)) - 1;
        assert(left < right);
        node_type left_leaf = select_leaf(get_char_pos(left, i, m_csa) + 1);
        node_type right_leaf = select_leaf(get_char_pos(right, i, m_csa) + 1);
        return lca(left_leaf, right_leaf);
    }

    //! Compute the Weiner link of node v and character c.
    /*
     * \param v A valid node of a cst_sada.
     * \param c The character which should be prepended to the string of the current node.
     *   \return root() if the Weiner link of (v, c) does not exist, otherwise the Weiner link is returned.
     * \par Time complexity
     *    \f$ \Order{ t_{rank\_bwt} + t_{lca}}\f$
     */
    node_type wl(node_type v, const char_type c) const
    {
        // get leftmost leaf in the tree rooted at v
        size_type left = m_bp_rank10(v);
        // get the rightmost leaf in the tree rooted at v
        size_type right = is_leaf(v) ? left : m_bp_rank10(m_bp_support.find_close(v)) - 1;

        size_type c_left = m_csa.bwt.rank(left, c);
        size_type c_right = m_csa.bwt.rank(right + 1, c);

        if (c_left == c_right) // there exists no Weiner link
            return root();
        if (c_left + 1 == c_right)
            return select_leaf(m_csa.C[m_csa.char2comp[c]] + c_left + 1);
        else
        {
            size_type left = m_csa.C[m_csa.char2comp[c]] + c_left;
            size_type right = m_csa.C[m_csa.char2comp[c]] + c_right - 1;
            assert(left < right);
            node_type left_leaf = select_leaf(left + 1);
            node_type right_leaf = select_leaf(right + 1);
            return lca(left_leaf, right_leaf);
        }
    }

    //! Compute the suffix number of a leaf node v.
    /*!\param v A valid leaf node of a cst_sada.
     * \return The suffix array value corresponding to the leaf node v.
     * \par Time complexity
     *   \f$ \Order{ \saaccess } \f$
     */
    size_type sn(node_type v) const
    {
        assert(is_leaf(v));
        // count the leaves left to leaf v
        return m_csa[m_bp_rank10(v)];
    }

    //! Computes a unique identification number for a node of the suffix tree in the range [0..nodes()-1]
    /*!
     *\param v A valid node of a cst_sada.
     * \return A unique identification number for the node v in the range [0..nodes()-1]
     * \par Time complexity
     *     \f$ \Order{1} \f$
     * \sa inv_id(size_type id)
     */
    size_type id(node_type v) const
    {
        // v+1 is < m_bp.size(), as v is the position of an open parenthesis
        if (m_bp[v + 1])
        { // case (a) inner node
            return size() + (m_bp_support.rank(v) - 1) - m_bp_rank10(v);
        }
        else
        { // case (b) leaf
            return m_bp_rank10(v);
        }
    }

    //! Computes the node for such that id(v)=id.
    /*!
     * \param id An id in the range [0..nodes()-1].
     * \return A node v of the CST such that id(v)=id.
     * \par Time complexity
     *        \f$ \Order{1} \f$ for leaves and \f$ \log n \f$ for inner nodes
     * \sa id(node_type v)
     */
    size_type inv_id(size_type id)
    {
        if (id < size())
        { // the corresponding node is a leaf
            return select_leaf(id + 1);
        }
        else
        { // the corresponding node is a inner node
            id = id + 1 - size();
            // solved by binary search; TODO: can be done in constant time by using a select structure on the bitpattern
            // 11
            size_type lb = 0, rb = m_bp.size(); // lb inclusive, rb exclusive
            // invariant: arg(lb) < id, arg(rb)>= id
            while (rb - lb > 1)
            {
                size_type mid = lb + (rb - lb) / 2; // mid \in [0..m_bp.size()-1]
                if (m_bp[mid] == 0 and m_bp[mid - 1] == 1)
                {          // if we are ``half on a leaf''
                    ++mid; // we step one to the right to include it
                }
                // get the number of open inner nodes before position mid, i.e. arg(mid)
                size_type mid_id = m_bp_support.rank(mid - 1)
                                 - m_bp_rank10(mid); // Note: mid-1 is valid of mid is of type ``size_type'' as us the
                                                     // parameter of rank
                if (mid_id < id)
                {
                    lb = mid;
                }
                else
                { // mid_id >= x
                    rb = mid;
                }
            }
            return lb;
        }
    }

    //! Get the number of nodes of the suffix tree.
    /*
     *  \return The number of nodes of the suffix tree.
     *  \par Time complexity
     *    \f$ \Order{1} \f$
     */
    size_type nodes() const
    {
        return m_bp.size() >> 1;
    }

    //! Get the node in the suffix tree which corresponds to the lcp-interval [lb..rb]
    /* \param lb Left bound of the lcp-interval [lb..rb] (inclusive).
     * \param rb Right bound of the lcp-interval [lb..rb] (inclusive).
     *\ return The node in the suffix tree corresponding lcp-interval [lb..rb]
     */
    node_type node(size_type lb, size_type rb) const
    {
        return lca(select_leaf(lb + 1), select_leaf(rb + 1));
    }

    //! Get the number of children of a node v.
    /*!
     *  \param v A valid node v of a cst_sada.
     *  \returns The number of children of node v.
     *  \par Time complexity
     *       \f$ \Order{\sigma} \f$
     */
    size_type degree(node_type v) const
    {
        size_type res = 0;
        v = v + 1;
        while (m_bp[v])
        { // found open parentheses
            ++res;
            v = m_bp_support.find_close(v) + 1;
        }
        return res;
    }

    //! Maps an index i to the position in TLCP where LCP[i] can be found
    /*!
     * \param i The index in the LCP array
     * \return The corresponding position in the TLCP array
     */
    size_type tlcp_idx(size_type i) const
    {
        size_type ii = 0;
        if (i > 0)
        {
            size_type ipos = m_bp_select10(i) - 1;       // -1 as select returns the position of the zero
            size_type ip1pos = m_bp_select10(i + 1) - 1; // "  "    "        "    "     "     "   "   "
            ii = m_bp_support.double_enclose(ipos, ip1pos);
        }
        ii = m_bp_support.find_close(ii);
        // all right, as bp[ii] = 0
        return ii - m_bp_support.rank(ii) - m_bp_rank10(ii);
    }
    /* @} */
};

} // end namespace sdsl
#endif
