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

#ifndef _AUTOMATON_RUN_H__
#define _AUTOMATON_RUN_H__

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

#include <automaton/FSM/DFA.h>
#include <automaton/FSM/NFA.h>
#include <automaton/FSM/EpsilonNFA.h>
#include <automaton/TA/DFTA.h>
#include <automaton/TA/NFTA.h>
#include <automaton/TA/UnorderedDFTA.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 <global/GlobalData.h>

#include <label/FailStateLabel.h>

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

#include <automaton/properties/EpsilonClosure.h>

namespace automaton {

namespace run {

/**
 * \brief
 * Implementation of automaton run over its input.
 */
class Run {
	/**
	 * Recursive implementation of automaton run over a tree.
	 *
	 * \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
	 * \param occ the set of collected occurrences
	 * \param i the index to the tree following the postorder traversal
	 *
	 * \return pair of
	 *	true if the automaton does not fail to find transitions, false otherwise
	 *	state where the computation ended
	 */
	template < class SymbolType, class StateType >
	static ext::pair < bool, StateType > calculateState ( const automaton::DFTA < SymbolType, StateType > & automaton, const ext::tree < common::ranked_symbol < SymbolType > > & node, ext::set < unsigned > & occ, unsigned & i );

	/**
	 * Recursive implementation of automaton run over a tree.
	 *
	 * \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
	 * \param occ the set of collected occurrences
	 * \param i the index to the tree following the postorder traversal
	 *
	 * \return pair of
	 *	true if the automaton does not fail to find transitions, false otherwise
	 *	state where the computation ended
	 */
	template < class SymbolType, class StateType >
	static ext::pair < bool, StateType > calculateState ( const automaton::UnorderedDFTA < SymbolType, StateType > & automaton, const ext::tree < common::ranked_symbol < SymbolType > > & node, ext::set < unsigned > & occ, unsigned & i );

	/**
	 * \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
	 * \param occ the set of collected occurrences
	 * \param i the index to the tree following the postorder traversal
	 *
	 * \return pair of
	 *	true if the automaton does not fail to find transitions, false otherwise
	 *	state where the computation ended
	 */
	template < class SymbolType, class StateType >
	static ext::pair < bool, ext::set < StateType > > calculateStates ( const automaton::NFTA < SymbolType, StateType > & automaton, const ext::tree < common::ranked_symbol < SymbolType > > & node, ext::set < unsigned > & occ, unsigned & i );

