// 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_sct3.hpp
    \brief cst_sct3.hpp contains an implementation of the interval based CST.
    \author Simon Gog
*/
#ifndef INCLUDED_SDSL_CST_SCT3
#define INCLUDED_SDSL_CST_SCT3

#include "int_vector.hpp"
#include "iterators.hpp"
#include "lcp.hpp"
#include "bp_support.hpp"
#include "csa_wt.hpp" // for std initialization of cst_sct3
#include "cst_iterators.hpp"
#include "rank_support.hpp"
#include "select_support.hpp"
#include "util.hpp"
#include "sdsl_concepts.hpp"
#include "construct.hpp"
#include "suffix_tree_helper.hpp"
#include "suffix_tree_algorithm.hpp"
#include <iostream>
#include <algorithm>
#include <cassert>
#include <cstring> // for strlen
#include <iomanip>
#include <iterator>
#include <stack> // for the calculation of the balanced parentheses sequence
#include <ostream>
#include <type_traits>

namespace sdsl {

// Declaration of the CST's node type
template <class t_int = int_vector<>::size_type>
struct bp_interval;

//! A class for the Compressed Suffix Tree (CST) proposed by Ohlebusch and Gog.
/*!
 * \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       Type of rank structure which supports the bitvector
 *                      which indicates the leftmost child of the nodes.
 *
 * It also contains a sdsl::bit_vector which represents the BP sequence of the
 * Super-Cartesian tree of the LCP array. This bitvector can be accessed via
 * the member `bp`. Another sdsl::bit_vector stores information, if a node is
 * the leftmost child of another node. This bitvector can be access via the
 * member first_child_bv and takes n bits.
 *
 * A node \f$v\f$ of the csa_sct is represented by an sdsl::bp_interval. The
 * size of the sdsl::cst_sct3 is smaller than the size of a sdsl::cst_sada
 * since the tree topology needs only \f$2n+n=3n\f$ bits in contrast to the
 * \f$4n\f$ bits in sdsl::cst_sada.
 *
 * \par Reference
 * Enno Ohlebusch, Johannes Fischer, Simon Gog:
 * CST++.
 * SPIRE 2010: 322-333
 *
 * \par Applications of the CST
 * The compressed suffix tree could be used for string matching and many other
 * application in sequence analysis. 17 applications are in the book
 * "Algorithms on Strings, Trees, and Sequences" of Dan Gusfield.
 *
 * @ingroup cst
 */
template <class t_csa		 = csa_wt<>,
		  class t_lcp		 = lcp_dac<>,
		  class t_bp_support = bp_support_sada<>,
		  class t_bv		 = bit_vector,
		  class t_rank		 = typename std::conditional<std::is_same<t_bv, bit_vector>::value,
												   rank_support_v5<>,
												   typename t_bv::rank_1_type>::type,
		  class t_sel = typename std::conditional<
		  std::is_same<t_bv, bit_vector>::value and
		  std::is_same<typename t_csa::alphabet_category, byte_alphabet_tag>::value,
		  select_support_scan<>,
		  typename t_bv::select_1_type>::type>
class cst_sct3 {
	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_sct3>	   const_iterator;
	typedef cst_bottom_up_const_forward_iterator<cst_sct3> 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_sct3>		   lcp_type;
	typedef t_bp_support								   bp_support_type;
	typedef typename t_csa::char_type					   char_type;
	typedef typename t_csa::string_type					   string_type;
	typedef bp_interval<size_type> node_type; //!< Type for the nodes in the tree
	typedef t_bv				   bv_type;
	typedef t_rank				   rank_type;
	typedef t_sel				   sel_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:
	csa_type		m_csa;
	lcp_type		m_lcp;
	bit_vector		m_bp;
	bp_support_type m_bp_support;
	bv_type			m_first_child;
	rank_type		m_first_child_rank;
	sel_type		m_first_child_select;
	size_type		m_nodes;

	// Get the first l index of a [i,j] interval.
	/* I.e. given an interval [i,j], the function returns the position of
         * the smallest entry lcp[k] with \f$ i<k\leq j \f$
         * \par Time complexity
         *      \f$ \Order{1} \f$
         */
	inline size_type first_l_index(const node_type& node, size_type& kpos, size_type& ckpos) const
	{
		if (node.cipos > node.jp1pos) { // corresponds to m_lcp[i] <= m_lcp[j+1]
			ckpos = node.jp1pos - 1;
		} else { // corresponds to m_lcp[i] > m_lcp[j+1]
			ckpos = node.cipos - 1;
		}
		assert(m_bp[ckpos] == 0);
		kpos = m_bp_support.find_open(ckpos);
		return m_bp_support.rank(kpos) - 1;
	}

