// 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 <sdsl/coder.hpp>
#include <sdsl/int_vector.hpp>
#include <sdsl/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
