/*
 * Determinize.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: 16. 1. 2014
 *	  Author: Jan Vesely
 */

#ifndef DETERMINIZE_H_
#define DETERMINIZE_H_

#include <alib/set>

#include <automaton/PDA/InputDrivenNPDA.h>
#include <automaton/PDA/InputDrivenDPDA.h>
#include <automaton/PDA/RealTimeHeightDeterministicNPDA.h>
#include <automaton/PDA/RealTimeHeightDeterministicDPDA.h>
#include <automaton/PDA/VisiblyPushdownNPDA.h>
#include <automaton/PDA/VisiblyPushdownDPDA.h>
#include <automaton/FSM/NFA.h>
#include <automaton/FSM/DFA.h>
#include <automaton/FSM/MultiInitialStateNFA.h>
#include <automaton/TA/NFTA.h>
#include <automaton/TA/DFTA.h>

#include <automaton/transform/PDAToRHPDA.h>
#include <automaton/transform/RHPDAToPDA.h>

#include <automaton/PDA/NPDA.h>
#include <automaton/PDA/DPDA.h>
#include <automaton/PDA/SinglePopNPDA.h>
#include <automaton/PDA/SinglePopDPDA.h>

#include <automaton/TM/OneTapeDTM.h>

namespace automaton {

namespace determinize {

/**
 * Class implementing various algorithms for determinization of various kinds of automata.
 */
class Determinize {
public:
	/**
	 * Determinization of deterministic finite automata.
	 * Implemented as a no-op.
	 *
	 * @tparam SymbolType Type for the input symbols.
	 * @tparam StateType Type for the states.
	 * @param dfa deterministic finite automaton
	 * @return deterministic finite automaton equivalent to @p dfa
	 */
	template < class SymbolType, class StateType >
	static automaton::DFA < SymbolType, StateType > determinize ( const automaton::DFA < SymbolType, StateType > & automaton );

	/**
	 * Implementation of subset determinization for nondeterministic finite automata.
	 *
	 * @tparam SymbolType Type for the input symbols.
	 * @tparam StateType Type for the states.
	 * @param nfa nondeterministic finite automaton
	 * @return deterministic finite automaton equivalent to @p nfa
	 */
	template < class SymbolType, class StateType >
	static automaton::DFA < SymbolType, ext::set < StateType > > determinize ( const automaton::NFA < SymbolType, StateType > & nfa );

	/**
	 * Implementation of subset determinization for nondeterministic finite automata with multiple initial states.
	 *
	 * @tparam SymbolType Type for the input symbols.
	 * @tparam StateType Type for the states.
	 * @param nfa nondeterministic finite automaton with multiple initial states
	 * @return deterministic finite automaton equivalent to @p nfa
	 */
	template < class SymbolType, class StateType >
	static automaton::DFA < SymbolType, ext::set < StateType > > determinize ( const automaton::MultiInitialStateNFA < SymbolType, StateType > & nfa );

	/**
	 * Determinization of deterministic finite tree automata.
	 * Implemented as a no-op.
	 *
	 * @tparam SymbolType Type for the input symbols.
	 * @tparam RankType Type for the rank (arity) in ranked alphabet.
	 * @tparam StateType Type for the states.
	 * @param dfta deterministic finite tree automaton
	 * @return deterministic finite tree automaton equivalent to @p dfta
	 */
	template < class SymbolType, class StateType >
	static automaton::DFTA < SymbolType, StateType > determinize ( const automaton::DFTA < SymbolType, StateType > & automaton );

	/**
	 * Implementation of subset determinization for nondeterministic finite tree automata.
	 *
	 * @tparam SymbolType Type for the input symbols.
	 * @tparam RankType Type for the rank (arity) in ranked alphabet.
	 * @tparam StateType Type for the states.
	 * @param nfta nondeterministic finite tree automaton
	 * @return deterministic finite tree automaton equivalent to @p nfta
	 */
	template < class SymbolType, class StateType >
	static automaton::DFTA < SymbolType, ext::set < StateType > > determinize ( const automaton::NFTA < SymbolType, StateType > & nfta );