	// Get the i-th l-index of a node
	// if there exists no ith l-index return node.j+1
	/* \param v Node
         * \param i l-index in [1..degree()]
         * \paran
         */
	size_type
	select_l_index(const node_type& v, size_type i, size_type& kpos, size_type& ckpos) const
	{
		assert(i > 0);
		if (v.cipos > v.jp1pos) { // corresponds to m_lcp[i] <= m_lcp[j+1]
			ckpos = v.jp1pos - 1;
		} else { // corresponds to m_lcp[i] > m_lcp[j+1]
			ckpos = v.cipos - 1;
		}
		assert(m_bp[ckpos] ==
			   0); // at least the first l-index should be present, i.e. node is not leaf
		if (1 == i) {
			kpos = m_bp_support.find_open(ckpos);
			return m_bp_support.rank(kpos) - 1;
		} else { // i > 1
			// numbers of closing parentheses - 1 = index of first child in m_first_child
			size_type r = ckpos - m_bp_support.rank(ckpos);
			if (r + 1 >= i) { // if there exist more than i l-indices
				// check if m_first_child[r-i+1..r-1] consists of zeros
				if (i < degree(v)) { // there exists an i-th l-index
					ckpos -= (i - 1);
					assert(m_bp[ckpos] == 0);
					kpos = m_bp_support.find_open(ckpos);
					return m_bp_support.rank(kpos) - 1;
				}
			}
			// if i >= degree(node)
			kpos = v.jp1pos;
			if (kpos < m_bp.size())
				ckpos = m_bp_support.find_close(kpos);
			else
				ckpos = m_bp.size();
			return v.j + 1;
		}
	}

	// Position of the first l-index of a l-[i,j] interval in the BP.
	/* \par Time complexity
         *   \f$ \Order{1} \f$
         */
	inline size_type closing_pos_of_first_l_index(const node_type& node) const
	{
		if (node.cipos > node.jp1pos) { // corresponds to m_lcp[i] <= m_lcp[j+1]
			return node.jp1pos - 1;
		} else { // corresponds to m_lcp[i] > m_lcp[j+1]
			return node.cipos - 1;
		}
	}

	// Get the next smaller value.
	/*
         * \param i    Position in the original vector.
         * \param ipos Position of the corresponding opening parenthesis in BP.
         * \return Position of the next smaller value in [i+1..n-1], and n when
         *         no such value exists.
         * \par Time complexity
         *      \f$ \Order{1} \f$
         */
	// possible optimization: calculate also position of nsv,
	// i.e. next ( following position cipos
	inline size_type nsv(SDSL_UNUSED size_type i, size_type ipos) const
	{
		size_type cipos  = m_bp_support.find_close(ipos);
		size_type result = m_bp_support.rank(cipos);
		return result;
	}

	// Get the previous smaller value.
	/*
         * \param i      Position in the original vector.
         * \param ipos   Corresponding opening parenthesis in m_bp
         * \param cipos  Corresponding closing parenthesis to ipos
         * \par Time complexity
         *    \f$ \Order{\frac{\sigma}{w}} \f$, where w=64 is the word size,
         *    can be implemented in \f$\Order{1}\f$ with rank and select.
         */
	inline size_type psv(SDSL_UNUSED size_type i,
						 size_type			   ipos,
						 size_type			   cipos,
						 size_type&			   psvpos,
						 size_type&			   psvcpos) const
	{
		// if lcp[i]==0 => psv is the 0-th index by definition
		if ((cipos + (size_type)m_csa.sigma) >= m_bp.size()) {
			psvpos  = 0;
			psvcpos = m_bp.size() - 1;
			return 0;
		}
		if (m_bp[cipos + 1]) {
			psvpos  = m_bp_support.enclose(ipos);
			psvcpos = m_bp_support.find_close(psvpos);
			return m_bp_support.rank(psvpos) - 1;
		}
		// r0 = index of clothing parenthesis in m_first_child
		size_type		r0				 = cipos - m_bp_support.rank(cipos);
		size_type		next_first_child = 0;
		const uint64_t* p				 = m_first_child.data() + (r0 >> 6);
		uint64_t		w				 = (*p) >> (r0 & 0x3F);
		if (w) { // if w!=0
			next_first_child = cipos + bits::lo(w);
			if (cipos == next_first_child and m_bp[next_first_child + 1]) {
				psvpos  = m_bp_support.enclose(ipos);
				psvcpos = m_bp_support.find_close(psvpos);
				return m_bp_support.rank(psvpos) - 1;
			}
		} else {
			size_type delta = 63 - (r0 & 0x3F);
			++p;
			int steps = 4;
			while (!(w = *p) and steps-- > 0) { // while w==0
				++p;
				delta += 64;
			}
			if (w != 0) {
				delta += bits::lo(w) + 1;
			} else {
				auto pos = m_first_child_select(m_first_child_rank(r0 + 1) + 1);
				delta	= pos - r0;
			}
			next_first_child = cipos + delta;
		}
		if (!m_bp[next_first_child + 1]) { // if next parenthesis is a closing one
			psvcpos = next_first_child + 1;
			psvpos  = m_bp_support.find_open(psvcpos);
			return m_bp_support.rank(psvpos) - 1;
		} else {
			psvpos  = m_bp_support.enclose(m_bp_support.find_open(next_first_child));
			psvcpos = m_bp_support.find_close(psvpos);
			return m_bp_support.rank(psvpos) - 1;
		}
	}

