/*
 * Accept.h
 *
 *  Created on: 9. 2. 2014
 *      Author: Jan Travnicek
 */

#pragma once

#include <string/LinearString.h>
#include <tree/ranked/RankedTree.h>

#include "Run.h"
#include <automaton/FSM/DFA.h>
#include <automaton/FSM/NFA.h>
#include <automaton/TA/DFTA.h>
#include <automaton/TA/NFTA.h>
#include <automaton/PDA/InputDrivenDPDA.h>
#include <automaton/PDA/VisiblyPushdownDPDA.h>
#include <automaton/PDA/RealTimeHeightDeterministicDPDA.h>
#include <automaton/PDA/DPDA.h>
#include <automaton/PDA/NPDTA.h>

#include <alib/deque>
#include <alib/vector>
#include <alib/algorithm>

namespace automaton {

namespace run {

/**
 * \brief
 * Implementation of test whether automaton accepts its input.
 */
class Accept {
public:
	/**
	 * Automaton run implementation resulting in boolean true if the automaton accepted the input.
	 *
	 * \tparam SymbolType type of symbols of the string and terminal symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 *
	 * \return true if the automaton accepts given string
	 */
	template < class SymbolType, class StateType >
	static bool accept ( const automaton::DFA < SymbolType, StateType > & automaton, const string::LinearString < SymbolType > & string );

	/**
	 * \override
	 *
	 * \tparam SymbolType type of symbols of the string and terminal symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 *
	 * \return true if the automaton accepts given string
	 */
	template < class SymbolType, class StateType >
	static bool accept ( const automaton::NFA < SymbolType, StateType > & automaton, const string::LinearString < SymbolType > & string );

	/**
	 * \override
	 *
	 * \tparam SymbolType type of symbols of the string and terminal symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 *
	 * \return true if the automaton accepts given string
	 */
	template < class SymbolType, class StateType >
	static bool accept ( const automaton::EpsilonNFA < SymbolType, StateType > & automaton, const string::LinearString < SymbolType > & string );

	/**
	 * \override
	 *
	 * \tparam SymbolType type of symbols of tree nodes and terminal symbols of the runned automaton
	 * \tparam RankType type of ranks of tree nodes and terminal symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 *
	 * \return true if the automaton accepts given string
	 */
	template < class SymbolType, class StateType >
	static bool accept ( const automaton::DFTA < SymbolType, StateType > & automaton, const tree::RankedTree < SymbolType > & tree );

	/**
	 * \override
	 *
	 * \tparam SymbolType type of symbols of tree nodes and terminal symbols of the runned automaton
	 * \tparam RankType type of ranks of tree nodes and terminal symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 *
	 * \return true if the automaton accepts given string
	 */
	template < class SymbolType, class StateType >
	static bool accept ( const automaton::NFTA < SymbolType, StateType > & automaton, const tree::RankedTree < SymbolType > & tree );

