// 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 rrr_vector.hpp
   \brief rrr_vector.hpp contains the sdsl::rrr_vector class, and
          classes which support rank and select for rrr_vector.
   \author Simon Gog, Matthias Petri
*/
#ifndef INCLUDED_SDSL_RRR_VECTOR
#define INCLUDED_SDSL_RRR_VECTOR

#include "int_vector.hpp"
#include "util.hpp"
#include "rrr_helper.hpp" // for binomial helper class
#include "iterators.hpp"
#include <vector>
#include <algorithm> // for next_permutation
#include <iostream>

//! Namespace for the succinct data structure library
namespace sdsl {

// forward declaration needed for friend declaration
template <uint8_t t_b = 1, uint16_t t_bs = 15, class t_rac = int_vector<>, uint16_t t_k = 32>
class rank_support_rrr; // in rrr_vector

// forward declaration needed for friend declaration
template <uint8_t t_b = 1, uint16_t t_bs = 15, class t_rac = int_vector<>, uint16_t t_k = 32>
class select_support_rrr; // in rrr_vector

//! A \f$H_0f$-compressed bitvector representation.
/*!
 *   \tparam t_bs   Size of a basic block.
 *   \tparam t_rac  Random access integer vector. Use to store the block types.
 *                  It is possible to use WTs for t_rac.
 *   \tparam t_k    A rank sample value is stored before every t_k-th basic block.
 *
 *   References:
 *    - Rasmus Pagh
 *      Low redundancy in dictionaries with O(1) worst case lookup time
 *      Technical Report 1998.
 *      ftp://ftp.cs.au.dk/BRICS/Reports/RS/98/28/BRICS-RS-98-28.pdf, Section 2.
 *    - Rajeev Raman, V. Raman and S. Srinivasa Rao
 *      Succinct Indexable Dictionaries with Applications to representations
 *      of k-ary trees and multi-sets.
 *      SODA 2002.
 *    - Francisco Claude, Gonzalo Navarro:
 *      Practical Rank/Select Queries over Arbitrary Sequences.
 *      SPIRE 2008: 176-187
 *    - On the fly-decoding and encoding was discovered in;
 *      Gonzalo Navarro, Eliana Providel:
 *      Fast, Small, Simple Rank/Select on Bitmaps.
 *      SEA 2012
 *
 *    In this version the block size can be adjust by the template parameter t_bs!
 *    \sa sdsl::rrr_vector for a specialized version for block_size=15
 */
template <uint16_t t_bs = 63, class t_rac = int_vector<>, uint16_t t_k = 32>
class rrr_vector {
	static_assert(t_bs >= 3 and t_bs <= 256,
				  "rrr_vector: block size t_bs must be 3 <= t_bs <= 256.");
	static_assert(t_k > 1, "rrr_vector: t_k must be > 0.");

public:
	typedef bit_vector::size_type					 size_type;
	typedef bit_vector::value_type					 value_type;
	typedef bit_vector::difference_type				 difference_type;
	typedef t_rac									 rac_type;
	typedef random_access_const_iterator<rrr_vector> iterator;
	typedef iterator								 const_iterator;
	typedef bv_tag									 index_category;

	typedef rank_support_rrr<1, t_bs, t_rac, t_k>   rank_1_type;
	typedef rank_support_rrr<0, t_bs, t_rac, t_k>   rank_0_type;
	typedef select_support_rrr<1, t_bs, t_rac, t_k> select_1_type;
	typedef select_support_rrr<0, t_bs, t_rac, t_k> select_0_type;

	friend class rank_support_rrr<0, t_bs, t_rac, t_k>;
	friend class rank_support_rrr<1, t_bs, t_rac, t_k>;
	friend class select_support_rrr<0, t_bs, t_rac, t_k>;
	friend class select_support_rrr<1, t_bs, t_rac, t_k>;

	typedef rrr_helper<t_bs>					  rrr_helper_type;
	typedef typename rrr_helper_type::number_type number_type;