	// Range minimum query based on the rr_enclose method.
	/* \par Time complexity
         *   \f$ \Order{\rrenclose} \f$
         */
	inline size_type rmq(size_type l, size_type r) const
	{
		size_type i	= m_bp_support.select(l + 1);
		size_type j	= m_bp_support.select(r + 1);
		size_type fc_i = m_bp_support.find_close(i);
		if (j < fc_i) { // i < j < find_close(j) < find_close(i)
			return l;
		} else { // i < find_close(i) < j < find_close(j)
			size_type ec = m_bp_support.rr_enclose(i, j);
			if (ec == m_bp_support.size()) { // no restricted enclosing pair found
				return r;
			} else {							  // found range restricted enclosing pair
				return m_bp_support.rank(ec) - 1; // subtract 1, as the index is 0 based
			}
		}
	}

public:
	const csa_type&		   csa		  = m_csa;
	const lcp_type&		   lcp		  = m_lcp;
	const bit_vector&	  bp		  = m_bp;
	const bp_support_type& bp_support = m_bp_support;

	const bv_type&   first_child_bv		= m_first_child;
	const rank_type& first_child_rank   = m_first_child_rank;
	const sel_type&  first_child_select = m_first_child_select;

	/*! \defgroup cst_sct3_constructors Constructors of cst_sct3 */
	/* @{ */

	//! Default constructor
	cst_sct3() = default;

	//! Construct CST from cache config
	cst_sct3(cache_config& cache, bool build_only_bps = false);

	//! Copy constructor
	/*!
         *  \param cst The cst_sct3 which should be copied.
         *  \par Time complexity
         *       \f$ \Order{n} \f$, where \f$n=\f$cst_sct3.size()
         */
	cst_sct3(const cst_sct3& cst)
		: m_csa(cst.m_csa)
		, m_bp(cst.m_bp)
		, m_bp_support(cst.m_bp_support)
		, m_first_child(cst.m_first_child)
		, m_first_child_rank(cst.m_first_child_rank)
		, m_first_child_select(cst.m_first_child_select)
		, m_nodes(cst.m_nodes)
	{
		copy_lcp(m_lcp, cst.m_lcp, *this);
		m_bp_support.set_vector(&m_bp);
		m_first_child_rank.set_vector(&m_first_child);
		m_first_child_select.set_vector(&m_first_child);
	}

	//! Move constructor
	/*!
         *  \param cst The cst_sct3 which should be moved.
         */
	cst_sct3(cst_sct3&& 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_first_child(std::move(cst.m_first_child))
		, m_first_child_rank(std::move(cst.m_first_child_rank))
		, m_first_child_select(std::move(cst.m_first_child_select))
		, m_nodes(cst.m_nodes)
	{
		move_lcp(m_lcp, cst.m_lcp, *this);
		m_bp_support.set_vector(&m_bp);
		m_first_child_rank.set_vector(&m_first_child);
		m_first_child_select.set_vector(&m_first_child);
	}

	/* @} */

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

	//! Returns the largest size that cst_sct3 can ever have.
	/*! Required for the Container Concept of the STL.
         *  \sa size
         */
	static size_type max_size() { return t_csa::max_size(); }

	//! Returns if the data structure 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 of a depth first traversal of the tree.
	/*! 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(const node_type& 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 of a depth first traversal of the tree.
	/*! 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(const node_type& 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_sct3& operator=(const cst_sct3& cst)
	{
		if (this != &cst) {
			cst_sct3 tmp(cst);
			*this = std::move(tmp);
		}
		return *this;
	}

	//! Assignment Move Operator.
	/*!
         *    Required for the Assignable Concept of the STL.
         */
	cst_sct3& operator=(cst_sct3&& 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_first_child	  = std::move(cst.m_first_child);
			m_first_child_rank = std::move(cst.m_first_child_rank);
			m_first_child_rank.set_vector(&m_first_child);
			m_first_child_select = std::move(cst.m_first_child_select);
			m_first_child_select.set_vector(&m_first_child);
			m_nodes = std::move(cst.m_nodes);
		}
		return *this;
	}


	//! 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;

	//! Load from a stream.
	/*! \param in Inputstream to load the data structure from.
         */
	void load(std::istream& in);

	//!\brief Serialise (save) via cereal
	template <typename archive_t>
	void CEREAL_SAVE_FUNCTION_NAME(archive_t & ar) const;

	//!\brief Serialise (load) via cereal
	template <typename archive_t>
	void CEREAL_LOAD_FUNCTION_NAME(archive_t & ar);

