/*
 * AllEpsilonClosure.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: 12. 4. 2015
 *	  Author: Jan Travnicek
 */

#ifndef ALL_EPSILON_CLOSURE_H_
#define ALL_EPSILON_CLOSURE_H_

#include <alib/set>
#include <alib/map>
#include <alib/deque>
#include <alib/iostream>

#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 <regexp/properties/RegExpEpsilon.h>

namespace automaton {

namespace properties {

/**
 * Algorithm computing an epsilon closure for all states of a finite automaton.
 */
class AllEpsilonClosure {
public:
	/**
	 * Computes epsilon closure for all states of a nondeterministic finite automaton with epsilon transitions.
	 * Implemented using closures (breadth-first search).
	 *
	 * @tparam SymbolType Type for the input symbols.
	 * @tparam StateType Type for the states.
	 * @param fsm nondeterministic finite automaton with epsilon transitions
	 * @return mapping of states to set of states representing the epsilon closures for each state of @p fsm
	 */
	template < class SymbolType, class StateType >
	static ext::map<StateType, ext::set<StateType>> allEpsilonClosure( const automaton::EpsilonNFA < SymbolType, StateType > & fsm);

	/**
	 * Computes epsilon closure for all states of a nondeterministic finite automaton with multiple initial states.
	 * Epsilon closure of a state q of an automaton without epsilon transitions is eps-closure(q) = {q}.
	 *
	 * @overload
	 *
	 * @tparam T type of tested automaton.
	 * @param fsm (nondeterministic) finite automaton (with multiple initial states)
	 * @return mapping of states to set of states representing the epsilon closures for each state of @p fsm
	 */
	template < class T >
	static ext::require < isDFA < T > || isNFA < T > || isMultiInitialStateNFA < T >, ext::map < typename T::StateType, ext::set < typename T::StateType > > > allEpsilonClosure ( const T & fsm );

	/**
	 * Computes epsilon closure for all states of an extended nondeterministic finite automaton.
	 * Any regexp that can denote epsilon word is considered as an epsilon transition for the purpose of this algorithm.
	 *
	 * @overload
	 *
	 * @tparam SymbolType Type for the input symbols.
	 * @tparam StateType Type for the states.
	 * @param fsm extended nondeterministic finite automaton
	 * @return mapping of states to set of states representing the epsilon closures for each state of @p fsm
	 */
	template < class SymbolType, class StateType >
	static ext::map<StateType, ext::set<StateType>> allEpsilonClosure( const automaton::ExtendedNFA < SymbolType, StateType > & fsm);

	/**
	 * Computes epsilon closure for all states of a compact nondeterministic finite automaton.
	 * Any string of size 0 is considered as epsilon.
	 *
	 * @overload
	 *
	 * @tparam SymbolType Type for the input symbols.
	 * @tparam StateType Type for the states.
	 * @param fsm compact nondeterministic finite automaton
	 * @return mapping of states to set of states representing the epsilon closures for each state of @p fsm
	 */
	template < class SymbolType, class StateType >
	static ext::map<StateType, ext::set<StateType>> allEpsilonClosure( const automaton::CompactNFA < SymbolType, StateType > & fsm);
};

template < class SymbolType, class StateType >
ext::map<StateType, ext::set<StateType>> AllEpsilonClosure::allEpsilonClosure( const automaton::EpsilonNFA < SymbolType, StateType > & fsm) {
	ext::deque<ext::map<StateType, ext::set<StateType>>> Qi;

	Qi.push_back({});
	for(const StateType& state : fsm.getStates())
		Qi.at(0)[state].insert(state);

	int i = 1;
	while(true) {
		Qi.push_back( Qi.at( i - 1 ) );

		for( const std::pair<const StateType, ext::set<StateType>> & p : Qi.at( i - 1 ) )
			for(const StateType& pclosure : p.second )
				for(const auto& transition : fsm.getEpsilonTransitionsFromState(pclosure))
					Qi.at( i )[p.first].insert(transition.second);

		if( Qi.at( i ) == Qi.at( i - 1 ) )
			break;

		i = i + 1;
	};

	return Qi.at(i);
}

template < class T >
ext::require < isDFA < T > || isNFA < T > || isMultiInitialStateNFA < T >, ext::map < typename T::StateType, ext::set < typename T::StateType > > > AllEpsilonClosure::allEpsilonClosure ( const T & fsm ) {
	ext::map < typename T::StateType, ext::set < typename T::StateType > > closure;
	for ( const typename T::StateType & state : fsm.getStates ( ) )
		closure [ state ].insert ( state );
	return closure;
}

template < class SymbolType, class StateType >
ext::map<StateType, ext::set<StateType>> AllEpsilonClosure::allEpsilonClosure( const automaton::ExtendedNFA < SymbolType, StateType > & fsm) {
	ext::map<StateType, ext::set<StateType>> res;
	ext::map<StateType, ext::set<StateType>> step;

	for(const std::pair<const ext::pair<StateType, regexp::UnboundedRegExpStructure < SymbolType > >, StateType >& transition : fsm.getTransitions() )
		if( regexp::properties::RegExpEpsilon::languageContainsEpsilon( transition.first.second ) )
			step[transition.first.first].insert(transition.second);

	for(const StateType& state : fsm.getStates())
		step[state].insert(state);

	do {
		res = step;

		for(const std::pair<const StateType, ext::set<StateType>>& item : res)
			for(const StateType& to : item.second)
				step[item.first].insert(res[to].begin(), res[to].end());
	} while(res != step);

	return res;
}

template < class SymbolType, class StateType >
ext::map<StateType, ext::set<StateType>> AllEpsilonClosure::allEpsilonClosure( const automaton::CompactNFA < SymbolType, StateType > & fsm) {
	ext::map<StateType, ext::set<StateType>> res;
	ext::map<StateType, ext::set<StateType>> step;

	for(const std::pair<const ext::pair<StateType, ext::vector < SymbolType > >, StateType >& transition : fsm.getTransitions() )
		if( transition.first.second.empty ( ) )
			step[transition.first.first].insert(transition.second);

	for(const StateType& state : fsm.getStates())
		step[state].insert(state);

	do {
		res = step;

		for(const std::pair<const StateType, ext::set<StateType>>& item : res)
			for(const StateType& to : item.second)
				step[item.first].insert(res[to].begin(), res[to].end());
	} while(res != step);

	return res;
}

} /* namespace properties */

} /* namespace automaton */

#endif /* ALL_EPSILON_CLOSURE_H_ */