	enum { block_size = t_bs };

private:
	size_type m_size = 0; // Size of the original bit_vector.
	rac_type  m_bt;		  // Vector for the block types (bt). bt equals the
	// number of set bits in the block.
	bit_vector   m_btnr;   // Compressed block type numbers.
	int_vector<> m_btnrp;  // Sample pointers into m_btnr.
	int_vector<> m_rank;   // Sample rank values.
	bit_vector   m_invert; // Specifies if a superblock (i.e. t_k blocks)
						   // have to be considered as inverted i.e. 1 and
						   // 0 are swapped

public:
	const rac_type&   bt   = m_bt;
	const bit_vector& btnr = m_btnr;

	//! Default constructor
	rrr_vector(){};
	rrr_vector(const rrr_vector& v)
		: m_size(v.m_size)
		, m_bt(v.m_bt)
		, m_btnr(v.m_btnr)
		, m_btnrp(v.m_btnrp)
		, m_rank(v.m_rank)
		, m_invert(v.m_invert)
	{
	}

	rrr_vector(rrr_vector&& v) { *this = std::move(v); }
	rrr_vector& operator=(const rrr_vector& v)
	{
		if (this != &v) {
			rrr_vector tmp(v);		// re-use copy-constructor
			*this = std::move(tmp); // re-use move-assignment
		}
		return *this;
	}
	rrr_vector& operator=(rrr_vector&& v)
	{
		if (this != &v) {
			m_size   = v.m_size;
			m_bt	 = std::move(v.m_bt);
			m_btnr   = std::move(v.m_btnr);
			m_btnrp  = std::move(v.m_btnrp);
			m_rank   = std::move(v.m_rank);
			m_invert = std::move(v.m_invert);
		}
		return *this;
	}