	//! Equality operator.
	bool operator==(cst_sct3 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_first_child == other.m_first_child) && (m_first_child_rank == other.m_first_child_rank) &&
		       (m_first_child_select == other.m_first_child_select) /*&& (m_nodes == other.m_nodes)*/;
	}

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

	/*! \defgroup cst_sct3_tree_methods Tree methods of cst_sct3 */
	/* @{ */

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

	//! Decide if a node is a leaf.
	/*!
         * \param v A valid node.
         * \returns A boolean value indicating if v is a leaf.
         * \par Time complexity
         *      \f$ \Order{1} \f$
         */
	bool is_leaf(const node_type& v) const { return v.i == v.j; }

	//! Return the i-th leaf (1-based from left to right).
	/*!
         * \param i 1-based position of the leaf.
         * \return The i-th leave.
         * \par Time complexity
         *      \f$ \Order{1} \f$
         * \pre \f$ 1 \leq i \leq size() \f$
         */
	node_type select_leaf(size_type i) const
	{
		assert(i > 0 and i <= size());
		size_type ipos = m_bp_support.select(i);
		size_type jp1pos;
		if (i == size())
			jp1pos = m_bp.size();
		else if (m_bp[ipos + 1])
			jp1pos = ipos + 1;
		else
			jp1pos = m_bp_support.select(i + 1);
		return node_type(i - 1, i - 1, ipos, m_bp_support.find_close(ipos), jp1pos);
	}

	//! 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$
         */
	size_type size(const node_type& v) const { return v.j - v.i + 1; }

	//! 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 select_leaf(v.i + 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 { return select_leaf(v.j + 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 v.i; }

	//! 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 { return v.j; }

	//! Calculate the parent node of a node v.
	/*! \param v A valid node of the suffix tree.
         *  \return The parent node of v or the root if v==root().
         *  \par Time complexity
         *     \f$ \Order{1}\f$
         */
	node_type parent(const node_type& v) const
	{
		if (v.cipos > v.jp1pos) { // LCP[i] <= LCP[j+1]
			size_type psv_pos, psv_cpos, psv_v, nsv_v, nsv_p1pos;
			psv_v = psv(v.j + 1, v.jp1pos, m_bp_support.find_close(v.jp1pos), psv_pos, psv_cpos);
			nsv_v = nsv(v.j + 1, v.jp1pos) - 1;
			if (nsv_v == size() - 1) {
				nsv_p1pos = m_bp.size();
			} else { // nsv_v < size()-1
				nsv_p1pos = m_bp_support.select(nsv_v + 2);
			}
			return node_type(psv_v, nsv_v, psv_pos, psv_cpos, nsv_p1pos);
		} else { // LCP[i] > LCP[j+1]
			size_type psv_pos, psv_cpos, psv_v;
			psv_v = psv(v.i, v.ipos, v.cipos, psv_pos, psv_cpos);
			return node_type(psv_v, v.j, psv_pos, psv_cpos, v.jp1pos);
		}
	}

