/*
 * NumberModuloAutomaton.cpp
 *
 *  Created on: 9. 2. 2017
 *	  Author: Jan Travnicek
 */

#include "NumberModuloAutomaton.h"
#include <exception/CommonException.h>
#include <registration/AlgoRegistration.hpp>

namespace automaton::generate {

automaton::DFA < std::string, unsigned > NumberModuloAutomaton::generate ( unsigned base, unsigned modulo, unsigned result_modulo ) {
	if ( modulo == 0 )
		throw exception::CommonException ( "Modulo is zero." );

	if ( base > 36 )
		throw exception::CommonException ( "Number base " + ext::to_string ( base ) + " not handled." );

	if ( result_modulo >= modulo )
		throw exception::CommonException ( "Result modulo bigger than modulo limit." );

	std::vector < std::string > base_map;
	for ( unsigned i = 0; i < std::min ( 10u, base ); ++ i )
		base_map.push_back ( ext::to_string ( i ) );

	for ( unsigned i = 10; i < base; ++ i )
		base_map.emplace_back ( 'A' + i, 1 );

	automaton::DFA < std::string, unsigned > res ( 0 );

	for ( const std::string & input : base_map )
		res.addInputSymbol ( input );

	for ( unsigned modulo_state = 0; modulo_state < modulo; ++ modulo_state )
		res.addState ( modulo_state );

	for ( unsigned modulo_state = 0; modulo_state < modulo; ++ modulo_state )
		for ( unsigned input = 0; input < base; ++ input )
			res.addTransition ( modulo_state, base_map.at ( input ), ( modulo_state * base + input ) % modulo );

	res.addFinalState ( result_modulo );

	return res;
}

} /* automaton::generate */

namespace {

auto NumberModuloAutomatonDFA = registration::AbstractRegister < automaton::generate::NumberModuloAutomaton, automaton::DFA < std::string, unsigned >, unsigned, unsigned, unsigned > ( automaton::generate::NumberModuloAutomaton::generate, "base", "modulo", "result_modulo" ).setDocumentation (
"Generates an automaton recognising numbers in given base that have modulo equal to a concrete value.\n\
\n\
@param base the base of read number\n\
@param modulo the requested modulo\n\
@param result_modulo the final modulo" );

} /* namespace */