	//! Constructor
	/*!
        *  \param bv  Uncompressed bitvector.
        *  \param k   Store rank samples and pointers each k-th blocks.
        */
	rrr_vector(const bit_vector& bv)
	{
		m_size = bv.size();
		int_vector<> bt_array;
		bt_array.width(bits::hi(t_bs) + 1);
		bt_array.resize((m_size + t_bs) /
						((size_type)t_bs)); // blocks for the bt_array + a dummy block at the end,
		// if m_size%t_bs == 0

		// (1) calculate the block types and store them in m_bt
		size_type pos = 0, i = 0, x;
		size_type btnr_pos = 0;
		size_type sum_rank = 0;
		while (pos + t_bs <= m_size) { // handle all blocks full blocks
			bt_array[i++] = x = rrr_helper_type::get_bt(bv, pos, t_bs);
			sum_rank += x;
			btnr_pos += rrr_helper_type::space_for_bt(x);
			pos += t_bs;
		}
		if (pos < m_size) { // handle last not full block
			bt_array[i++] = x = rrr_helper_type::get_bt(bv, pos, m_size - pos);
			sum_rank += x;
			btnr_pos += rrr_helper_type::space_for_bt(x);
		}
		m_btnr =
		bit_vector(std::max(btnr_pos, (size_type)64), 0); // max necessary for case: t_bs == 1
		m_btnrp = int_vector<>((bt_array.size() + t_k - 1) / t_k, 0, bits::hi(btnr_pos) + 1);
		m_rank  = int_vector<>((bt_array.size() + t_k - 1) / t_k + ((m_size % (t_k * t_bs)) > 0),
							  0,
							  bits::hi(sum_rank) + 1);
		//                                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
		//   only add a finishing block, if the last block of the superblock is not a dummy block
		m_invert = bit_vector((bt_array.size() + t_k - 1) / t_k, 0);

		// (2) calculate block type numbers and pointers into btnr and rank samples
		pos		 = 0;
		i		 = 0;
		btnr_pos = 0, sum_rank = 0;
		bool invert = false;
		while (pos + t_bs <= m_size) { // handle all full blocks
			if ((i % t_k) == (size_type)0) {
				m_btnrp[i / t_k] = btnr_pos;
				m_rank[i / t_k]  = sum_rank;
				// calculate invert bit for that superblock
				if (i + t_k <= bt_array.size()) {
					size_type gt_half_t_bs =
					0; // counter for blocks greater than half of the blocksize
					for (size_type j = i; j < i + t_k; ++j) {
						if (bt_array[j] > t_bs / 2) ++gt_half_t_bs;
					}
					if (gt_half_t_bs > (t_k / 2)) {
						m_invert[i / t_k] = 1;
						for (size_type j = i; j < i + t_k; ++j) {
							bt_array[j] = t_bs - bt_array[j];
						}
						invert = true;
					} else {
						invert = false;
					}
				} else {
					invert = false;
				}
			}
			uint16_t space_for_bt = rrr_helper_type::space_for_bt(x = bt_array[i++]);
			sum_rank += (invert ? (t_bs - x) : x);
			if (space_for_bt) {
				number_type bin = rrr_helper_type::decode_btnr(bv, pos, t_bs);
				number_type nr  = rrr_helper_type::bin_to_nr(bin);
				rrr_helper_type::set_bt(m_btnr, btnr_pos, nr, space_for_bt);
			}
			btnr_pos += space_for_bt;
			pos += t_bs;
		}
		if (pos < m_size) { // handle last not full block
			if ((i % t_k) == (size_type)0) {
				m_btnrp[i / t_k]  = btnr_pos;
				m_rank[i / t_k]   = sum_rank;
				m_invert[i / t_k] = 0; // default: set last block to not inverted
				invert			  = false;
			}
			uint16_t space_for_bt = rrr_helper_type::space_for_bt(x = bt_array[i++]);
			//          no extra dummy block added to bt_array, therefore this condition should hold
			assert(i == bt_array.size());
			sum_rank += invert ? (t_bs - x) : x;
			if (space_for_bt) {
				number_type bin = rrr_helper_type::decode_btnr(bv, pos, m_size - pos);
				number_type nr  = rrr_helper_type::bin_to_nr(bin);
				rrr_helper_type::set_bt(m_btnr, btnr_pos, nr, space_for_bt);
			}
			btnr_pos += space_for_bt;
			assert(m_rank.size() - 1 == ((i + t_k - 1) / t_k));
		} else { // handle last empty full block
			assert(m_rank.size() - 1 == ((i + t_k - 1) / t_k));
		}
		// for technical reasons we add a last element to m_rank
		m_rank[m_rank.size() - 1] =
		sum_rank; // sum_rank contains the total number of set bits in bv
		m_bt = bt_array;
	}


	//! Accessing the i-th element of the original bit_vector
	/*! \param i An index i with \f$ 0 \leq i < size()  \f$.
           \return The i-th bit of the original bit_vector
        */
	value_type operator[](size_type i) const
	{
		size_type bt_idx			 = i / t_bs;
		uint16_t  bt				 = m_bt[bt_idx];
		size_type sample_pos		 = bt_idx / t_k;
		if (m_invert[sample_pos]) bt = t_bs - bt;
#ifndef RRR_NO_OPT
		if (bt == 0 or bt == t_bs) { // very effective optimization
			return bt > 0;
		}
#endif
		uint16_t  off   = i % t_bs; //i - bt_idx*t_bs;
		size_type btnrp = m_btnrp[sample_pos];
		for (size_type j = sample_pos * t_k; j < bt_idx; ++j) {
			btnrp += rrr_helper_type::space_for_bt(m_bt[j]);
		}
		uint16_t	btnrlen = rrr_helper_type::space_for_bt(bt);
		number_type btnr	= rrr_helper_type::decode_btnr(m_btnr, btnrp, btnrlen);
		return rrr_helper_type::decode_bit(bt, btnr, off);
	}