	//! 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_sct3> children(const node_type& v) const
	{
		return cst_node_child_proxy<cst_sct3>(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(const node_type& v) const
	{
		//Procedure:(1) Determine, if v has a right sibling.
		if (
		v.cipos <
		v
		.jp1pos) { // LCP[i] > LCP[j+1] => v has the same right border as parent(v) => no right sibling
			return root();
		}
		//          (2)    There exists a right sibling, LCP[j+1] >= LCP[i] and j>i
		// Now it holds:  v.cipos > v.jp1pos
		size_type cjp1posm1 = m_bp_support.find_close(v.jp1pos) - 1; // v.cipos-2 ???
		// m_bp[cjp1posm1] equals 1 =>  v is the last child
		bool last_child = m_bp[cjp1posm1];
		// otherwise if m_bp[cjp1posm1] equals 0 => we don't know if it is the last child
		if (!last_child) {
			size_type first_child_idx = cjp1posm1 - m_bp_support.rank(cjp1posm1);
			last_child				  = m_first_child
			[first_child_idx]; // if first_child indicator is true => the new sibling is the rightmost sibling
		}
		if (last_child) {
			size_type nsv_v = nsv(v.j + 1, v.jp1pos) - 1, nsv_p1pos;
			if (nsv_v == size() - 1) {
				nsv_p1pos = m_bp.size();
			} else {
				nsv_p1pos = m_bp_support.select(nsv_v + 2);
			}
			return node_type(
			v.j + 1, nsv_v, v.jp1pos, m_bp_support.find_close(v.jp1pos), nsv_p1pos);
		} else {
			size_type new_j = m_bp_support.rank(m_bp_support.find_open(cjp1posm1)) - 2;
			return node_type(v.j + 1,
							 new_j,
							 v.jp1pos,
							 m_bp_support.find_close(v.jp1pos),
							 m_bp_support.select(new_j + 2));
		}
	}

	//! 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.
         * \return The i-th child node of v or root() if v has no i-th child.
         * \par Time complexity
         * \f$ \Order{\frac{\sigma}{w}} \f$, where w=64 is the word size,
         * can be implemented in \f$\Order{1}\f$ with rank and select.
         * \pre \f$ 1 \leq i \leq degree(v) \f$
         */

	node_type select_child(const node_type& v, size_type i) const
	{
		assert(i > 0);
		if (is_leaf(v)) // if v is a leave, v has no child
			return root();
		if (1 == i) {
			size_type k = 0, kpos = 0, k_find_close = 0;
			// v is not a leave: v has at least two children
			k =
			select_l_index(v, 1, kpos, k_find_close); // get first l-index k and the position of k
			return node_type(v.i, k - 1, v.ipos, v.cipos, kpos);
		} else { // i > 1
			size_type k1, kpos1, k_find_close1;
			k1 = select_l_index(v, i - 1, kpos1, k_find_close1);
			if (k1 == v.j + 1) return root();
			size_type k2, kpos2, k_find_close2;
			k2 = select_l_index(v, i, kpos2, k_find_close2);
			return node_type(k1, k2 - 1, kpos1, k_find_close1, kpos2);
		}
	}

	//! Get the number of children of a node v.
	/*!
         * \param v A valid node v.
         * \returns The number of children of node v.
         *  \par Time complexity
         *    \f$ \Order{\frac{\sigma}{w}} \f$, where w=64 is the word size,
         *    can be implemented in \f$\Order{1}\f$ with rank and select.
         */
	size_type degree(const node_type& v) const
	{
		if (is_leaf(v)) // if v is a leave, v has no child
			return 0;
		// v is not a leave: v has at least two children
		size_type		r	  = closing_pos_of_first_l_index(v);
		size_type		r0	 = r - m_bp_support.rank(r);
		const uint64_t* p	  = m_first_child.data() + (r0 >> 6);
		uint8_t			offset = r0 & 0x3F;

		uint64_t w = (*p) & bits::lo_set[offset];
		if (w) { // if there is a bit set in the current word
			return offset - bits::hi(w) + 1;
		} else if (m_first_child.data() == p) { // no bit set and we are in the first word
			return offset +
				   2; // since would have to be bits::hi(w)=-1, child marked in previous word
		} else {
			size_type res   = offset + 2;
			int		  steps = 4;
			// search in previous four words for result
			while (p > m_first_child.data() and steps-- > 0) {
				w = *(--p);
				if (0 == w)
					res += 64;
				else {
					return res + (63 - bits::hi(w));
				}
			}
			// if not found: use rank + select to answer query
			auto goal_rank = m_first_child_rank(r0);
			if (goal_rank == 0) {
				return r0 + 2;
			} else {
				return r0 - m_first_child_select(goal_rank) + 1;
			}
		}
	}

	//! 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 on the edge label.
         * \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 \log\sigma + \lcpaccess} \f$
         */
	node_type child(const 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: change char2comp so that we don't need this special case
			return root();
		size_type char_ex_max_pos = m_csa.C[((size_type)1) + cc], char_inc_min_pos = m_csa.C[cc];

		size_type d = depth(v);

		//            (1) check the first child
		char_pos = get_char_pos(v.i, d, m_csa);
		if (
		char_pos >=
		char_ex_max_pos) { // the first character of the first child interval is lex. greater than c
			// => all other first characters of the child intervals are also greater than c => no solution
			return root();
		} else if (
		char_pos >=
		char_inc_min_pos) { // i.e. char_pos < char_ex_max_pos and char_pos >= char_inc_min_pos
			return select_child(v, 1);
		}

		size_type child_cnt = degree(v);

		//            (2) check the last child
		char_pos = get_char_pos(v.j, d, m_csa);
		if (
		char_pos <
		char_inc_min_pos) { // the first character of the last child interval is lex. smaller than c
			// =>    all other first characters of the child intervals are also smaller than c => no solution
			return root();
		} else if (
		char_pos <
		char_ex_max_pos) { // i.e. char_pos < char_ex_max_pos and char_pos >= char_inc_min_pos
			return select_child(v, child_cnt);
		}

		//             (3) binary search for c in the children [2..children)
		size_type l_bound = 2, r_bound = child_cnt, mid, kpos, ckpos, l_index;
		while (l_bound < r_bound) {
			mid = (l_bound + r_bound) >> 1;

			l_index  = select_l_index(v, mid - 1, kpos, ckpos);
			char_pos = get_char_pos(l_index, d, m_csa);

			if (char_inc_min_pos > char_pos) {
				l_bound = mid + 1;
			} else if (char_ex_max_pos <= char_pos) {
				r_bound = mid;
			} else { // char_inc_min_pos <= char_pos < char_ex_max_pos => found child
				// we know that the child is not the last child, see (2)
				// find next l_index: we know that a new l_index exists: i.e. assert( 0 == m_bp[ckpos-1]);
				size_type lp1_index = m_bp_support.rank(m_bp_support.find_open(ckpos - 1)) - 1;
				size_type jp1pos	= m_bp.size();
				if (lp1_index - 1 < size() - 1) {
					jp1pos = m_bp_support.select(lp1_index + 1);
				}
				return node_type(l_index, lp1_index - 1, kpos, ckpos, jp1pos);
			}
		}
		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(const node_type& v, const char_type c) const
	{
		size_type char_pos;
		return child(v, c, char_pos);
	}

	//! 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) 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(const node_type& v, size_type d) const
	{
		assert(1 <= d);
		assert(d <= depth(v));
		size_type order   = get_char_pos(v.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 LCA of two nodes `v` and `w`
	/*!
         * \param v The first node.
         * \param w The second node.
         * \return The lowest common ancestor of v and w.
         * \par Time complexity
         *   \f$ \Order{\rrenclose}\   \f$
         */

	node_type lca(node_type v, node_type w) const
	{
		if (v.i > w.i or (v.i == w.i and v.j < w.j)) {
			std::swap(v, w);
		}
		if (v.j >= w.j) { // v encloses w or v==w
			return v;
		} else { // v.i < v.j < w.i < w.j
			size_type min_index		 = rmq(v.i + 1, w.j);
			size_type min_index_pos  = m_bp_support.select(min_index + 1);
			size_type min_index_cpos = m_bp_support.find_close(min_index_pos);

			if (min_index_cpos >=
				(m_bp.size() - m_csa.sigma)) { // if lcp[min_index]==0 => return root
				return root();
			}
			size_type new_j = nsv(min_index, min_index_pos) - 1;
			size_type new_ipos, new_icpos;
			size_type new_i  = psv(min_index, min_index_pos, min_index_cpos, new_ipos, new_icpos);
			size_type jp1pos = m_bp.size();
			if (new_j < size() - 1) {
				jp1pos = m_bp_support.select(new_j + 2);
			}
			return node_type(new_i, new_j, new_ipos, new_icpos, jp1pos);
		}
	}

	//! Returns the string depth of node v.
	/*!
         * \param v A valid node of a cst_sct3.
         * \return The string depth of node v.
         * \par Time complexity
         *  \f$ \Order{1} \f$ for non-leaves and \f$\Order{t_{SA}}\f$ for leaves
         */
	size_type depth(const node_type& v) const
	{
		if (v == root()) {
			return 0;
		}
		else if (v.i == v.j) {
			return size() - m_csa[v.i];
		} else {
			size_type kpos, ckpos;
			size_type l = select_l_index(v, 1, kpos, ckpos);
			return m_lcp[l];
		}
	}

	//! Returns the node depth of node v
	/*!
         * \param v A valid node of a cst_sct3.
         * \return The node depth of node v.
         * \par Time complexity
         *   \f$ \Order{z} \f$, where \f$z\f$ is the resulting node depth.
         * \par Note
         * Can be implemented in O(1) with o(n) space. See
         * Jansson, Sadakane, Sung:
         * Ultra-succinct Representation of Ordered Trees
         * SODA 2007
         */
	size_type node_depth(node_type v) const
	{
		size_type d = 0;
		while (v != root()) {
			++d;
			v = parent(v);
		}
		return d;
	}

	//! Compute the suffix link of node v.
	/*!
         * \param v A valid node of a cst_sct3.
         * \return The suffix link of node v.
         * \par Time complexity
         *      \f$ \Order{ \rrenclose } \f$
         */
	node_type sl(const node_type& v) const
	{
		if (v == root()) return root();
		// get interval with first char deleted
		size_type i = m_csa.psi[v.i];
		if (is_leaf(v)) {
			if (v.i == 0 and v.j == 0) // if( v.l==1 )
				return root();
			else
				return select_leaf(i + 1);
		}
		size_type j = m_csa.psi[v.j];
		assert(i < j);
		size_type min_index		 = rmq(i + 1, j); // rmq
		size_type min_index_pos  = m_bp_support.select(min_index + 1);
		size_type min_index_cpos = m_bp_support.find_close(min_index_pos);
		if (min_index_cpos >= (m_bp.size() - m_csa.sigma)) { // if lcp[min_index]==0 => return root
			return root();
		}
		size_type new_j = nsv(min_index, min_index_pos) - 1;
		size_type new_ipos, new_icpos;
		size_type new_i  = psv(min_index, min_index_pos, min_index_cpos, new_ipos, new_icpos);
		size_type jp1pos = m_bp.size();
		if (new_j < size() - 1) {
			jp1pos = m_bp_support.select(new_j + 2);
		}
		return node_type(new_i, new_j, new_ipos, new_icpos, jp1pos);
	}


	//! Compute the Weiner link of node v and character c.
	/*!
         * \param v A valid node of a cst_sct3.
         * \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} } \f$
         */
	node_type wl(const node_type& v, const char_type c) const
	{
		size_type c_left  = m_csa.bwt.rank(v.i, c);
		size_type c_right = m_csa.bwt.rank(v.j + 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);

			size_type ipos   = m_bp_support.select(left + 1);
			size_type jp1pos = m_bp.size();
			if (right < size() - 1) {
				jp1pos = m_bp_support.select(right + 2);
			}
			return node_type(left, right, ipos, m_bp_support.find_close(ipos), jp1pos);
		}
	}

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

	//! Computes a unique identification number for a node of the suffx tree in the range [0..nodes()-1]
	/*!
         * \param v A valid node of a cst_sct3.
         * \return A unique identification number for the node v in the range [0..nodes()-1]
         * \par Time complexity
         *    \f$ \Order{1} \f$
         */
	size_type id(const node_type& v) const
	{
		if (is_leaf(v)) { // return id in the range from 0..csa.size()-1
			return v.i;
		}
		size_type ckpos;		  // closing parentheses of the l-index
		if (v.cipos > v.jp1pos) { // corresponds to m_lcp[i] <= m_lcp[j+1]
			ckpos = v.jp1pos - 1;
		} else { // corresponds to m_lcp[i] > m_lcp[j+1]
			ckpos = v.cipos - 1;
		}
		assert(m_bp[ckpos] == 0);
		size_type r0ckpos =
		ckpos - m_bp_support.rank(ckpos); // determine the rank of the closing parenthesis
		return size() + m_first_child_rank(r0ckpos);
	}

	//! 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$ \Order{\log size()} \f$ for inner nodes
         * \sa id(node_type v)
         */
	node_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
			// (1) get index of the closing parenthesis in m_first_child
			size_type r0ckpos = 0;
			{
				//binary search for the position of the (id-size()+1)-th set bit in
				id			 = id - size() + 1;
				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;
					size_type arg = m_first_child_rank(mid); // ones in the prefix [0..mid-1]
					if (arg < id) {
						lb = mid;
					} else { // arg >= id
						rb = mid;
					}
				}
				r0ckpos = lb;
			}
			// (2) determine position clpos of the r0clpos-th closing parentheses in the parentheses sequence
			size_type ckpos = 0;
			{
				// binary search for the position of the (r0ckpos+1)-th closing parenthesis
				size_type lb = 0, rb = m_bp.size(); // lb inclusive, rb exclusive
				// invariant: arg(lb) < r0ckpos+1,  arg(rb) >= r0ckpos+1
				while (rb - lb > 1) {
					size_type mid = lb + (rb - lb) / 2;
					size_type arg =
					mid - m_bp_support.rank(mid - 1); // zeros in the prefix [0..mid-1]
					if (arg < r0ckpos + 1) {
						lb = mid;
					} else { // arg >= x
						rb = mid;
					}
				}
				ckpos = lb;
			}
			if (ckpos == m_bp.size() - 1) {
				return root();
			}
			if (m_bp[ckpos + 1]) { // jp1pos < cipos
				size_type jp1pos = ckpos + 1;
				size_type j		 = m_bp_support.rank(jp1pos - 1) - 1;
				size_type kpos   = m_bp_support.find_open(ckpos);
				size_type ipos   = m_bp_support.enclose(kpos);
				size_type cipos  = m_bp_support.find_close(ipos);
				size_type i		 = m_bp_support.rank(ipos - 1);
				return node_type(i, j, ipos, cipos, jp1pos);
			} else { //
				size_type cipos  = ckpos + 1;
				size_type ipos   = m_bp_support.find_open(cipos);
				size_type i		 = m_bp_support.rank(ipos - 1);
				size_type j		 = nsv(i, ipos) - 1;
				size_type jp1pos = m_bp.size();
				if (j != size() - 1) {
					jp1pos = m_bp_support.select(j + 2);
				}
				return node_type(i, j, ipos, cipos, jp1pos);
			}
		}
	}