	/**
	 * Determinization of deterministic input-driven pushdown automata.
	 * Implemented as a no-op.
	 *
	 * @tparam SymbolType Type for the input symbols.
	 * @tparam PushdownSymbolType Type for the pushdown store symbols.
	 * @tparam StateType Type for the states.
	 * @param dpda deterministic input-driven pushdown automaton
	 * @return deterministic input-driven pushdown automaton equivalent to @p dpda
	 */
	template < class InputSymbolType, class PushdownSymbolType, class StateType >
	static automaton::InputDrivenDPDA < InputSymbolType, PushdownSymbolType, StateType > determinize ( const automaton::InputDrivenDPDA < InputSymbolType, PushdownSymbolType, StateType > & automaton );

	/**
	 * Implementation of determinization for input-driven pushdown automata.
	 *
	 * @tparam SymbolType Type for the input symbols.
	 * @tparam PushdownSymbolType Type for the pushdown store symbols.
	 * @tparam StateType Type for the states.
	 * @param npda nondeterministic input-driven pushdown automaton
	 * @return deterministic input-driven pushdown automaton equivalent to @p npda
	 */
	template < class InputSymbolType, class PushdownSymbolType, class StateType >
	static automaton::InputDrivenDPDA < InputSymbolType, PushdownSymbolType, ext::set < StateType > > determinize ( const automaton::InputDrivenNPDA < InputSymbolType, PushdownSymbolType, StateType > & npda );