	//! Get the integer value of the binary string of length len starting at position idx.
	/*! \param idx Starting index of the binary representation of the integer.
         *  \param len Length of the binary representation of the integer. Default value is 64.
         *   \returns The integer value of the binary string of length len starting at position idx.
         *
         *  \pre idx+len-1 in [0..size()-1]
         *  \pre len in [1..64]
         */
	uint64_t get_int(size_type idx, uint8_t len = 64) const
	{
		uint64_t  res		 = 0;
		size_type bb_idx	 = idx / t_bs; // begin block index
		size_type bb_off	 = idx % t_bs; // begin block offset
		uint16_t  bt		 = m_bt[bb_idx];
		size_type sample_pos = bb_idx / t_k;
		size_type eb_idx	 = (idx + len - 1) / t_bs; // end block index
		if (bb_idx == eb_idx) {						   // extract only in one block
			if (m_invert[sample_pos]) bt = t_bs - bt;
			if (bt == 0) { // all bits are zero
				res = 0;
			} else if (bt == t_bs and t_bs <= 64) { // all bits are zero
				res = bits::lo_set[len];
			} else {
				size_type btnrp = m_btnrp[sample_pos];
				for (size_type j = sample_pos * t_k; j < bb_idx; ++j) {
					btnrp += rrr_helper_type::space_for_bt(m_bt[j]);
				}
				uint16_t	btnrlen = rrr_helper_type::space_for_bt(bt);
				number_type btnr	= rrr_helper_type::decode_btnr(m_btnr, btnrp, btnrlen);
				res					= rrr_helper_type::decode_int(bt, btnr, bb_off, len);
			}
		} else {								// solve multiple block case by recursion
			uint16_t b_len	 = t_bs - bb_off; // remaining bits in first block
			uint16_t b_len_sum = 0;
			do {
				res |= get_int(idx, b_len) << b_len_sum;
				idx += b_len;
				b_len_sum += b_len;
				len -= b_len;
				b_len = t_bs;
				b_len = std::min((uint16_t)len, b_len);
			} while (len > 0);
		}
		return res;
	}


	//! Returns the size of the original bit vector.
	size_type size() const { return m_size; }

	//! Answers select queries
	//! Serializes the data structure into the given ostream
	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 += write_member(m_size, out, child, "size");
		written_bytes += m_bt.serialize(out, child, "bt");
		written_bytes += m_btnr.serialize(out, child, "btnr");
		written_bytes += m_btnrp.serialize(out, child, "btnrp");
		written_bytes += m_rank.serialize(out, child, "rank_samples");
		written_bytes += m_invert.serialize(out, child, "invert");
		structure_tree::add_size(child, written_bytes);
		return written_bytes;
	}

	//! Loads the data structure from the given istream.
	void load(std::istream& in)
	{
		read_member(m_size, in);
		m_bt.load(in);
		m_btnr.load(in);
		m_btnrp.load(in);
		m_rank.load(in);
		m_invert.load(in);
	}

	template <typename archive_t>
	void CEREAL_SAVE_FUNCTION_NAME(archive_t & ar) const
	{
		ar(CEREAL_NVP(m_size));
		ar(CEREAL_NVP(m_bt));
		ar(CEREAL_NVP(m_btnr));
		ar(CEREAL_NVP(m_btnrp));
		ar(CEREAL_NVP(m_rank));
		ar(CEREAL_NVP(m_invert));
	}

	template <typename archive_t>
	void CEREAL_LOAD_FUNCTION_NAME(archive_t & ar)
	{
		ar(CEREAL_NVP(m_size));
		ar(CEREAL_NVP(m_bt));
		ar(CEREAL_NVP(m_btnr));
		ar(CEREAL_NVP(m_btnrp));
		ar(CEREAL_NVP(m_rank));
		ar(CEREAL_NVP(m_invert));
	}

	iterator begin() const { return iterator(this, 0); }

	iterator end() const { return iterator(this, size()); }

	bool operator==(const rrr_vector& v) const
	{
		return m_size == v.m_size && m_bt == v.m_bt && m_btnr == v.m_btnr && m_btnrp == v.m_btnrp &&
			   m_rank == v.m_rank && m_invert == v.m_invert;
	}

