/*
 * EpsilonRemoverIncoming.h
 *
 * This file is part of Algorithms library toolkit.
 * Copyright (C) 2017 Jan Travnicek (jan.travnicek@fit.cvut.cz)

 * Algorithms library toolkit is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * Algorithms library toolkit is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with Algorithms library toolkit.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  Created on: 24. 11. 2014
 *	  Author: Jan Travnicek
 */

#ifndef EPSILON_REMOVER_INCOMING_H_
#define EPSILON_REMOVER_INCOMING_H_

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

#include <automaton/properties/EpsilonClosure.h>

namespace automaton {

namespace simplify {

/**
 * Removes epsilon transitions from an automaton.
 * This method is the one teached at BI-AAG course.
 *
 * @sa automaton::simplify::EpsilonRemoverOutgoing
 */
class EpsilonRemoverIncoming {
public:
	/**
	 * Removes epsilon transitions from an automaton.
	 *
	 * @tparam SymbolType Type for input symbols.
	 * @tparam StateType Type for states.
	 * @param res automaton to remove epsilon transitions from
	 * @return an automaton equivalent to @p res but without epsilon transitions
	 */
	template < class SymbolType, class StateType >
	static automaton::NFA < SymbolType, StateType > remove( const automaton::EpsilonNFA < SymbolType, StateType > & fsm );

	/**
	 * @overload
	 */
	template < class SymbolType, class StateType >
	static automaton::MultiInitialStateNFA < SymbolType, StateType > remove( const automaton::MultiInitialStateNFA < SymbolType, StateType > & fsm );

	/**
	 * For nondeterministic finite automata, we remove nothing and return the @p res
	 *
	 * @overload
	 */
	template < class SymbolType, class StateType >
	static automaton::NFA < SymbolType, StateType > remove( const automaton::NFA < SymbolType, StateType > & fsm );

	/**
	 * For deterministic finite automata, we remove nothing and return the @p res
	 *
	 * @overload
	 */
	template < class SymbolType, class StateType >
	static automaton::DFA < SymbolType, StateType > remove( const automaton::DFA < SymbolType, StateType > & fsm );

};

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

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

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

template < class SymbolType, class StateType >
automaton::NFA < SymbolType, StateType > EpsilonRemoverIncoming::remove( const automaton::EpsilonNFA < SymbolType, StateType > & fsm ) {
	automaton::NFA < SymbolType, StateType > res(fsm.getInitialState());

	res.setStates( fsm.getStates() );
	res.setInputAlphabet( fsm.getInputAlphabet() );

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

	/**
	 * Step 1 from Melichar 2.41
	 */
	for( const auto & from : fsm.getStates( ) )
		for( const auto & fromClosure : automaton::properties::EpsilonClosure::epsilonClosure( fsm, from ) )
			for( const auto & symbol : fsm.getInputAlphabet() ) {
				auto transitions = origTransitions.equal_range(ext::make_pair(fromClosure, symbol));
				if(transitions.empty ( ) ) continue;

				for( const auto & transition : transitions )
					res.addTransition( from, symbol, transition.second );
			}

	/**
	 * Step 2 from Melichar 2.41
	 */
	const ext::set<StateType> & F = fsm.getFinalStates( );
	for( const auto & q : res.getStates( ) ) {
		const ext::set<StateType> & cl = automaton::properties::EpsilonClosure::epsilonClosure( fsm, q );

		if ( ! ext::excludes ( cl.begin(), cl.end(), F.begin(), F.end() ) )
			res.addFinalState( q );
	}

	return res;
}

} /* namespace simplify */

} /* namespace automaton */

#endif /* EPSILON_REMOVER_INCOMING_H_ */
