// 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 rank_support.hpp
    \brief rank_support.hpp contains classes that support a sdsl::bit_vector with constant time rank information.
	\author Simon Gog
*/
#ifndef INCLUDED_SDSL_RANK_SUPPORT
#define INCLUDED_SDSL_RANK_SUPPORT

/** \defgroup rank_support_group Rank Support (RS)
 * This group contains data structures which support an sdsl::bit_vector with the rank method.
 */

#include "int_vector.hpp"

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

//! The base class of classes supporting rank_queries for a sdsl::bit_vector in constant time.
/*!
*/
class rank_support {
protected:
	const bit_vector* m_v; //!< Pointer to the rank supported bit_vector
public:
	typedef bit_vector::size_type size_type;

	//! Constructor
	/*! \param v The supported bit_vector.
         */
	rank_support(const bit_vector* v = nullptr);
	//! Copy constructor
	rank_support(const rank_support&) = default;
	rank_support(rank_support&&)	  = default;
	rank_support& operator=(const rank_support&) = default;
	rank_support& operator=(rank_support&&) = default;
	//! Destructor
	virtual ~rank_support() {}

	//! Answers rank queries for the supported bit_vector.
	/*!	\param i Argument for the length of the prefix v[0..i-1].
        	\returns Number of 1-bits in the prefix [0..i-1] of the supported bit_vector.
        	\note Method init has to be called before the first call of rank.
        	\sa init
         */
	virtual size_type rank(size_type i) const = 0;
	//! Alias for rank(i)
	virtual size_type operator()(size_type idx) const = 0;
	//! Serializes rank_support.
	/*! \param out Out-Stream to serialize the data to.
        */
	virtual size_type
	serialize(std::ostream& out, structure_tree_node* v, std::string name) const = 0;
	//! Loads the rank_support.
	/*! \param in In-Stream to load the rank_support data from.
            \param v The supported bit_vector.
         */
	virtual void load(std::istream& in, const bit_vector* v = nullptr) = 0;
	//! Sets the supported bit_vector to the given pointer.
	/*! \param v The new bit_vector to support.
         *  \note Method init has to be called before the next call of rank.
         *  \sa init, rank
         */
	virtual void set_vector(const bit_vector* v = nullptr) = 0;
};

inline rank_support::rank_support(const bit_vector* v) { m_v = v; }

//----------------------------------------------------------------------

template <uint8_t bit_pattern, uint8_t pattern_len>
struct rank_support_trait {
	typedef rank_support::size_type size_type;

	static size_type args_in_the_word(uint64_t, uint64_t&) { return 0; }

	static uint32_t word_rank(const uint64_t*, size_type) { return 0; }

	static uint32_t full_word_rank(const uint64_t*, size_type) { return 0; }

	static uint64_t init_carry() { return 0; }
};

template <>
struct rank_support_trait<0, 1> {
	typedef rank_support::size_type size_type;

	static size_type args_in_the_word(uint64_t w, uint64_t&) { return bits::cnt(~w); }

	static uint32_t word_rank(const uint64_t* data, size_type idx)
	{
		return bits::cnt((~*(data + (idx >> 6))) & bits::lo_set[idx & 0x3F]);
	}

	static uint32_t full_word_rank(const uint64_t* data, size_type idx)
	{
		return bits::cnt((~*(data + (idx >> 6))));
	}

	static uint64_t init_carry() { return 0; }
};

template <>
struct rank_support_trait<1, 1> {
	typedef rank_support::size_type size_type;

	static size_type args_in_the_word(uint64_t w, uint64_t&) { return bits::cnt(w); }

	static uint32_t word_rank(const uint64_t* data, size_type idx)
	{
		return bits::cnt(*(data + (idx >> 6)) & bits::lo_set[idx & 0x3F]);
	}

	static uint32_t full_word_rank(const uint64_t* data, size_type idx)
	{
		return bits::cnt(*(data + (idx >> 6)));
	}