	//! Get the number of nodes of the suffix tree.
	size_type nodes() const { return m_nodes; }

	//! 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]
         * \par Time complexity
         *        \f$ \Order{1} \f$
         */
	node_type node(size_type lb, size_type rb) const
	{
		size_type ipos = m_bp_support.select(lb + 1);
		size_type jp1pos;
		if (rb == size() - 1) {
			jp1pos = m_bp.size();
		} else {
			jp1pos = m_bp_support.select(rb + 2);
		}
		return node_type(lb, rb, ipos, m_bp_support.find_close(ipos), jp1pos);
	}

	//! 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 ipos  = m_bp_support.select(i + 1);
		size_type cipos = m_bp_support.find_close(ipos);
		return m_first_child_rank.rank(((ipos + cipos - 1) >> 1) - i);
	}
	/* @} */
};

// == template functions ==


template <class t_csa, class t_lcp, class t_bp_support, class t_bv, class t_rank, class t_sel>
cst_sct3<t_csa, t_lcp, t_bp_support, t_bv, t_rank, t_sel>::cst_sct3(cache_config& config,
																	bool build_only_bps)
{
	{
		auto				event = memory_monitor::event("bps-sct");
		int_vector_buffer<> lcp_buf(cache_file_name(conf::KEY_LCP, config));
		m_nodes =
		construct_supercartesian_tree_bp_succinct_and_first_child(lcp_buf, m_bp, m_first_child);
		m_nodes += m_bp.size() / 2;
		if (m_bp.size() == 2) { // handle special case, when the tree consists only of the root node
			m_nodes = 1;
		}
	}
	{
		auto event = memory_monitor::event("bpss-sct");
		util::init_support(m_bp_support, &m_bp);
		util::init_support(m_first_child_rank, &m_first_child);
		util::init_support(m_first_child_select, &m_first_child);
	}
	if (!build_only_bps) {
		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;
	}
	if (!build_only_bps) {
		auto event = memory_monitor::event("load csa");
		load_from_cache(
		m_csa, std::string(conf::KEY_CSA) + "_" + util::class_to_hash(m_csa), config);
	}
}