	bool operator!=(const rrr_vector& v) const { return !(*this == v); }
};

template <uint8_t t_bit_pattern>
struct rank_support_rrr_trait {
	typedef bit_vector::size_type size_type;
	static size_type adjust_rank(size_type r, SDSL_UNUSED size_type n) { return r; }
};

template <>
struct rank_support_rrr_trait<0> {
	typedef bit_vector::size_type size_type;
	static size_type adjust_rank(size_type r, size_type n) { return n - r; }
};

//! rank_support for the rrr_vector class
/*!
* \tparam t_b   The bit pattern of size one. (so `0` or `1`)
* \tparam t_bs  The block size of the corresponding rrr_vector
* \tparam t_rac Type used to store the block type in the corresponding rrr_vector.
*  TODO: Test if the binary search can be speed up by
*        saving the (n/2)-th rank value in T[0], the (n/4)-th in T[1],
*        the (3n/4)-th in T[2],... for small number of rank values
*    is this called hinted binary search???
*    or is this called
*/
template <uint8_t t_b, uint16_t t_bs, class t_rac, uint16_t t_k>
class rank_support_rrr {
	static_assert(t_b == 1u or t_b == 0u, "rank_support_rrr: bit pattern must be `0` or `1`");

public:
	typedef rrr_vector<t_bs, t_rac, t_k> bit_vector_type;
	typedef typename bit_vector_type::size_type		  size_type;
	typedef typename bit_vector_type::rrr_helper_type rrr_helper_type;
	typedef typename rrr_helper_type::number_type	 number_type;
	enum { bit_pat = t_b };
	enum { bit_pat_len = (uint8_t)1 };

private:
	const bit_vector_type* m_v; //!< Pointer to the rank supported rrr_vector

public:
	//! Standard constructor
	/*! \param v Pointer to the rrr_vector, which should be supported
         */
	explicit rank_support_rrr(const bit_vector_type* v = nullptr) { set_vector(v); }

	//! Answers rank queries
	/*! \param i Argument for the length of the prefix v[0..i-1], with \f$0\leq i \leq size()\f$.
           \returns Number of 1-bits in the prefix [0..i-1] of the original bit_vector.
           \par Time complexity
                \f$ \Order{ sample\_rate of the rrr\_vector} \f$
        */
	const size_type rank(size_type i) const
	{
		assert(m_v != nullptr);
		assert(i <= m_v->size());
		size_type bt_idx	 = i / t_bs;
		size_type sample_pos = bt_idx / t_k;
		size_type btnrp		 = m_v->m_btnrp[sample_pos];
		size_type rank		 = m_v->m_rank[sample_pos];
		if (sample_pos + 1 < m_v->m_rank.size()) {
			size_type diff_rank = m_v->m_rank[sample_pos + 1] - rank;
#ifndef RRR_NO_OPT
			if (diff_rank == (size_type)0) {
				return rank_support_rrr_trait<t_b>::adjust_rank(rank, i);
			} else if (diff_rank == (size_type)t_bs * t_k) {
				return rank_support_rrr_trait<t_b>::adjust_rank(rank + i - sample_pos * t_k * t_bs,
																i);
			}
#endif
		}
		const bool inv = m_v->m_invert[sample_pos];
		for (size_type j = sample_pos * t_k; j < bt_idx; ++j) {
			uint16_t r = m_v->m_bt[j];
			rank += (inv ? t_bs - r : r);
			btnrp += rrr_helper_type::space_for_bt(r);
		}
		uint16_t off = i % t_bs;
		if (!off) { // needed for special case: if i=size() is a multiple of t_bs
			// the access to m_bt would cause a invalid memory access
			return rank_support_rrr_trait<t_b>::adjust_rank(rank, i);
		}
		uint16_t bt = inv ? t_bs - m_v->m_bt[bt_idx] : m_v->m_bt[bt_idx];

		uint16_t	btnrlen = rrr_helper_type::space_for_bt(bt);
		number_type btnr	= rrr_helper_type::decode_btnr(m_v->m_btnr, btnrp, btnrlen);
		uint16_t	popcnt  = rrr_helper_type::decode_popcount(bt, btnr, off);
		return rank_support_rrr_trait<t_b>::adjust_rank(rank + popcnt, i);
	}

