#ifndef ARITHMETIC_MODEL_H_
#define ARITHMETIC_MODEL_H_

#include <stdexcept>
#include <alib/map>
#include <alib/set>

template < class SymbolType >
class ArithmeticModel {
	ext::map < SymbolType, unsigned > m_high_cumulative_frequency;
	unsigned m_global_high; // EOF is with probability 1/n (ie its low is m_global_high - 1 and hight is m_global_high) with cumulative frequency right below the m_global_high

public:
	ArithmeticModel ( const ext::set < SymbolType > & alphabet ) {
		unsigned frequency = 0;
		for ( const SymbolType & symbol : alphabet )
			m_high_cumulative_frequency.insert ( std::make_pair ( symbol, ++ frequency ) );
		m_global_high = frequency + 1;
	}

	void update ( const SymbolType & symbol ) {
		for ( auto i = m_high_cumulative_frequency.find ( symbol ); i != m_high_cumulative_frequency.end ( ) ; ++ i )
			i->second += 1;
		m_global_high += 1;
	}

	void getProbability ( const SymbolType & c, unsigned & low_prob, unsigned & high_prob ) const {
		auto i = m_high_cumulative_frequency.find ( c );
		high_prob = i->second;
		low_prob = 0;

		if ( i != m_high_cumulative_frequency.begin ( ) )
			low_prob = std::prev ( i )->second;
	}

	void getProbabilityEof ( unsigned & low_prob, unsigned & high_prob ) const {
		low_prob = m_global_high - 1;
		high_prob = m_global_high;
	}

	SymbolType getChar ( unsigned scaled_value, unsigned & low_prob, unsigned & high_prob ) const {
		for ( auto i = m_high_cumulative_frequency.begin ( ); i != m_high_cumulative_frequency.end ( ); ++ i )
			if ( scaled_value < i->second ) {
				high_prob = i->second;
				low_prob = 0;

				if ( i != m_high_cumulative_frequency.begin ( ) )
					low_prob = std::prev ( i )->second;

				return i->first;
			}
		throw std::logic_error("error");
	}

	bool isEof ( unsigned scaled_value ) const {
		return scaled_value == m_global_high - 1;
	}

	unsigned getCount ( ) const {
		return m_global_high;
	}

};

#endif //#ifndef ARITHMETIC_MODEL_H_