	/**
	 * Determinization of deterministic visibly pushdown automata.
	 * Implemented as a no-op.
	 *
	 * @tparam SymbolType Type for the input symbols.
	 * @tparam PushdownSymbolType Type for the pushdown store symbols.
	 * @tparam StateType Type for the states.
	 * @param dpda deterministic visibly pushdown automaton
	 * @return deterministic pushdown automaton equivalent to @p dpda
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static automaton::VisiblyPushdownDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > determinize ( const automaton::VisiblyPushdownDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton );

	/**
	 * Determinization of nondeterministic visibly pushdown automata.
	 *
	 * @tparam SymbolType Type for the input symbols.
	 * @tparam PushdownSymbolType Type for the pushdown store symbols.
	 * @tparam StateType Type for the states.
	 * @param npda nondeterministic visibly pushdown automaton
	 * @return deterministic pushdown automaton equivalent to @p npda
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static automaton::VisiblyPushdownDPDA < InputSymbolType, ext::pair < ext::set < ext::pair < StateType, StateType > >, InputSymbolType >, ext::set < ext::pair < StateType, StateType > > > determinize ( const automaton::VisiblyPushdownNPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & npda );

	/**
	 * Determinization of deterministic real-time height-deterministic pushdown automata.
	 * Implemented as a no-op.
	 *
	 * @tparam SymbolType Type for the input symbols.
	 * @tparam PushdownSymbolType Type for the pushdown store symbols.
	 * @tparam StateType Type for the states.
	 * @param dpda deterministic real-time height-deterministic pushdown automaton
	 * @return deterministic pushdown automaton equivalent to @p dpda
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static automaton::RealTimeHeightDeterministicDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > determinize ( const automaton::RealTimeHeightDeterministicDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton );

	/**
	 * Determinization of nondeterministic real-time height-deterministic pushdown automata.
	 *
	 * @tparam SymbolType Type for the input symbols.
	 * @tparam PushdownSymbolType Type for the pushdown store symbols.
	 * @tparam StateType Type for the states.
	 * @param npda nondeterministic real-time height-deterministic pushdown automaton
	 * @return deterministic pushdown automaton equivalent to @p npda
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static automaton::RealTimeHeightDeterministicDPDA < InputSymbolType, ext::pair < ext::set < ext::pair < StateType, StateType > >, common::symbol_or_epsilon < InputSymbolType > >, ext::set < ext::pair < StateType, StateType > > > determinize ( const automaton::RealTimeHeightDeterministicNPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & npda );

	/**
	 * Determinization of deterministic single pop pushdown automata.
	 * Implemented as a no-op.
	 *
	 * @tparam SymbolType Type for the input symbols.
	 * @tparam PushdownSymbolType Type for the pushdown store symbols.
	 * @tparam StateType Type for the states.
	 * @param dpda nondeterministic finite automaton with multiple initial states
	 * @return deterministic single pop deterministic pushdown automaton equivalent do @p dpda
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static automaton::SinglePopDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > determinize ( const automaton::SinglePopDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton );

	/**
	 * Determinization of deterministic pushdown automata.
	 * Implemented as a no-op.
	 *
	 * @tparam SymbolType Type for the input symbols.
	 * @tparam PushdownSymbolType Type for the pushdown store symbols.
	 * @tparam StateType Type for the states.
	 * @param dpda deterministic pushdown automaton
	 * @return deterministic pushdown automaton equivalent to @p dpda
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static automaton::DPDA < InputSymbolType, PushdownStoreSymbolType, StateType > determinize ( const automaton::DPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton );

	/**
	 * Determinization of nondeterministic pushdown automata is implemented as a cast of such automaton to RhPDA.
	 *
	 * @tparam SymbolType Type for the input symbols.
	 * @tparam PushdownSymbolType Type for the pushdown store symbols.
	 * @tparam StateType Type for the states.
	 * @param npda nondeterministic pushdown automaton
	 * @return nondeterministic pushdown automaton equivalent to @p npda
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static automaton::DPDA < InputSymbolType, PushdownStoreSymbolType, StateType > determinize ( const automaton::NPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton );

	/**
	 * Determinization of deterministic pushdown automata.
	 * Implemented as a no-op.
	 *
	 * @tparam SymbolType Type for the input symbols.
	 * @tparam StateType Type for the states.
	 * @param dtm deterministic one-tape turing machine
	 * @return deterministic one-tape turing machine equivalent to @p dtm
	 */
	template < class SymbolType, class StateType >
	static automaton::OneTapeDTM < SymbolType, StateType > determinize ( const automaton::OneTapeDTM < SymbolType, StateType > & automaton );
};

template < class SymbolType, class StateType >
automaton::DFA < SymbolType, StateType > Determinize::determinize ( const automaton::DFA < SymbolType, StateType > & automaton ) {
	return automaton;
}

template < class SymbolType, class StateType >
automaton::DFTA < SymbolType, StateType > Determinize::determinize ( const automaton::DFTA < SymbolType, StateType > & automaton ) {
	return automaton;
}

template < class InputSymbolType, class PushdownSymbolType, class StateType >
automaton::InputDrivenDPDA < InputSymbolType, PushdownSymbolType, StateType > Determinize::determinize ( const automaton::InputDrivenDPDA < InputSymbolType, PushdownSymbolType, StateType > & automaton ) {
	return automaton;
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
automaton::VisiblyPushdownDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > Determinize::determinize ( const automaton::VisiblyPushdownDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton ) {
	return automaton;
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
automaton::RealTimeHeightDeterministicDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > Determinize::determinize ( const automaton::RealTimeHeightDeterministicDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton ) {
	return automaton;
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
automaton::SinglePopDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > Determinize::determinize ( const automaton::SinglePopDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton ) {
	return automaton;
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
automaton::DPDA < InputSymbolType, PushdownStoreSymbolType, StateType > Determinize::determinize ( const automaton::DPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton ) {
	return automaton;
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
automaton::DPDA < InputSymbolType, PushdownStoreSymbolType, StateType > Determinize::determinize ( const automaton::NPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton ) {
	automaton::RealTimeHeightDeterministicNPDA < InputSymbolType, PushdownStoreSymbolType, StateType > rhpda = automaton::transform::PDAToRHPDA::convert(automaton);
	automaton::RealTimeHeightDeterministicDPDA < InputSymbolType, ext::pair < ext::set < ext::pair < StateType, StateType > >, common::symbol_or_epsilon < InputSymbolType > >, ext::set < ext::pair < StateType, StateType > > > dpda = Determinize::determinize(rhpda);
	automaton::RealTimeHeightDeterministicDPDA < > norm = core::normalize < decltype ( dpda ) >::eval ( std::move ( dpda ) );
	return automaton::transform::RHPDAToPDA::convert ( norm );
}

template < class SymbolType, class StateType >
automaton::OneTapeDTM < SymbolType, StateType > Determinize::determinize ( const automaton::OneTapeDTM < SymbolType, StateType > & automaton ) {
	return automaton;
}

} /* namespace determinize */

} /* namespace automaton */

#include "DeterminizeNFAPart.hxx"
#include "DeterminizeIDPDAPart.hxx"
#include "DeterminizeNFTAPart.hxx"
#include "DeterminizeVPAPart.hxx"
#include "DeterminizeRHDPDAPart.hxx"

#endif /* DETERMINIZE_H_ */