	static uint64_t init_carry() { return 0; }
};

template <>
struct rank_support_trait<10, 2> {
	typedef rank_support::size_type size_type;

	static size_type args_in_the_word(uint64_t w, uint64_t& carry) { return bits::cnt10(w, carry); }

	static uint32_t word_rank(const uint64_t* data, size_type idx)
	{
		data		   = data + (idx >> 6);
		uint64_t carry = (idx > 63) ? *(data - 1) >> 63 : 0;
		return bits::cnt(bits::map10(*data, carry) & bits::lo_set[idx & 0x3F]);
	}

	static uint32_t full_word_rank(const uint64_t* data, size_type idx)
	{
		data		   = data + (idx >> 6);
		uint64_t carry = (idx > 63) ? *(data - 1) >> 63 : 0;
		return bits::cnt(bits::map10(*data, carry));
	}

	static uint64_t init_carry() { return 0; }
};

template <>
struct rank_support_trait<01, 2> {
	typedef rank_support::size_type size_type;

	static size_type args_in_the_word(uint64_t w, uint64_t& carry) { return bits::cnt01(w, carry); }

	static uint32_t word_rank(const uint64_t* data, size_type idx)
	{
		data		   = data + (idx >> 6);
		uint64_t carry = (idx > 63) ? *(data - 1) >> 63 : 1;
		return bits::cnt(bits::map01(*data, carry) & bits::lo_set[idx & 0x3F]);
	}

	static uint32_t full_word_rank(const uint64_t* data, size_type idx)
	{
		data		   = data + (idx >> 6);
		uint64_t carry = (idx > 63) ? *(data - 1) >> 63 : 1;
		return bits::cnt(bits::map01(*data, carry));
	}

	static uint64_t init_carry() { return 1; }
};

template <>
struct rank_support_trait<00, 2> {
	typedef rank_support::size_type size_type;

	static size_type args_in_the_word(uint64_t w, uint64_t& carry)
	{
		size_type res = bits::cnt(~(w | (w << 1 | carry)));
		carry		  = (w >> 63);
		return res;
	}

	static uint32_t word_rank(const uint64_t* data, size_type idx)
	{
		data		   = data + (idx >> 6);
		uint64_t carry = (idx > 63) ? *(data - 1) >> 63 : 1;
		return bits::cnt((~(*data | ((*data) << 1 | carry))) & bits::lo_set[idx & 0x3F]);
	}

	static uint32_t full_word_rank(const uint64_t* data, size_type idx)
	{
		data		   = data + (idx >> 6);
		uint64_t carry = (idx > 63) ? *(data - 1) >> 63 : 1;
		return bits::cnt(~(*data | ((*data) << 1 | carry)));
	}

	static uint64_t init_carry() { return 1; }
};

template <>
struct rank_support_trait<11, 2> {
	typedef rank_support::size_type size_type;

	static size_type args_in_the_word(uint64_t w, uint64_t& carry)
	{
		size_type res = bits::cnt(w & (w << 1 | carry));
		carry		  = (w >> 63);
		return res;
	}

	static uint32_t word_rank(const uint64_t* data, size_type idx)
	{
		data		   = data + (idx >> 6);
		uint64_t carry = (idx > 63) ? *(data - 1) >> 63 : 0;
		return bits::cnt((*data & ((*data) << 1 | carry)) & bits::lo_set[idx & 0x3F]);
	}

	static uint32_t full_word_rank(const uint64_t* data, size_type idx)
	{
		data		   = data + (idx >> 6);
		uint64_t carry = (idx > 63) ? *(data - 1) >> 63 : 0;
		return bits::cnt(*data & ((*data) << 1 | carry));
	}

	static uint64_t init_carry() { return 0; }
};

} // end namespace sdsl

#include "rank_support_v.hpp"
#include "rank_support_v5.hpp"
#include "rank_support_scan.hpp"

#endif // end file