	/**
	 * Test whether a pushdown store contains symbols to pop.
	 *
	 * \tparam SymbolType type of pushdown store symbols
	 *
	 * \param pushdownStore the pushdown store to test
	 * \param pop symbols tested to be on top of the pushdown store
	 *
	 * \return true if the pushdown store contains pop symbols as its top symbols, false otherwise
	 */
	template < class SymbolType >
	static bool canPop ( const ext::deque < SymbolType > & pushdownStore, const ext::vector < SymbolType > & pop );

public:
	/**
	 * General automaton run implementation.
	 *
	 * \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 tuple of
	 *	true if the automaton does not fail to find transitions, false otherwise
	 *	state where the run stopped
	 *	set of indexes to the string where the automaton passed a final state
	 */
	template < class SymbolType, class StateType >
	static ext::tuple < bool, StateType, ext::set < unsigned > > calculateState ( 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 tuple of
	 *	true if the automaton does not fail to find transitions, false otherwise
	 *	set of states where the run stopped
	 *	set of indexes to the string where the automaton passed a final state
	 */
	template < class SymbolType, class StateType >
	static ext::tuple < bool, ext::set < StateType >, ext::set < unsigned > > calculateStates ( 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 tuple of
	 *	true if the automaton does not fail to find transitions, false otherwise
	 *	set of states where the run stopped
	 *	set of indexes to the string where the automaton passed a final state
	 */
	template < class SymbolType, class StateType >
	static ext::tuple < bool, ext::set < StateType >, ext::set < unsigned > > calculateStates ( 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 tuple of
	 *	true if the automaton does not fail to find transitions, false otherwise
	 *	state where the run stopped
	 *	set of indexes to the tree where the automaton passed a final state (as in the postorder traversal)
	 */
	template < class SymbolType, class StateType >
	static ext::tuple < bool, StateType, ext::set < unsigned > > calculateState ( 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 tuple of
	 *	true if the automaton does not fail to find transitions, false otherwise
	 *	state where the run stopped
	 *	set of indexes to the tree where the automaton passed a final state (as in the postorder traversal)
	 */
	template < class SymbolType, class StateType >
	static ext::tuple < bool, StateType, ext::set < unsigned > > calculateState ( const automaton::UnorderedDFTA < SymbolType, StateType > & automaton, const tree::UnorderedRankedTree < 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 tuple of
	 *	true if the automaton does not fail to find transitions, false otherwise
	 *	set of states where the run stopped
	 *	set of indexes to the tree where the automaton passed a final state (as in the postorder traversal)
	 */
	template < class SymbolType, class StateType >
	static ext::tuple < bool, ext::set < StateType >, ext::set < unsigned > > calculateStates ( 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 tuple of
	 *	true if the automaton does not fail to find transitions, false otherwise
	 *	state where the run stopped
	 *	set of indexes to the tree where the automaton passed a final state
	 *	deque representing the final content of the pushdown store
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static ext::tuple < bool, StateType, ext::set < unsigned >, ext::deque < PushdownStoreSymbolType > > calculateState ( 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 tuple of
	 *	true if the automaton does not fail to find transitions, false otherwise
	 *	state where the run stopped
	 *	set of indexes to the tree where the automaton passed a final
	 *	deque representing the final content of the pushdown store
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static ext::tuple < bool, StateType, ext::set < unsigned >, ext::deque < PushdownStoreSymbolType > > calculateState ( 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 tuple of
	 *	true if the automaton does not fail to find transitions, false otherwise
	 *	state where the run stopped
	 *	set of indexes to the tree where the automaton passed a final state
	 *	deque representing the final content of the pushdown store
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static ext::tuple < bool, StateType, ext::set < unsigned >, ext::deque < PushdownStoreSymbolType > > calculateState ( 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 tuple of
	 *	true if the automaton does not fail to find transitions, false otherwise
	 *	state where the run stopped
	 *	set of indexes to the tree where the automaton passed a final state
	 *	deque representing the final content of the pushdown store
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static ext::tuple < bool, StateType, ext::set < unsigned >, ext::deque < PushdownStoreSymbolType > > calculateState ( const automaton::DPDA < 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 tuple of
	 *	true if the automaton does not fail to find transitions, false otherwise
	 *	set of states where the run stopped
	 *	set of symbol vectors representing the translations
	 */
	template < class InputSymbolType, class OutputSymbolType, class PushdownStoreSymbolType, class StateType >
	static ext::tuple < bool, ext::set < StateType >, ext::set < ext::vector < OutputSymbolType > > > calculateStates ( const automaton::NPDTA < InputSymbolType, OutputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string );
};

// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

template < class SymbolType, class StateType >
ext::tuple < bool, StateType, ext::set < unsigned > > Run::calculateState ( const automaton::DFA < SymbolType, StateType > & automaton, const string::LinearString < SymbolType > & string ) {
	unsigned i = 0;
	ext::set < unsigned > occurrences;
	StateType state = automaton.getInitialState ( );

	for ( const SymbolType & symbol : string.getContent ( ) ) {
		if ( automaton.getFinalStates ( ).count ( state ) )
			occurrences.insert ( i );

		if ( common::GlobalData::verbose )
			common::Streams::log << state << std::endl;

		auto transition = automaton.getTransitions ( ).find ( ext::make_pair ( state, symbol ) );

		if ( transition == automaton.getTransitions ( ).end ( ) )
			return ext::make_tuple ( false, state, occurrences );

		state = transition->second;
		i++;
	}

	if ( automaton.getFinalStates ( ).count ( state ) )
		occurrences.insert ( i );

	if ( common::GlobalData::verbose )
		common::Streams::log << state << std::endl;

	return ext::make_tuple ( true, state, occurrences );
}

// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

template < class SymbolType, class StateType >
ext::tuple < bool, ext::set < StateType >, ext::set < unsigned > > Run::calculateStates ( const automaton::NFA < SymbolType, StateType > & automaton, const string::LinearString < SymbolType > & string ) {
	unsigned i = 0;
	ext::set < unsigned > occurrences;
	ext::set < StateType > states {
		automaton.getInitialState ( )
	};

	for ( const StateType & state : states )
		if ( automaton.getFinalStates ( ).count ( state ) )
			occurrences.insert ( i );

	if ( common::GlobalData::verbose )
		common::Streams::log << states << std::endl;

	for ( const SymbolType & symbol : string.getContent ( ) ) {
		ext::set < StateType > next;

		for ( const StateType & state : states ) {
			auto transitions = automaton.getTransitions ( ).equal_range ( ext::make_pair ( state, symbol ) );

			for ( const auto & transition : transitions )
				next.insert ( transition.second );
		}

		i++;
		states = next;

		for ( const StateType & state : states )
			if ( automaton.getFinalStates ( ).count ( state ) )
				occurrences.insert ( i );

		if ( common::GlobalData::verbose )
			common::Streams::log << states << std::endl;
	}

	return ext::make_tuple ( true, states, occurrences );
}

// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

template < class SymbolType, class StateType >
ext::tuple < bool, ext::set < StateType >, ext::set < unsigned > > Run::calculateStates ( const automaton::EpsilonNFA < SymbolType, StateType > & automaton, const string::LinearString < SymbolType > & string ) {
	unsigned i = 0;
	ext::set < unsigned > occurrences;
	ext::set < StateType > states {
		automaton.getInitialState ( )
	};

	for ( const StateType & state : states )
		if ( automaton.getFinalStates ( ).count ( state ) )
			occurrences.insert ( i );

	if ( common::GlobalData::verbose )
		common::Streams::log << states << std::endl;

	for ( const SymbolType & symbol : string.getContent ( ) ) {
		ext::set < StateType > next;

		for ( const StateType & state : states ) {
			auto transitions = automaton.getTransitions ( ).equal_range ( ext::make_pair ( state, common::symbol_or_epsilon < SymbolType > ( symbol ) ) );

			if ( transitions.empty ( ) ) continue;

			for ( const auto & transition : transitions )
				next.insert ( transition.second );
		}

		ext::set < StateType > epsilonNext;

		for ( const StateType & state : next ) {
			const ext::set < StateType > closure = automaton::properties::EpsilonClosure::epsilonClosure ( automaton, state );
			epsilonNext.insert ( closure.begin ( ), closure.end ( ) );
		}

		i++;
		states = epsilonNext;

		for ( const StateType & state : states )
			if ( automaton.getFinalStates ( ).count ( state ) )
				occurrences.insert ( i );

		if ( common::GlobalData::verbose )
			common::Streams::log << states << std::endl;
	}

	return ext::make_tuple ( true, states, occurrences );
}

// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

template < class SymbolType, class StateType >
ext::pair < bool, StateType > Run::calculateState ( const automaton::DFTA < SymbolType, StateType > & automaton, const ext::tree < common::ranked_symbol < SymbolType > > & node, ext::set < unsigned > & occ, unsigned & i ) {
	ext::vector < StateType > states;

	states.reserve ( ( size_t ) node.getData ( ).getRank ( ) );

	unsigned tmp = i;
	i++;

	bool sign = true;

	for ( const ext::tree < common::ranked_symbol < SymbolType > > & child : node.getChildren ( ) ) {
		ext::pair < bool, StateType > res = calculateState ( automaton, child, occ, i );

		if ( res.first == false )
			sign = false;
		else
			states.push_back ( res.second );
	}

	if ( !sign ) return ext::make_pair ( false, label::FailStateLabel::instance < StateType > ( ) );

	const auto & it = automaton.getTransitions ( ).find ( ext::make_pair ( node.getData ( ), states ) );

	if ( it == automaton.getTransitions ( ).end ( ) ) return ext::make_pair ( false, label::FailStateLabel::instance < StateType > ( ) );

	StateType state = it->second;

	if ( automaton.getFinalStates ( ).count ( state ) ) occ.insert ( tmp );

	if ( common::GlobalData::verbose )
		common::Streams::log << state << std::endl;

	return ext::make_pair ( true, state );
}

template < class SymbolType, class StateType >
ext::tuple < bool, StateType, ext::set < unsigned > > Run::calculateState ( const automaton::DFTA < SymbolType, StateType > & automaton, const tree::RankedTree < SymbolType > & tree ) {
	ext::set < unsigned > occ;
	unsigned i = 0;
	ext::pair < bool, StateType > res = calculateState ( automaton, tree.getContent ( ), occ, i );

	return ext::make_tuple ( res.first, res.second, occ );
}

// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


template < class SymbolType, class StateType >
ext::pair < bool, StateType > Run::calculateState ( const automaton::UnorderedDFTA < SymbolType, StateType > & automaton, const ext::tree < common::ranked_symbol < SymbolType > > & node, ext::set < unsigned > & occ, unsigned & i ) {
	ext::multiset < StateType > states;

	unsigned tmp = i;
	i++;

	bool sign = true;

	for ( const ext::tree < common::ranked_symbol < SymbolType > > & child : node.getChildren ( ) ) {
		ext::pair < bool, StateType > res = calculateState ( automaton, child, occ, i );

		if ( res.first == false )
			sign = false;
		else
			states.insert ( res.second );
	}

	if ( !sign ) return ext::make_pair ( false, label::FailStateLabel::instance < StateType > ( ) );

	const auto & it = automaton.getTransitions ( ).find ( ext::make_pair ( node.getData ( ), states ) );

	if ( it == automaton.getTransitions ( ).end ( ) ) return ext::make_pair ( false, label::FailStateLabel::instance < StateType > ( ) );

	StateType state = it->second;

	if ( automaton.getFinalStates ( ).count ( state ) ) occ.insert ( tmp );

	if ( common::GlobalData::verbose )
		common::Streams::log << state << std::endl;

	return ext::make_pair ( true, state );
}

template < class SymbolType, class StateType >
ext::tuple < bool, StateType, ext::set < unsigned > > Run::calculateState ( const automaton::UnorderedDFTA < SymbolType, StateType > & automaton, const tree::UnorderedRankedTree < SymbolType > & tree ) {
	ext::set < unsigned > occ;
	unsigned i = 0;
	ext::pair < bool, StateType > res = calculateState ( automaton, tree.getContent ( ), occ, i );

	return ext::make_tuple ( res.first, res.second, occ );
}

// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

template < class SymbolType, class StateType >
ext::pair < bool, ext::set < StateType > > Run::calculateStates ( const automaton::NFTA < SymbolType, StateType > & automaton, const ext::tree < common::ranked_symbol < SymbolType > > & node, ext::set < unsigned > & occ, unsigned & i ) {
	ext::vector < ext::set < StateType > > resStates;

	resStates.reserve ( ( size_t ) node.getData ( ).getRank ( ) );

	unsigned tmp = i;
	i++;

	bool sign = true;

	for ( const ext::tree < common::ranked_symbol < SymbolType > > & child : node.getChildren ( ) ) {
		std::pair < bool, ext::set < StateType > > childStates = calculateStates ( automaton, child, occ, i );

		if ( childStates.second.empty ( ) )
			sign = false;

		resStates.push_back ( childStates.second );
	}

	ext::set < StateType > states;

	for ( const auto & transition : automaton.getTransitions ( ) ) {
		if ( transition.first.first != node.getData ( ) )
			continue;

		unsigned rank = ( unsigned ) transition.first.first.getRank ( );
		unsigned j;

		for ( j = 0; j < rank; j++ )
			if ( !resStates[j].count ( transition.first.second[j] ) )
				break;

		if ( j == rank )
			states.insert ( transition.second );
	}

	for ( const StateType & state : states )
		if ( automaton.getFinalStates ( ).count ( state ) ) occ.insert ( tmp );

	if ( common::GlobalData::verbose )
		common::Streams::log << states << std::endl;

	return ext::make_pair ( sign, states );
}

template < class SymbolType, class StateType >
ext::tuple < bool, ext::set < StateType >, ext::set < unsigned > > Run::calculateStates ( const automaton::NFTA < SymbolType, StateType > & automaton, const tree::RankedTree < SymbolType > & tree ) {
	ext::set < unsigned > occ;
	unsigned i = 0;
	ext::pair < bool, ext::set < StateType > > res = calculateStates ( automaton, tree.getContent ( ), occ, i );

	return ext::make_tuple ( res.first, res.second, occ );
}

// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

template < class SymbolType >
bool Run::canPop ( const ext::deque < SymbolType > & pushdownStore, const ext::vector < SymbolType > & pop ) {
	unsigned popSize = pop.size ( );
	unsigned pushdownStoreSize = pushdownStore.size ( );

	if ( pushdownStoreSize < popSize ) return false;

	for ( unsigned i = 0; i < popSize; i++ )
		if ( pop[i] != pushdownStore[pushdownStoreSize - i - 1] )
			return false;

	return true;
}

// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
ext::tuple < bool, StateType, ext::set < unsigned >, ext::deque < PushdownStoreSymbolType > > Run::calculateState ( const automaton::InputDrivenDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string ) {
	StateType state = automaton.getInitialState ( );
	ext::deque < PushdownStoreSymbolType > pushdownStore {
		automaton.getInitialSymbol ( )
	};
	unsigned i = 0;
	ext::set < unsigned > occ;

	for ( const InputSymbolType & symbol : string.getContent ( ) ) {
		if ( automaton.getFinalStates ( ).count ( state ) )
			occ.insert ( i );

		if ( common::GlobalData::verbose )
			common::Streams::log << state << std::endl;

		auto transition = automaton.getTransitions ( ).find ( ext::make_pair ( state, symbol ) );

		if ( transition == automaton.getTransitions ( ).end ( ) )
			return ext::make_tuple ( false, state, occ, pushdownStore );

		const std::pair < ext::vector < PushdownStoreSymbolType >, ext::vector < PushdownStoreSymbolType > > & operation = automaton.getPushdownStoreOperations ( ).find ( symbol )->second;

		if ( !canPop ( pushdownStore, operation.first ) )
			return ext::make_tuple ( false, state, occ, pushdownStore );

		for ( unsigned j = 0; j < operation.first.size ( ); j++ ) pushdownStore.pop_back ( );

		for ( const auto & push : ext::make_reverse ( operation.second ) ) pushdownStore.push_back ( push );

		state = transition->second;
		i++;
	}

	if ( automaton.getFinalStates ( ).count ( state ) )
		occ.insert ( i );

	if ( common::GlobalData::verbose )
		common::Streams::log << state << std::endl;

	return ext::make_tuple ( true, state, occ, pushdownStore );
}

// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
ext::tuple < bool, StateType, ext::set < unsigned >, ext::deque < PushdownStoreSymbolType > > Run::calculateState ( const automaton::VisiblyPushdownDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string ) {
	StateType state = automaton.getInitialState ( );
	ext::deque < PushdownStoreSymbolType > pushdownStore {
		automaton.getBottomOfTheStackSymbol ( )
	};
	unsigned i = 0;
	ext::set < unsigned > occ;

	for ( const InputSymbolType & symbol : string.getContent ( ) ) {
		if ( automaton.getFinalStates ( ).count ( state ) )
			occ.insert ( i );

		if ( common::GlobalData::verbose )
			common::Streams::log << state << std::endl;

		if ( automaton.getCallInputAlphabet ( ).count ( symbol ) ) {
			auto transition = automaton.getCallTransitions ( ).find ( ext::make_pair ( state, symbol ) );

			if ( transition == automaton.getCallTransitions ( ).end ( ) )
				return ext::make_tuple ( false, state, occ, pushdownStore );

			pushdownStore.push_back ( transition->second.second );
			state = transition->second.first;
		} else if ( automaton.getReturnInputAlphabet ( ).count ( symbol ) ) {
			auto transition = automaton.getReturnTransitions ( ).find ( ext::make_tuple ( state, symbol, pushdownStore.back ( ) ) );

			if ( transition == automaton.getReturnTransitions ( ).end ( ) )
				return ext::make_tuple ( false, state, occ, pushdownStore );

			if ( pushdownStore.back ( ) != automaton.getBottomOfTheStackSymbol ( ) ) pushdownStore.pop_back ( );

			state = transition->second;
		} else if ( automaton.getLocalInputAlphabet ( ).count ( symbol ) ) {
			auto transition = automaton.getLocalTransitions ( ).find ( ext::make_pair ( state, symbol ) );

			if ( transition == automaton.getLocalTransitions ( ).end ( ) )
				return ext::make_tuple ( false, state, occ, pushdownStore );

			state = transition->second;
		} else {
			return ext::make_tuple ( false, state, occ, pushdownStore );
		}

		i++;
	}

	if ( automaton.getFinalStates ( ).count ( state ) )
		occ.insert ( i );

	if ( common::GlobalData::verbose )
		common::Streams::log << state << std::endl;

	return ext::make_tuple ( true, state, occ, pushdownStore );
}

// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
ext::tuple < bool, StateType, ext::set < unsigned >, ext::deque < PushdownStoreSymbolType > > Run::calculateState ( const automaton::RealTimeHeightDeterministicDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string ) {
	StateType state = automaton.getInitialState ( );
	ext::deque < PushdownStoreSymbolType > pushdownStore {
		automaton.getBottomOfTheStackSymbol ( )
	};
	unsigned i = 0;
	ext::set < unsigned > occ;

	if ( automaton.getFinalStates ( ).count ( state ) )
		occ.insert ( i );

	if ( common::GlobalData::verbose )
		common::Streams::log << state << std::endl;

	for ( auto symbolIter = string.getContent ( ).begin ( ); symbolIter != string.getContent ( ).end ( ); ) {

		bool transitionFound = false;

		auto callTransition = automaton.getCallTransitions ( ).find ( std::pair < StateType, common::symbol_or_epsilon < InputSymbolType > > ( state, * symbolIter ) );

		if ( callTransition != automaton.getCallTransitions ( ).end ( ) ) {
			symbolIter++;
			i++;
		} else {
			callTransition = automaton.getCallTransitions ( ).find ( std::make_pair ( state, common::symbol_or_epsilon < InputSymbolType > ( ) ) );
		}

		if ( callTransition != automaton.getCallTransitions ( ).end ( ) ) {
			pushdownStore.push_back ( callTransition->second.second );
			state = callTransition->second.first;
			transitionFound = true;
		}

		if ( ! transitionFound ) {
			auto returnTransition = automaton.getReturnTransitions ( ).find ( std::tuple < StateType, common::symbol_or_epsilon < InputSymbolType >, PushdownStoreSymbolType > ( state, * symbolIter, pushdownStore.back ( ) ) );

			if ( returnTransition != automaton.getReturnTransitions ( ).end ( ) ) {
				symbolIter++;
				i++;
			} else {
				returnTransition = automaton.getReturnTransitions ( ).find ( std::make_tuple ( state, common::symbol_or_epsilon < InputSymbolType > ( ), pushdownStore.back ( ) ) );
			}

			if ( returnTransition != automaton.getReturnTransitions ( ).end ( ) ) {
				pushdownStore.pop_back ( );
				state = returnTransition->second;
				transitionFound = true;
			}
		}

		if ( ! transitionFound ) {
			auto localTransition = automaton.getLocalTransitions ( ).find ( std::pair < StateType, common::symbol_or_epsilon < InputSymbolType > > ( state, * symbolIter ) );

			if ( localTransition != automaton.getLocalTransitions ( ).end ( ) ) {
				symbolIter++;
				i++;
			} else {
				localTransition = automaton.getLocalTransitions ( ).find ( std::make_pair ( state, common::symbol_or_epsilon < InputSymbolType > ( ) ) );
			}

			if ( localTransition != automaton.getLocalTransitions ( ).end ( ) ) {
				state = localTransition->second;
				transitionFound = true;
			}
		}

		if ( ! transitionFound )
			return ext::make_tuple ( false, state, occ, pushdownStore );

		if ( automaton.getFinalStates ( ).count ( state ) )
			occ.insert ( i );

		if ( common::GlobalData::verbose )
			common::Streams::log << state << std::endl;
	}

	while ( true ) {

		bool transitionFound = false;

		auto callTransition = automaton.getCallTransitions ( ).find ( std::make_pair ( state, common::symbol_or_epsilon < InputSymbolType > ( ) ) );

		if ( callTransition != automaton.getCallTransitions ( ).end ( ) ) {
			pushdownStore.push_back ( callTransition->second.second );
			state = callTransition->second.first;
			transitionFound = true;
		}

		if ( ! transitionFound ) {
			auto returnTransition = automaton.getReturnTransitions ( ).find ( std::make_tuple ( state, common::symbol_or_epsilon < InputSymbolType > ( ), pushdownStore.back ( ) ) );

			if ( returnTransition != automaton.getReturnTransitions ( ).end ( ) ) {
				pushdownStore.pop_back ( );
				state = returnTransition->second;
				transitionFound = true;
			}
		}

		if ( ! transitionFound ) {
			auto localTransition = automaton.getLocalTransitions ( ).find ( std::make_pair ( state, common::symbol_or_epsilon < InputSymbolType > ( ) ) );

			if ( localTransition != automaton.getLocalTransitions ( ).end ( ) ) {
				state = localTransition->second;
				transitionFound = true;
			}
		}

		if ( ! transitionFound )
			break;

		if ( automaton.getFinalStates ( ).count ( state ) )
			occ.insert ( i );

		if ( common::GlobalData::verbose )
			common::Streams::log << state << std::endl;
	}

	return ext::make_tuple ( true, state, occ, pushdownStore );
}

// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
ext::tuple < bool, StateType, ext::set < unsigned >, ext::deque < PushdownStoreSymbolType > > Run::calculateState ( const automaton::DPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string ) {
	StateType state = automaton.getInitialState ( );
	ext::deque < PushdownStoreSymbolType > pushdownStore {
		automaton.getInitialSymbol ( )
	};
	unsigned i = 0;
	ext::set < unsigned > occ;

	for ( auto symbolIter = string.getContent ( ).begin ( ); symbolIter != string.getContent ( ).end ( ); ) {

		if ( automaton.getFinalStates ( ).count ( state ) )
			occ.insert ( i );

		if ( common::GlobalData::verbose ) {
			common::Streams::log << "State : " << state << std::endl;
			common::Streams::log << "PushdownStore : " << pushdownStore << std::endl;
		}

		auto transitions = automaton.getTransitionsFromState ( state );
		auto transition	 = transitions.begin ( );

		for ( ; transition != transitions.end ( ); transition++ ) {
			if ( ( std::get < 1 > ( transition->first ) != * symbolIter ) && !std::get < 1 > ( transition->first ).is_epsilon ( ) ) continue;

			if ( canPop ( pushdownStore, std::get < 2 > ( transition->first ) ) ) break;
		}

		if ( transition == transitions.end ( ) )
			return ext::make_tuple ( false, state, occ, pushdownStore );

		if ( common::GlobalData::verbose )
			common::Streams::log << "Transition: " << transition->first << " to " << transition->second << std::endl;

		for ( unsigned j = 0; j < std::get < 2 > ( transition->first ).size ( ); j++ ) pushdownStore.pop_back ( );

		for ( const auto & symbol : ext::make_reverse ( transition->second.second ) ) pushdownStore.push_back ( symbol );

		state = transition->second.first;

		if ( !std::get < 1 > ( transition->first ).is_epsilon ( ) ) {
			i++;
			symbolIter++;
		}
	}

	if ( automaton.getFinalStates ( ).count ( state ) )
		occ.insert ( i );

	if ( common::GlobalData::verbose ) {
		common::Streams::log << "State: " << state << std::endl;
		common::Streams::log << "PushdownStore: " << pushdownStore << std::endl;
	}

	return ext::make_tuple ( true, state, occ, pushdownStore );
}

// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

template < class SymbolType >
struct graphStructuredStack {
	graphStructuredStack ( std::shared_ptr < graphStructuredStack < SymbolType > > parent, SymbolType data ) : m_parent(parent), m_data(data) {}
	std::shared_ptr < graphStructuredStack < SymbolType > > m_parent;
	SymbolType m_data;
};

template < class InputSymbolType, class OutputSymbolType, class PushdownStoreSymbolType, class StateType >
ext::tuple < bool, ext::set < StateType >, ext::set < ext::vector < OutputSymbolType > > > Run::calculateStates ( const automaton::NPDTA < InputSymbolType, OutputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string ) {
	bool res = false;
	StateType state = automaton . getInitialState ();
	typename ext::vector<InputSymbolType>::const_iterator symbolIter = string . getContent () . begin ();
	std::shared_ptr < graphStructuredStack < InputSymbolType > > stackNode = std::make_shared < graphStructuredStack < InputSymbolType > > ( nullptr, * symbolIter );
	std::shared_ptr < graphStructuredStack < InputSymbolType > > outputNode = std::make_shared < graphStructuredStack < InputSymbolType > > ( nullptr, * symbolIter );

	ext::deque < ext::tuple < StateType, typename ext::vector<InputSymbolType>::const_iterator, std::shared_ptr < graphStructuredStack < InputSymbolType > >, std::shared_ptr < graphStructuredStack < InputSymbolType > > > > bftQueue;
	auto configuration = ext::make_tuple ( state, symbolIter, stackNode, outputNode );
	bftQueue . push_back ( configuration );

	ext::set < ext::vector < OutputSymbolType > > allOutputs;
	ext::set < StateType > states;

	while ( ! bftQueue . empty () ) {
		configuration = bftQueue . front (); bftQueue . pop_front ();
		state = std::get<0>(configuration);
		symbolIter = std::get<1>(configuration);
		stackNode = std::get<2>(configuration);
		outputNode = std::get<3>(configuration);

		if ( symbolIter == string . getContent () . end () ) {
			states . insert ( state );
			if ( automaton . getFinalStates () . count ( state ) && stackNode -> m_parent == nullptr ) {
				res = true;
				ext::vector < OutputSymbolType > output;
				while ( outputNode->m_parent != nullptr ) {
					output . push_back ( outputNode->m_data );
					outputNode = outputNode -> m_parent;
				}
				std::reverse ( output.begin(), output.end() );
				allOutputs . insert ( output );
			}
			continue;
		}

		for ( const auto & transition : automaton . getTransitionsFromState ( state ) ) {
			if ( std::get<1>(transition . first) != * symbolIter && ! std::get<1>(transition . first) . is_epsilon ( ) )
				continue;

			const auto & pop = std::get<2>(transition . first);
			std::shared_ptr < graphStructuredStack < InputSymbolType > > stackNodeCopy = stackNode;
			std::shared_ptr < graphStructuredStack < InputSymbolType > > outputNodeCopy = outputNode;

			unsigned j = 0;
			for ( ; j < pop . size (); j ++ ) {
				if ( stackNodeCopy->m_parent == nullptr || stackNodeCopy->m_data != pop . at (j) ) {
					break;
				}
				stackNodeCopy = stackNodeCopy -> m_parent;
			}
			if ( j == pop . size () ) {
				for ( const auto & elem : ext::make_reverse ( std::get<1>(transition . second ) ) ) {
					stackNodeCopy = std::make_shared < graphStructuredStack < InputSymbolType > > ( stackNodeCopy, elem );
				}
				for ( const auto & elem : std::get<2>(transition . second) ) {
					outputNodeCopy = std::make_shared < graphStructuredStack < InputSymbolType > > ( outputNodeCopy, elem );
				}

				if ( ! std::get<1>(transition . first) . is_epsilon ( ) ) {
					configuration = ext::make_tuple ( std::get<0>(transition . second), symbolIter + 1, stackNodeCopy, outputNodeCopy );
				} else {
					configuration = ext::make_tuple ( std::get<0>(transition . second), symbolIter, stackNodeCopy, outputNodeCopy );
				}
				bftQueue . push_back ( configuration );
			} else {
				states . insert ( state );
			}
		}
	}

	return ext::make_tuple ( res, states, allOutputs );
}

// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

} /* namespace run */

} /* namespace automaton */

#endif /* _AUTOMATON_RUN_H__ */