template <class t_csa, class t_lcp, class t_bp_support, class t_bv, class t_rank, class t_sel>
auto cst_sct3<t_csa, t_lcp, t_bp_support, t_bv, t_rank, t_sel>::serialize(std::ostream& out,
																		  structure_tree_node* v,
																		  std::string name) const
-> size_type
{
	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_first_child.serialize(out, child, "mark_child");
	written_bytes += m_first_child_rank.serialize(out, child, "mark_child_rank");
	written_bytes += m_first_child_select.serialize(out, child, "mark_child_select");
	written_bytes += write_member(m_nodes, out, child, "node_cnt");
	structure_tree::add_size(child, written_bytes);
	return written_bytes;
}

template <class t_csa, class t_lcp, class t_bp_support, class t_bv, class t_rank, class t_sel>
void cst_sct3<t_csa, t_lcp, t_bp_support, t_bv, t_rank, t_sel>::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_first_child.load(in);
	m_first_child_rank.load(in, &m_first_child);
	m_first_child_select.load(in, &m_first_child);
	read_member(m_nodes, in);
}

template <class t_csa, class t_lcp, class t_bp_support, class t_bv, class t_rank, class t_sel>
template <typename archive_t>
void cst_sct3<t_csa, t_lcp, t_bp_support, t_bv, t_rank, t_sel>::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_first_child));
	ar(CEREAL_NVP(m_first_child_rank));
	ar(CEREAL_NVP(m_first_child_select));
	ar(CEREAL_NVP(m_nodes));
}