	//! Short hand for rank(i)
	const size_type operator()(size_type i) const { return rank(i); }

	//! Returns the size of the original vector
	const size_type size() const { return m_v->size(); }

	//! Set the supported vector.
	void set_vector(const bit_vector_type* v = nullptr) { m_v = v; }

	rank_support_rrr& operator=(const rank_support_rrr& rs)
	{
		if (this != &rs) {
			set_vector(rs.m_v);
		}
		return *this;
	}

	//! Load the data structure from a stream and set the supported vector.
	void load(std::istream&, const bit_vector_type* v = nullptr) { set_vector(v); }

	//! Serializes the data structure into a stream.
	size_type
	serialize(std::ostream&, structure_tree_node* v = nullptr, std::string name = "") const
	{
		structure_tree_node* child = structure_tree::add_child(v, name, util::class_name(*this));
		structure_tree::add_size(child, 0);
		return 0;
	}

	template <typename archive_t>
	void CEREAL_SAVE_FUNCTION_NAME(archive_t &) const {}

	template <typename archive_t>
	void CEREAL_LOAD_FUNCTION_NAME(archive_t &) {}

	bool operator==(const rank_support_rrr& other) const noexcept
	{
		return *m_v == *other.m_v;
	}

	bool operator!=(const rank_support_rrr& other) const noexcept
	{
		return !(*this == other);
	}
};


//! Select support for the rrr_vector class.
/*
* \tparam t_b   The bit pattern of size one. (so `0` or `1`)
* \tparam t_bs  The block size of the corresponding rrr_vector
* \tparam t_rac Type used to store the block type in the corresponding rrr_vector.
*
* Possible TODO: Add heap which contains the 10 first items of
* each binary search could increase performance.
* Experiments on select_support_interleaved showed about
* 25%.
*/
template <uint8_t t_b, uint16_t t_bs, class t_rac, uint16_t t_k>
class select_support_rrr {
	static_assert(t_b == 1u or t_b == 0u, "select_support_rrr: bit pattern must be `0` or `1`");

public:
	typedef rrr_vector<t_bs, t_rac, t_k> bit_vector_type;
	typedef typename bit_vector_type::size_type		  size_type;
	typedef typename bit_vector_type::rrr_helper_type rrr_helper_type;
	typedef typename rrr_helper_type::number_type	 number_type;
	enum { bit_pat = t_b };
	enum { bit_pat_len = (uint8_t)1 };

private:
	const bit_vector_type* m_v; //!< Pointer to the rank supported rrr_vector

	size_type select1(size_type i) const
	{
		if (m_v->m_rank[m_v->m_rank.size() - 1] < i) return size();
		//  (1) binary search for the answer in the rank_samples
		size_type begin = 0, end = m_v->m_rank.size() - 1; // min included, max excluded
		size_type idx, rank;
		// invariant:  m_rank[end]   >= i
		//             m_rank[begin]  < i
		while (end - begin > 1) {
			idx  = (begin + end) >> 1; // idx in [0..m_rank.size()-1]
			rank = m_v->m_rank[idx];
			if (rank >= i)
				end = idx;
			else { // rank < i
				begin = idx;
			}
		}
		//   (2) linear search between the samples
		rank				= m_v->m_rank[begin]; // now i>rank
		idx					= begin * t_k;		  // initialize idx for select result
		size_type diff_rank = m_v->m_rank[end] - rank;
#ifndef RRR_NO_OPT
		if (diff_rank == (size_type)t_bs * t_k) { // optimisation for select<1>
			return idx * t_bs + i - rank - 1;
		}
#endif
		const bool inv   = m_v->m_invert[begin];
		size_type  btnrp = m_v->m_btnrp[begin];
		uint16_t   bt = 0, btnrlen = 0; // temp variables for block_type and space for block type
		while (i > rank) {
			bt = m_v->m_bt[idx++];
			bt = inv ? t_bs - bt : bt;
			rank += bt;
			btnrp += (btnrlen = rrr_helper_type::space_for_bt(bt));
		}
		rank -= bt;
		number_type btnr = rrr_helper_type::decode_btnr(m_v->m_btnr, btnrp - btnrlen, btnrlen);
		return (idx - 1) * t_bs + rrr_helper_type::decode_select(bt, btnr, i - rank);
	}

