/*
 * EpsilonRemoverOutgoing.h
 *
 *  Created on: 16. 1. 2014
 *	  Author: Jan Travnicek
 */

#ifndef EFFICIENT_EPSILON_REMOVER_OUTGOING_H_
#define EFFICIENT_EPSILON_REMOVER_OUTGOING_H_

#include <map>
#include <algorithm>

#include <automaton/FSM/EpsilonNFA.h>
#include <automaton/FSM/MultiInitialStateNFA.h>
#include <automaton/FSM/NFA.h>
#include <automaton/FSM/DFA.h>

#include "../../properties/efficient/AllEpsilonClosure.h"

namespace automaton {

namespace simplify {

namespace efficient {

class EpsilonRemoverOutgoing {
public:
	/**
	 * Computes epsilon closure of a state in epsilon nonfree automaton
	 */
	template < class StateType, class SymbolType >
	static automaton::MultiInitialStateNFA < SymbolType, StateType > remove ( const automaton::EpsilonNFA < SymbolType, StateType > & fsm );
	template < class StateType, class SymbolType >
	static automaton::MultiInitialStateNFA < SymbolType, StateType > remove ( const automaton::MultiInitialStateNFA < SymbolType, StateType > & fsm );
	template < class StateType, class SymbolType >
	static automaton::NFA < SymbolType, StateType > remove ( const automaton::NFA < SymbolType, StateType > & fsm );
	template < class StateType, class SymbolType >
	static automaton::DFA < SymbolType, StateType > remove ( const automaton::DFA < SymbolType, StateType > & fsm );
};

template < class StateType, class SymbolType >
automaton::DFA < SymbolType, StateType > EpsilonRemoverOutgoing::remove ( const automaton::DFA < SymbolType, StateType > & fsm) {
	return fsm;
}

template < class StateType, class SymbolType >
automaton::MultiInitialStateNFA < SymbolType, StateType > EpsilonRemoverOutgoing::remove ( const automaton::MultiInitialStateNFA < SymbolType, StateType > & fsm) {
	return fsm;
}

template < class StateType, class SymbolType >
automaton::NFA < SymbolType, StateType > EpsilonRemoverOutgoing::remove ( const automaton::NFA < SymbolType, StateType > & fsm) {
	return fsm;
}

template < class StateType, class SymbolType >
automaton::MultiInitialStateNFA < SymbolType, StateType > EpsilonRemoverOutgoing::remove ( const automaton::EpsilonNFA < SymbolType, StateType > & fsm ) {
	automaton::MultiInitialStateNFA < SymbolType, StateType > res;
	res.setStates ( fsm.getStates ( ) );
	res.setFinalStates ( fsm.getFinalStates ( ) );
	res.setInputAlphabet ( fsm.getInputAlphabet ( ) );

	ext::map < StateType, ext::set < StateType > > closures = automaton::properties::efficient::AllEpsilonClosure::allEpsilonClosure ( fsm );

	ext::multimap < ext::pair < StateType, SymbolType >, StateType > transitions = fsm.getSymbolTransitions ( );

	for ( const std::pair < const ext::pair < StateType, SymbolType >, StateType > & transition : transitions )
		for ( const StateType & toClosure : closures [ transition.second ] )
			res.addTransition ( transition.first.first, transition.first.second, toClosure );

	/**
	 * Step 2 from Melichar 2.41
	 */
	const ext::set < StateType > & cl = closures [ fsm.getInitialState ( ) ];
	res.setInitialStates ( cl );

	return res;
}

} /* namespace efficient */

} /* namespace simplify */

} /* namespace automaton */

#endif /* EFFICIENT_EPSILON_REMOVER_OUTGOING_H_ */
