// 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 enc_vector.hpp
   \brief enc_vector.hpp contains the sdsl::enc_vector class.
   \author Simon Gog
*/
#ifndef SDSL_ENC_VECTOR
#define SDSL_ENC_VECTOR

#include "int_vector.hpp"
#include "coder.hpp"
#include "iterators.hpp"


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

template <uint8_t t_width>
struct enc_vector_trait {
	typedef int_vector<0> int_vector_type;
};

template <>
struct enc_vector_trait<32> {
	typedef int_vector<32> int_vector_type;
};

template <>
struct enc_vector_trait<64> {
	typedef int_vector<64> int_vector_type;
};

//! A generic immutable space-saving vector class for unsigned integers.
/*! A vector v is stored more space-efficiently by self-delimiting coding
 *  the deltas v[i+1]-v[i] (v[-1]:=0). Space of the structure and random
 *  access time to it can be controlled by a sampling parameter t_dens.
 *
 *  \tparam t_coder  Self-delimiting coder.
 *  \tparam t_dens   Every t_dens-th element of v is sampled.
 *  \tparam t_width  Width of the int_vector used to store the samples and pointers.
 *  This class is a parameter of csa_sada.
 * @ingroup int_vector
 */
template <class t_coder = coder::elias_delta<>, uint32_t t_dens = 128, uint8_t t_width = 0>
class enc_vector {
private:
	static_assert(t_dens > 1, "enc_vector: sample density must be larger than `1`");

public:
	typedef uint64_t											value_type;
	typedef random_access_const_iterator<enc_vector>			iterator;
	typedef iterator											const_iterator;
	typedef const value_type									reference;
	typedef const value_type									const_reference;
	typedef const value_type*									const_pointer;
	typedef ptrdiff_t											difference_type;
	typedef int_vector<>::size_type								size_type;
	typedef t_coder												coder;
	typedef typename enc_vector_trait<t_width>::int_vector_type int_vector_type;
	typedef iv_tag												index_category;
	static const uint32_t										sample_dens = t_dens;
	typedef enc_vector											enc_vec_type;

	int_vector<0> m_z; // storage for encoded deltas
private:
	int_vector_type m_sample_vals_and_pointer; // samples and pointers
	size_type		m_size = 0;				   // number of vector elements

	void clear()
	{
		m_z.resize(0);
		m_z.shrink_to_fit();
		m_size = 0;
		m_sample_vals_and_pointer.resize(0);
		m_sample_vals_and_pointer.shrink_to_fit();
	}

public:
	enc_vector()				  = default;
	enc_vector(const enc_vector&) = default;
	enc_vector(enc_vector&&)	  = default;
	enc_vector& operator=(const enc_vector&) = default;
	enc_vector& operator=(enc_vector&&) = default;

	//! Constructor for a Container of unsigned integers.
	/*! \param c A container of unsigned integers.
          */
	template <class Container>
	enc_vector(const Container& c);

	//! Constructor for an int_vector_buffer of unsigned integers.
	/*
            \param v_buf A int_vector_buf.
        */
	template <uint8_t int_width>
	enc_vector(int_vector_buffer<int_width>& v_buf);

	//! Default Destructor
	~enc_vector() {}

	//! The number of elements in the enc_vector.
	size_type size() const { return m_size; }

	//! Return the largest size that this container can ever have.
	static size_type max_size() { return int_vector<>::max_size() / 2; }

	//!    Returns if the enc_vector is empty.
	bool empty() const { return 0 == m_size; }

	//! Iterator that points to the first element of the enc_vector.
	const const_iterator begin() const { return const_iterator(this, 0); }

	//! Iterator that points to the position after the last element of the enc_vector.
	const const_iterator end() const { return const_iterator(this, this->m_size); }

	bool operator==(const enc_vector& v) const
	{
		return m_size && v.m_size && m_z == v.m_z &&
			   m_sample_vals_and_pointer == v.m_sample_vals_and_pointer;
	}

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

	//! operator[]
	/*! \param i Index. \f$ i \in [0..size()-1]\f$.
         */
	value_type operator[](size_type i) const;