	size_type select0(size_type i) const
	{
		if ((size() - m_v->m_rank[m_v->m_rank.size() - 1]) < i) {
			return size();
		}
		//  (1) binary search for the answer in the rank_samples
		size_type begin = 0, end = m_v->m_rank.size() - 1; // min included, max excluded
		size_type idx, rank;
		// invariant:  m_rank[end] >= i
		//             m_rank[begin] < i
		while (end - begin > 1) {
			idx  = (begin + end) >> 1; // idx in [0..m_rank.size()-1]
			rank = idx * t_bs * t_k - m_v->m_rank[idx];
			if (rank >= i)
				end = idx;
			else { // rank < i
				begin = idx;
			}
		}
		//   (2) linear search between the samples
		rank = begin * t_bs * t_k - m_v->m_rank[begin]; // now i>rank
		idx  = begin * t_k;								// initialize idx for select result
		if (m_v->m_rank[end] == m_v->m_rank[begin]) {   // only for select<0>
			return idx * t_bs + i - rank - 1;
		}
		const bool inv   = m_v->m_invert[begin];
		size_type  btnrp = m_v->m_btnrp[begin];
		uint16_t   bt = 0, btnrlen = 0; // temp variables for block_type and space for block type
		while (i > rank) {
			bt = m_v->m_bt[idx++];
			bt = inv ? t_bs - bt : bt;
			rank += (t_bs - bt);
			btnrp += (btnrlen = rrr_helper_type::space_for_bt(bt));
		}
		rank -= (t_bs - bt);
		number_type btnr = rrr_helper_type::decode_btnr(m_v->m_btnr, btnrp - btnrlen, btnrlen);
		return (idx - 1) * t_bs +
			   rrr_helper_type::template decode_select_bitpattern<0, 1>(bt, btnr, i - rank);
	}


public:
	explicit select_support_rrr(const bit_vector_type* v = nullptr) { set_vector(v); }

	//! Answers select queries
	size_type select(size_type i) const { return t_b ? select1(i) : select0(i); }

	const size_type operator()(size_type i) const { return select(i); }

	const size_type size() const { return m_v->size(); }

	void set_vector(const bit_vector_type* v = nullptr) { m_v = v; }

	select_support_rrr& operator=(const select_support_rrr& rs)
	{
		if (this != &rs) {
			set_vector(rs.m_v);
		}
		return *this;
	}

	void load(std::istream&, const bit_vector_type* v = nullptr) { set_vector(v); }

	size_type
	serialize(std::ostream&, structure_tree_node* v = nullptr, std::string name = "") const
	{
		structure_tree_node* child = structure_tree::add_child(v, name, util::class_name(*this));
		structure_tree::add_size(child, 0);
		return 0;
	}

	template <typename archive_t>
	void CEREAL_SAVE_FUNCTION_NAME(archive_t &) const {}

	template <typename archive_t>
	void CEREAL_LOAD_FUNCTION_NAME(archive_t &) {}

	bool operator==(const select_support_rrr& other) const noexcept
	{
		return *m_v == *other.m_v;
	}

	bool operator!=(const select_support_rrr& other) const noexcept
	{
		return !(*this == other);
	}
};

} // end namespace sdsl
#include "rrr_vector_15.hpp" // include specialization

#endif