	/**
	 * \override
	 *
	 * \tparam InputSymbolType type of symbols of the string and terminal symbols of the runned automaton
	 * \tparam PushdownStoreSymbolType type of pushdown store symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 *
	 * \return true if the automaton accepts given string
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static bool accept ( const automaton::InputDrivenDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string );

	/**
	 * \override
	 *
	 * \tparam InputSymbolType type of symbols of the string and terminal symbols of the runned automaton
	 * \tparam PushdownStoreSymbolType type of pushdown store symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 *
	 * \return true if the automaton accepts given string
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static bool accept ( const automaton::VisiblyPushdownDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string );

	/**
	 * \override
	 *
	 * \tparam InputSymbolType type of symbols of the string and terminal symbols of the runned automaton
	 * \tparam PushdownStoreSymbolType type of pushdown store symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 *
	 * \return true if the automaton accepts given string
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static bool accept ( const automaton::RealTimeHeightDeterministicDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string );

	/**
	 * \override
	 *
	 * \tparam InputSymbolType type of symbols of the string and terminal symbols of the runned automaton
	 * \tparam PushdownStoreSymbolType type of pushdown store symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 *
	 * \return true if the automaton accepts given string
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static bool accept ( const automaton::DPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string );

	/**
	 * \override
	 *
	 * \tparam InputSymbolType type of symbols of the string and terminal symbols of the runned automaton
	 * \tparam PushdownStoreSymbolType type of pushdown store symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 *
	 * \return true if the automaton accepts given string
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static bool accept ( const automaton::NPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string );

	/**
	 * \override
	 *
	 * \tparam InputSymbolType type of input symbols of the string and terminal symbols of the runned automaton
	 * \tparam OutputSymbolType type of output symbols of the runned automaton
	 * \tparam PushdownStoreSymbolType type of pushdown store symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 *
	 * \return true if the automaton accepts given string
	 */
	template < class InputSymbolType, class OutputSymbolType, class PushdownStoreSymbolType, class StateType >
	static bool accept ( const automaton::NPDTA < InputSymbolType, OutputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string );

};

template < class SymbolType, class StateType >
bool Accept::accept ( const automaton::DFA < SymbolType, StateType > & automaton, const string::LinearString < SymbolType > & string ) {
	ext::tuple < bool, StateType, ext::set < unsigned > > res = Run::calculateState ( automaton, string );

	return std::get < 0 > ( res ) && automaton.getFinalStates ( ).count ( std::get < 1 > ( res ) );
}

template < class SymbolType, class StateType >
bool Accept::accept ( const automaton::NFA < SymbolType, StateType > & automaton, const string::LinearString < SymbolType > & string ) {
	ext::tuple < bool, ext::set < StateType >, ext::set < unsigned > > res = Run::calculateStates ( automaton, string );

	return std::get < 0 > ( res ) && std::any_of ( std::get < 1 > ( res ).begin ( ), std::get < 1 > ( res ).end ( ), [&] ( const StateType & state ) {
				return automaton.getFinalStates ( ).count ( state );
			} );
}

template < class SymbolType, class StateType >
bool Accept::accept ( const automaton::EpsilonNFA < SymbolType, StateType > & automaton, const string::LinearString < SymbolType > & string ) {
	ext::tuple < bool, ext::set < StateType >, ext::set < unsigned > > res = Run::calculateStates ( automaton, string );

	return std::get < 0 > ( res ) && std::any_of ( std::get < 1 > ( res ).begin ( ), std::get < 1 > ( res ).end ( ), [&] ( const StateType & state ) {
				return automaton.getFinalStates ( ).count ( state );
			} );
}

template < class SymbolType, class StateType >
bool Accept::accept ( const automaton::DFTA < SymbolType, StateType > & automaton, const tree::RankedTree < SymbolType > & tree ) {
	ext::tuple < bool, StateType, ext::set < unsigned > > res = Run::calculateState ( automaton, tree );

	return std::get < 0 > ( res ) && automaton.getFinalStates ( ).count ( std::get < 1 > ( res ) );
}

template < class SymbolType, class StateType >
bool Accept::accept ( const automaton::NFTA < SymbolType, StateType > & automaton, const tree::RankedTree < SymbolType > & tree ) {
	ext::tuple < bool, ext::set < StateType >, ext::set < unsigned > > res = Run::calculateStates ( automaton, tree );

	return std::get < 0 > ( res ) && std::any_of ( std::get < 1 > ( res ).begin ( ), std::get < 1 > ( res ).end ( ), [&] ( const StateType & state ) {
				return automaton.getFinalStates ( ).count ( state );
			} );
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
bool Accept::accept ( const automaton::InputDrivenDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string ) {
	ext::tuple < bool, StateType, ext::set < unsigned >, ext::deque < PushdownStoreSymbolType > > res = Run::calculateState ( automaton, string );

	return std::get < 0 > ( res ) && ( automaton.getFinalStates ( ).count ( std::get < 1 > ( res ) ) || ( automaton.getFinalStates ( ).empty ( ) && std::get < 3 > ( res ).empty ( ) ) );
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
bool Accept::accept ( const automaton::VisiblyPushdownDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string ) {
	ext::tuple < bool, StateType, ext::set < unsigned >, ext::deque < PushdownStoreSymbolType > > res = Run::calculateState ( automaton, string );

	return std::get < 0 > ( res ) && automaton.getFinalStates ( ).count ( std::get < 1 > ( res ) );
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
bool Accept::accept ( const automaton::RealTimeHeightDeterministicDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string ) {
	ext::tuple < bool, StateType, ext::set < unsigned >, ext::deque < PushdownStoreSymbolType > > res = Run::calculateState ( automaton, string );

	return std::get < 0 > ( res ) && automaton.getFinalStates ( ).count ( std::get < 1 > ( res ) );
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
bool Accept::accept ( const automaton::DPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string ) {
	ext::tuple < bool, StateType, ext::set < unsigned >, ext::deque < PushdownStoreSymbolType > > res = Run::calculateState ( automaton, string );

	return std::get < 0 > ( res ) && ( automaton.getFinalStates ( ).count ( std::get < 1 > ( res ) ) || ( automaton.getFinalStates ( ).empty ( ) && std::get < 3 > ( res ).empty ( ) ) );
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
bool Accept::accept ( const automaton::NPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string ) {
	ext::set < ext::tuple < StateType, ext::set < unsigned >, ext::deque < PushdownStoreSymbolType > > > res = Run::calculateStates ( automaton, string );

	for ( const ext::tuple < StateType, ext::set < unsigned >, ext::deque < PushdownStoreSymbolType > > & finalConfiguration : res )
		if ( automaton.getFinalStates ( ).contains ( std::get < 0 > ( finalConfiguration ) ) || ( automaton.getFinalStates ( ).empty ( ) && std::get < 2 > ( finalConfiguration ).empty ( ) ) )
			return true;

	return false;
}

template < class InputSymbolType, class OutputSymbolType, class PushdownStoreSymbolType, class StateType >
bool Accept::accept ( const automaton::NPDTA < InputSymbolType, OutputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string ) {
	ext::set < ext::tuple < StateType, ext::set < unsigned >, ext::deque < PushdownStoreSymbolType >, ext::deque < OutputSymbolType > > > res = Run::calculateStates ( automaton, string );

	for ( const ext::tuple < StateType, ext::set < unsigned >, ext::deque < PushdownStoreSymbolType >, ext::deque < OutputSymbolType > > & finalConfiguration : res )
		if ( automaton.getFinalStates ( ).contains ( std::get < 0 > ( finalConfiguration ) ) || ( automaton.getFinalStates ( ).empty ( ) && std::get < 2 > ( finalConfiguration ).empty ( ) ) )
			return true;

	return false;
}

} /* namespace run */

} /* namespace automaton */

