#ifndef BITARRAYS_H
#define BITARRAYS_H 1

#include "bit_array.h"
#include <algorithm>
#include <cassert>
#include <functional>
#include <istream>
#include <limits> // for numeric_limits
#include <ostream>
#include <stdint.h>
#include <vector>

/** Store a string of symbols from a small alphabet using a vector of
 * BitArray.
 */
class BitArrays
{
	/** A symbol. */
	typedef uint8_t T;

	/** The sentinel symbol. */
	static T SENTINEL() { return std::numeric_limits<T>::max(); }

  public:
	/** Count the occurrences of the symbols of [first, last). */
	template<typename It>
	void assign(It first, It last)
	{
		assert(first < last);
		m_data.clear();

		// Determine the size of the alphabet ignoring the sentinel.
		T n = 0;
		for (It it = first; it != last; ++it)
			if (*it != SENTINEL())
				n = std::max(n, *it);
		n++;

		assert(n < std::numeric_limits<T>::max());
		m_data.resize(n, wat_array::BitArray(last - first));

		size_t i = 0;
		for (It it = first; it != last; ++it, ++i) {
			T c = *it;
			if (c == SENTINEL())
				continue;
			assert(c < m_data.size());
			m_data[c].SetBit(1, i);
		}

		std::for_each(
		    m_data.begin(), m_data.end(), [](wat_array::BitArray& b) { return b.Build(); });
	}

	/** Return the size of the string. */
	size_t size() const
	{
		assert(!m_data.empty());
		return m_data.front().length();
	}

	/** Return the number of occurrences of the specified symbol. */
	size_t count(T c) const { return m_data[c].one_num(); }

	/** Return the count of symbol c in s[0, i). */
	size_t rank(T c, size_t i) const { return m_data[c].Rank(1, i); }

	/** Return the symbol at the specified position. */
	T at(size_t i) const
	{
		assert(!m_data.empty());
		assert(i < m_data.front().length());
		for (Data::const_iterator it = m_data.begin(); it != m_data.end(); ++it)
			if (it->Lookup(i))
				return it - m_data.begin();
		return std::numeric_limits<T>::max();
	}

	/** Store this data structure. */
	friend std::ostream& operator<<(std::ostream& out, const BitArrays& o)
	{
		uint32_t n = o.m_data.size();
		out.write(reinterpret_cast<char*>(&n), sizeof n);
		for (Data::const_iterator it = o.m_data.begin(); it != o.m_data.end(); ++it)
			it->Save(out);
		return out;
	}

	/** Load this data structure. */
	friend std::istream& operator>>(std::istream& in, BitArrays& o)
	{
		o.m_data.clear();
		uint32_t n = 0;
		if (!in.read(reinterpret_cast<char*>(&n), sizeof n))
			return in;
		assert(n > 0);
		assert(n < std::numeric_limits<T>::max());
		o.m_data.resize(n);
		for (Data::iterator it = o.m_data.begin(); it != o.m_data.end(); ++it)
			it->Load(in);
		return in;
	}

  private:
	typedef std::vector<wat_array::BitArray> Data;
	Data m_data;
};

#endif