template <class t_csa, class t_lcp, class t_bp_support, class t_bv, class t_rank, class t_sel>
template <typename archive_t>
void cst_sct3<t_csa, t_lcp, t_bp_support, t_bv, t_rank, t_sel>::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_first_child));
	ar(CEREAL_NVP(m_first_child_rank));
	m_first_child_rank.set_vector(&m_first_child);
	ar(CEREAL_NVP(m_first_child_select));
	m_first_child_select.set_vector(&m_first_child);
	ar(CEREAL_NVP(m_nodes));
}

template <class t_int>
struct bp_interval {
	t_int i;	//!< The left border of the lcp-interval \f$\ell-[left..right]\f$.
	t_int j;	//!< The right border of the lcp-interval \f$\ell-[left..right]\f$.
	t_int ipos; // position of the i+1th opening parenthesis in the balanced parentheses sequence
	t_int
		  cipos;  // position of the matching closing parenthesis of the i+1th opening parenthesis in the balanced parentheses sequence
	t_int jp1pos; // position of the j+2th opening parenthesis in the balanced parentheses sequence

	//! Constructor
	bp_interval(t_int i = 0, t_int j = 0, t_int ipos = 0, t_int cipos = 0, t_int jp1pos = 0)
		: i(i), j(j), ipos(ipos), cipos(cipos), jp1pos(jp1pos){};

	//! Copy constructor
	bp_interval(const bp_interval& iv) = default;
	//! Move copy constructor
	bp_interval(bp_interval&& iv) = default;

	bool operator<(const bp_interval& interval) const
	{
		if (i != interval.i) return i < interval.i;
		return j < interval.j;
	}

	//! Equality operator.
	/*! Two lcp-intervals are equal if and only if all their corresponding member variables have the same values.
     */
	bool operator==(const bp_interval& interval) const
	{
		return i == interval.i and j == interval.j;
	}

	//! Inequality operator.
	/*! Two lcp-intervals are not equal if and only if not all their corresponding member variables have the same values.
      */
	bool operator!=(const bp_interval& interval) const { return !(*this == interval); }

	//! Assignment operator.
	bp_interval& operator=(const bp_interval& interval) = default;
	//! Move assignment
	bp_interval& operator=(bp_interval&& interval) = default;
};


template <class t_int>
inline std::ostream& operator<<(std::ostream& os, const bp_interval<t_int>& interval)
{
	os << "-[" << interval.i << "," << interval.j << "](" << interval.ipos << "," << interval.cipos
	   << "," << interval.jp1pos << ")";
	return os;
}


} // end namespace sdsl
#endif