	//! Serialize the enc_vector to a stream.
	/*! \param out Out stream 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 the enc_vector from a stream.
	void load(std::istream& in);

	template <typename archive_t>
	void CEREAL_SAVE_FUNCTION_NAME(archive_t & ar) const;

	template <typename archive_t>
	void CEREAL_LOAD_FUNCTION_NAME(archive_t & ar);

	//! Returns the i-th sample of enc_vector
	/*! \param i The index of the sample. 0 <= i < size()/get_sample_dens()
         *  \return The value of the i-th sample.
         */
	value_type sample(const size_type i) const;

	uint32_t get_sample_dens() const { return t_dens; }

	/*!
         * \param i The index of the sample for which all values till the next sample should be decoded. 0 <= i < size()/get_sample_dens()
         * \param it A pointer to a uint64_t vector, whereto the values should be written
         */
	void get_inter_sampled_values(const size_type i, uint64_t* it) const
	{
		*(it++) = 0;
		if (i * t_dens + t_dens - 1 < size()) {
			t_coder::template decode<true, true>(
			m_z.data(), m_sample_vals_and_pointer[(i << 1) + 1], t_dens - 1, it);
		} else {
			assert(i * t_dens < size());
			t_coder::template decode<true, true>(
			m_z.data(), m_sample_vals_and_pointer[(i << 1) + 1], size() - i * t_dens - 1, it);
		}
	};
};

template <class t_coder, uint32_t t_dens, uint8_t t_width>
inline typename enc_vector<t_coder, t_dens, t_width>::value_type
enc_vector<t_coder, t_dens, t_width>::operator[](const size_type i) const
{
	assert(i + 1 != 0);
	assert(i < m_size);
	size_type idx = i / get_sample_dens();
	return m_sample_vals_and_pointer[idx << 1] +
		   t_coder::decode_prefix_sum(
		   m_z.data(), m_sample_vals_and_pointer[(idx << 1) + 1], i - t_dens * idx);
}

template <class t_coder, uint32_t t_dens, uint8_t t_width>
inline typename enc_vector<t_coder, t_dens, t_width>::value_type
enc_vector<t_coder, t_dens, t_width>::sample(const size_type i) const
{
	assert(i * get_sample_dens() + 1 != 0);
	assert(i * get_sample_dens() < m_size);
	return m_sample_vals_and_pointer[i << 1];
}

template <class t_coder, uint32_t t_dens, uint8_t t_width>
template <class Container>
enc_vector<t_coder, t_dens, t_width>::enc_vector(const Container& c)
{
	// clear bit_vectors
	clear();

	if (c.empty()) // if c is empty there is nothing to do...
		return;
	typename Container::const_iterator it = c.begin(), end = c.end();
	typename Container::value_type	 v1 = *it, v2, max_sample_value = 0, x;
	size_type						   samples = 0;
	size_type						   z_size  = 0;
	//  (1) Calculate maximal value of samples and of deltas
	for (size_type i = 0, no_sample = 0; it != end; ++it, ++i, --no_sample) {
		v2 = *it;
		if (!no_sample) { // add a sample
			no_sample									= get_sample_dens();
			if (max_sample_value < v2) max_sample_value = v2;
			++samples;
		} else {
			z_size += t_coder::encoding_length(v2 - v1);
		}
		v1 = v2;
	}
	//    (2) Write sample values and deltas
	{
		if (max_sample_value > z_size + 1)
			m_sample_vals_and_pointer.width(bits::hi(max_sample_value) + 1);
		else
			m_sample_vals_and_pointer.width(bits::hi(z_size + 1) + 1);
		m_sample_vals_and_pointer.resize(2 * samples + 2); // add 2 for last entry
		util::set_to_value(m_sample_vals_and_pointer, 0);
		typename int_vector_type::iterator sv_it = m_sample_vals_and_pointer.begin();
		z_size									 = 0;
		size_type no_sample						 = 0;
		for (it = c.begin(); it != end; ++it, --no_sample) {
			v2 = *it;
			if (!no_sample) { // add a sample
				no_sample = get_sample_dens();
				*sv_it	= v2;
				++sv_it;
				*sv_it = z_size;
				++sv_it;
			} else {
				x = v2 - v1;
				z_size += t_coder::encoding_length(x);
			}
			v1 = v2;
		}
		*sv_it = 0;
		++sv_it; // initialize
		*sv_it = z_size + 1;
		++sv_it; // last entry

		m_z				 = int_vector<>(z_size, 0, 1);
		uint64_t* z_data = t_coder::raw_data(m_z);
		uint8_t   offset = 0;
		no_sample		 = 0;
		for (it = c.begin(); it != end; ++it, --no_sample) {
			v2 = *it;
			if (!no_sample) { // add a sample
				no_sample = get_sample_dens();
			} else {
				t_coder::encode(v2 - v1, z_data, offset);
			}
			v1 = v2;
		}
	}
	m_size = c.size();
}

