/*
 * UnreachableStatesRemover.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: 23. 3. 2014
 *	  Author: Tomas Pecka
 */

#ifndef UNREACHABLE_STATES_REMOVER_H_
#define UNREACHABLE_STATES_REMOVER_H_

#include <automaton/FSM/ExtendedNFA.h>
#include <automaton/FSM/CompactNFA.h>
#include <automaton/FSM/EpsilonNFA.h>
#include <automaton/FSM/MultiInitialStateNFA.h>
#include <automaton/FSM/NFA.h>
#include <automaton/FSM/DFA.h>
#include <automaton/TA/DFTA.h>

#include <automaton/properties/ReachableStates.h>

namespace automaton {

namespace simplify {

/**
 * Algorithm for the removal of unreachable states from a finite automaton and a deterministic finite tree automaton.
 * Unreachable state is a state that is not accessible from the initial state of the automaton by any string.
 *
 * @details
 * For a finite automaton, we implement Melichar: Jazyky a překlady, 2.29.
 * For a deterministic finite tree automaton, we implement ???
 *
 * @sa automaton::simplify::Trim
 * @sa automaton::simplify::UselessStatesRemover
 */
class UnreachableStatesRemover {
public:
	/**
	 * Removes unreachable states from a finite automaton.
	 *
	 * @tparam T type of a finite automaton
	 *
	 * @param fsm automaton to remove unreachable states from
	 *
	 * @return @p fsm without unreachable states
	 */
	template < class T >
	static ext::require < isDFA < T > || isNFA < T > || isEpsilonNFA < T > || isCompactNFA < T > || isExtendedNFA < T >, T > remove ( const T & fsm );

	/**
	 * @overload
	 *
	 * @tparam SymbolType Type for input symbols.
	 * @tparam StateType Type for states.
	 *
	 * @param fsm automaton to remove unreachable states from
	 *
	 * @return @p fsm without unreachable states
	 */
	template < class SymbolType, class StateType >
	static automaton::MultiInitialStateNFA < SymbolType, StateType > remove( const automaton::MultiInitialStateNFA < SymbolType, StateType > & fsm );

	/**
	 * Removes unreachable states from a finite tree automaton.
	 *
	 * @overload
	 *
	 * @tparam T type of a finite tree automaton
	 *
	 * @param fta automaton to remove unreachable states from
	 *
	 * @return @p fta without unreachable states
	 */
	template < class T >
	static ext::require < isDFTA < T > || isNFTA < T >, T > remove ( const T & fta );
};

template < class T >
ext::require < isDFA < T > || isNFA < T > || isEpsilonNFA < T > || isCompactNFA < T > || isExtendedNFA < T >, T > UnreachableStatesRemover::remove ( const T & fsm ) {
	using StateType = typename T::StateType;

	// 1a
	ext::set<StateType> Qa = automaton::properties::ReachableStates::reachableStates( fsm );

	// 2
	T M(fsm.getInitialState());

	M.setStates( Qa );
	M.setInputAlphabet( fsm.getInputAlphabet( ) );

	for( const auto & transition : fsm.getTransitions( ) )
		if( Qa.count( transition.first.first ) )
			M.addTransition( transition.first.first, transition.first.second, transition.second );

	ext::set<StateType> finalStates;
	std::set_intersection( fsm.getFinalStates( ).begin(), fsm.getFinalStates( ).end(), Qa.begin( ), Qa.end( ), std::inserter( finalStates, finalStates.begin( ) ) );
	M.setFinalStates( finalStates );

	return M;
}

template < class SymbolType, class StateType >
automaton::MultiInitialStateNFA < SymbolType, StateType > UnreachableStatesRemover::remove( const automaton::MultiInitialStateNFA < SymbolType, StateType > & fsm ) {
	// 1a
	ext::set<StateType> Qa = automaton::properties::ReachableStates::reachableStates( fsm );

	// 2
	automaton::MultiInitialStateNFA < SymbolType, StateType > M;

	M.setStates( Qa );
	M.setInitialStates( fsm.getInitialStates() );
	M.setInputAlphabet( fsm.getInputAlphabet( ) );

	for( const auto & transition : fsm.getTransitions( ) )
		if( Qa.count( transition.first.first ) )
			M.addTransition( transition.first.first, transition.first.second, transition.second );

	ext::set<StateType> finalStates;
	std::set_intersection( fsm.getFinalStates( ).begin(), fsm.getFinalStates( ).end(), Qa.begin( ), Qa.end( ), std::inserter( finalStates, finalStates.begin( ) ) );
	M.setFinalStates( finalStates );

	return M;
}

template < class T >
ext::require < isDFTA < T > || isNFTA < T >, T >  UnreachableStatesRemover::remove( const T & fta ) {
	using StateType = typename T::StateType;

	// 1a
	ext::set<StateType> Qa = automaton::properties::ReachableStates::reachableStates( fta );

	// 2
	T M;

	M.setStates( Qa );
	M.setInputAlphabet( fta.getInputAlphabet( ) );

	for( const auto & transition : fta.getTransitions( ) )
		if( std::all_of ( transition.first.second.begin ( ), transition.first.second.end ( ), [ & ] ( const StateType & state ) { return Qa.count ( state ); } ) )
			M.addTransition ( transition.first.first, transition.first.second, transition.second );

	ext::set<StateType> finalStates;
	std::set_intersection( fta.getFinalStates( ).begin(), fta.getFinalStates( ).end(), Qa.begin( ), Qa.end( ), std::inserter( finalStates, finalStates.begin( ) ) );
	M.setFinalStates( std::move ( finalStates ) );

	return M;
}

} /* namespace simplify */

} /* namespace automaton */

#endif /* UNREACHABLE_STATES_REMOVER_H_ */