template <class t_coder, uint32_t t_dens, uint8_t t_width>
template <uint8_t int_width>
enc_vector<t_coder, t_dens, t_width>::enc_vector(int_vector_buffer<int_width>& v_buf)
{
	// clear bit_vectors
	clear();
	size_type n = v_buf.size();
	if (n == 0) // if c is empty there is nothing to do...
		return;
	value_type		v1 = 0, v2 = 0, max_sample_value = 0;
	size_type		samples = 0, z_size = 0;
	const size_type sd = get_sample_dens();
	//  (1) Calculate maximal value of samples and of deltas
	for (size_type i = 0, no_sample = 0; i < n; ++i, --no_sample) {
		v2 = v_buf[i];
		if (!no_sample) { // is sample
			no_sample									= sd;
			if (max_sample_value < v2) max_sample_value = v2;
			++samples;
		} else {
			z_size += t_coder::encoding_length(v2 - v1);
		}
		v1 = v2;
	}

	//    (2) Write sample values and deltas
	//    (a) Initialize array for sample values and pointers
	if (max_sample_value > z_size + 1)
		m_sample_vals_and_pointer.width(bits::hi(max_sample_value) + 1);
	else
		m_sample_vals_and_pointer.width(bits::hi(z_size + 1) + 1);
	m_sample_vals_and_pointer.resize(2 * samples + 2); // add 2 for last entry
	util::set_to_value(m_sample_vals_and_pointer, 0);

	//    (b) Initilize bit_vector for encoded data
	m_z				 = int_vector<>(z_size, 0, 1);
	uint64_t* z_data = t_coder::raw_data(m_z);
	uint8_t   offset = 0;

	//    (c) Write sample values and deltas
	z_size = 0;
	for (size_type i = 0, j = 0, no_sample = 0; i < n; ++i, --no_sample) {
		v2 = v_buf[i];
		if (!no_sample) { // is sample
			no_sample					   = sd;
			m_sample_vals_and_pointer[j++] = v2;	 // write samples
			m_sample_vals_and_pointer[j++] = z_size; // write pointers
		} else {
			z_size += t_coder::encoding_length(v2 - v1);
			t_coder::encode(v2 - v1, z_data, offset); // write encoded values
		}
		v1 = v2;
	}
	m_size = n;
}

template <class t_coder, uint32_t t_dens, uint8_t t_width>
enc_vector<>::size_type enc_vector<t_coder, t_dens, t_width>::serialize(std::ostream& out,
																		structure_tree_node* v,
																		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_z.serialize(out, child, "encoded deltas");
	written_bytes += m_sample_vals_and_pointer.serialize(out, child, "samples_and_pointers");
	structure_tree::add_size(child, written_bytes);
	return written_bytes;
}

template <class t_coder, uint32_t t_dens, uint8_t t_width>
void enc_vector<t_coder, t_dens, t_width>::load(std::istream& in)
{
	read_member(m_size, in);
	m_z.load(in);
	m_sample_vals_and_pointer.load(in);
}

template <class t_coder, uint32_t t_dens, uint8_t t_width>
template <typename archive_t>
void enc_vector<t_coder, t_dens, t_width>::CEREAL_SAVE_FUNCTION_NAME(archive_t & ar) const
{
	ar(CEREAL_NVP(m_size));
	ar(CEREAL_NVP(m_z));
	ar(CEREAL_NVP(m_sample_vals_and_pointer));
}

template <class t_coder, uint32_t t_dens, uint8_t t_width>
template <typename archive_t>
void enc_vector<t_coder, t_dens, t_width>::CEREAL_LOAD_FUNCTION_NAME(archive_t & ar)
{
	ar(CEREAL_NVP(m_size));
	ar(CEREAL_NVP(m_z));
	ar(CEREAL_NVP(m_sample_vals_and_pointer));
}

} // end namespace sdsl
#endif
