/*
 * InputDrivenNPDA.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: Mar 25, 2013
 *      Author: Jan Travnicek
 */

#ifndef INPUT_DRIVEN_NPDA_H_
#define INPUT_DRIVEN_NPDA_H_

#include <ostream>
#include <sstream>

#include <alib/multimap>
#include <alib/set>
#include <alib/vector>
#include <alib/algorithm>
#include <alib/iterator>

#include <core/components.hpp>

#include <common/DefaultStateType.h>
#include <common/DefaultSymbolType.h>

#include <automaton/AutomatonException.h>

#include <core/normalize.hpp>
#include <alphabet/common/SymbolNormalize.h>
#include <automaton/common/AutomatonNormalize.h>

namespace automaton {

class InputAlphabet;
class PushdownStoreAlphabet;
class InitialSymbol;
class States;
class FinalStates;
class InitialState;

/**
 * \brief
 * Nondeterministic input driven pushdown automaton. Accepts subset of context free languages.

 * \details
 * Definition is similar to the deterministic finite automata extended with pushdown store.
 * A = (Q, T, G, I, Z, \delta, \zeta, F),
 * Q (States) = nonempty finite set of states,
 * T (TerminalAlphabet) = finite set of terminal symbols - having this empty won't let automaton do much though,
 * G (PushdownStoreAlphabet) = finite set of pushdown store symbol - having this empty makes the automaton equivalent to NFA
 * I (InitialState) = initial state,
 * Z (InitialPushdownStoreSymbol) = initial pushdown store symbol
 * \delta = transition function of the form A \times a -> P(Q), where A \in Q, a \in T, and P(Q) is a powerset of states,
 * \zeta = mapping function of the form a -> ( \alpha, \beta ) where a \in T and \alpha, \beta \in G*
 * F (FinalStates) = set of final states
 *
 * Note that target state of a transition is required.
 * This class is used to store minimal, total, ... variants of deterministic finite automata.
 *
 * \tparam InputSymbolTypeT used for the terminal alphabet
 * \tparam PushdownSymbolTypeT used for the pushdown store alphabet
 * \tparam StateTypeT used to the states, and the initial state of the automaton.
 */
template < class InputSymbolTypeT = DefaultSymbolType, class PushdownStoreSymbolTypeT = DefaultSymbolType, class StateTypeT = DefaultStateType >
class InputDrivenNPDA final : public ext::CompareOperators < InputDrivenNPDA < InputSymbolTypeT, PushdownStoreSymbolTypeT, StateTypeT > >, public core::Components < InputDrivenNPDA < InputSymbolTypeT, PushdownStoreSymbolTypeT, StateTypeT >, ext::set < InputSymbolTypeT >, component::Set, InputAlphabet, ext::set < PushdownStoreSymbolTypeT >, component::Set, PushdownStoreAlphabet, PushdownStoreSymbolTypeT, component::Value, InitialSymbol, ext::set < StateTypeT >, component::Set, std::tuple < States, FinalStates >, StateTypeT, component::Value, InitialState > {
public:
	typedef InputSymbolTypeT InputSymbolType;
	typedef PushdownStoreSymbolTypeT PushdownStoreSymbolType;
	typedef StateTypeT StateType;

private:
	/**
	 * Transition function as mapping from a state times an input symbol on the left hand side to a set of states.
	 */
	ext::multimap < ext::pair < StateType, InputSymbolType >, StateType > transitions;

	/**
	 * Pushdown store operation as mapping from a input symbol to the pop and push pushdown store operations.
	 */
	ext::map < InputSymbolType, ext::pair < ext::vector < PushdownStoreSymbolType >, ext::vector < PushdownStoreSymbolType > > > inputSymbolToPushdownStoreOperation;

	/*
	 * Helper function to validate pop and push pushdown store operaion maped to input symbol.
	 *
	 * \param input the input symbol
	 * \param pop the vector of symbols to be poped
	 * \param push the vector of symbols to be pushed
	 */
	void checkPushdownStoreOperation ( const InputSymbolType & input, const ext::vector < PushdownStoreSymbolType > & pop, const ext::vector < PushdownStoreSymbolType > & push );

public:
	/**
	 * \brief Creates a new instance of the automaton with a concrete set of states, input alphabet, pushdown store alphabet, initial state, initial pushdown symbol and a set of final states.
	 *
	 * \param states the initial set of states of the automaton
	 * \param inputAlphabet the initial input alphabet
	 * \param pushdownStoreAlphabet the initial set of symbols used in the pushdown store by the automaton
	 * \param initialState the initial state of the automaton
	 * \param initialPushdownSymbol the initial pushdown symbol of the automaton
	 * \param finalStates the initial set of final states of the automaton
	 */
	explicit InputDrivenNPDA ( ext::set < StateType > states, ext::set < InputSymbolType > inputAlphabet, ext::set < PushdownStoreSymbolType > pushdownStoreAlphabet, StateType initialState, PushdownStoreSymbolType initialSymbol, ext::set < StateType > finalStates );

	/**
	 * \brief Creates a new instance of the automaton with a concrete initial state and initial pushdown store symbol.
	 *
	 * \param initialState the initial state of the automaton
	 * \param initialPushdownSymbol the initial pushdown symbol of the automaton
	 */
	explicit InputDrivenNPDA ( StateType initialState, PushdownStoreSymbolType initialPushdownSymbol );

	/**
	 * Getter of the initial state.
	 *
	 * \returns the initial state of the automaton
	 */
	const StateType & getInitialState ( ) const & {
		return this-> template accessComponent < InitialState > ( ).get ( );
	}

	/**
	 * Getter of the initial state.
	 *
	 * \returns the initial state of the automaton
	 */
	StateType && getInitialState ( ) && {
		return std::move ( this-> template accessComponent < InitialState > ( ).get ( ) );
	}

	/**
	 * Setter of the initial state.
	 *
	 * \param state new initial state of the automaton
	 *
	 * \returns true if the initial state was indeed changed
	 */
	bool setInitialState ( StateType state ) {
		return this-> template accessComponent < InitialState > ( ).set ( std::move ( state ) );
	}

	/**
	 * Getter of states.
	 *
	 * \returns the states of the automaton
	 */
	const ext::set < StateType > & getStates ( ) const & {
		return this-> template accessComponent < States > ( ).get ( );
	}

	/**
	 * Getter of states.
	 *
	 * \returns the states of the automaton
	 */
	ext::set < StateType > && getStates ( ) && {
		return std::move ( this-> template accessComponent < States > ( ).get ( ) );
	}

	/**
	 * Adder of a state.
	 *
	 * \param state the new state to be added to a set of states
	 *
	 * \returns true if the state was indeed added
	 */
	bool addState ( StateType state ) {
		return this-> template accessComponent < States > ( ).add ( std::move ( state ) );
	}

	/**
	 * Setter of states.
	 *
	 * \param states completely new set of states
	 */
	void setStates ( ext::set < StateType > states ) {
		this-> template accessComponent < States > ( ).set ( std::move ( states ) );
	}

	/**
	 * Remover of a state.
	 *
	 * \param state a state to be removed from a set of states
	 *
	 * \returns true if the state was indeed removed
	 */
	void removeState ( const StateType & state ) {
		this-> template accessComponent < States > ( ).remove ( state );
	}

	/**
	 * Getter of final states.
	 *
	 * \returns the final states of the automaton
	 */
	const ext::set < StateType > & getFinalStates ( ) const & {
		return this-> template accessComponent < FinalStates > ( ).get ( );
	}

	/**
	 * Getter of final states.
	 *
	 * \returns the final states of the automaton
	 */
	ext::set < StateType > && getFinalStates ( ) && {
		return std::move ( this-> template accessComponent < FinalStates > ( ).get ( ) );
	}

	/**
	 * Adder of a final state.
	 *
	 * \param state the new state to be added to a set of final states
	 *
	 * \returns true if the state was indeed added
	 */
	bool addFinalState ( StateType state ) {
		return this-> template accessComponent < FinalStates > ( ).add ( std::move ( state ) );
	}

	/**
	 * Setter of final states.
	 *
	 * \param states completely new set of final states
	 */
	void setFinalStates ( ext::set < StateType > states ) {
		this-> template accessComponent < FinalStates > ( ).set ( std::move ( states ) );
	}

	/**
	 * Remover of a final state.
	 *
	 * \param state a state to be removed from a set of final states
	 *
	 * \returns true if the state was indeed removed
	 */
	void removeFinalState ( const StateType & state ) {
		this-> template accessComponent < FinalStates > ( ).remove ( state );
	}

	/**
	 * Getter of the pushdown store alphabet.
	 *
	 * \returns the pushdown store alphabet of the automaton
	 */
	const ext::set < PushdownStoreSymbolType > & getPushdownStoreAlphabet ( ) const & {
		return this->template accessComponent < PushdownStoreAlphabet > ( ).get ( );
	}

	/**
	 * Getter of the pushdown store alphabet.
	 *
	 * \returns the pushdown store alphabet of the automaton
	 */
	ext::set < PushdownStoreSymbolType > && getPushdownStoreAlphabet ( ) && {
		return std::move ( this->template accessComponent < PushdownStoreAlphabet > ( ).get ( ) );
	}

	/**
	 * Adder of a pushdown store symbol.
	 *
	 * \param symbol the new symbol to be added to a pushdown store alphabet
	 *
	 * \returns true if the symbol was indeed added
	 */
	bool addPushdownStoreSymbol ( PushdownStoreSymbolType symbol ) {
		return this->template accessComponent < PushdownStoreAlphabet > ( ).add ( std::move ( symbol ) );
	}

	/**
	 * Adder of pushdown store symbols.
	 *
	 * \param symbols new symbols to be added to a pushdown store alphabet
	 */
	void addPushdownStoreSymbols ( ext::set < PushdownStoreSymbolType > symbols ) {
		this->template accessComponent < PushdownStoreAlphabet > ( ).add ( std::move ( symbols ) );
	}

	/**
	 * Setter of a pushdown store alphabet.
	 *
	 * \param symbols completely new pushdown store alphabet
	 */
	void setPushdownStoreAlphabet ( ext::set < PushdownStoreSymbolType > symbols ) {
		this->template accessComponent < PushdownStoreAlphabet > ( ).set ( std::move ( symbols ) );
	}

	/**
	 * Remover of an pushdown store symbol.
	 *
	 * \param symbol a symbol to be removed from a pushdown store alphabet
	 *
	 * \returns true if the symbol was indeed removed
	 */
	void removePushdownStoreSymbol ( const PushdownStoreSymbolType & symbol ) {
		this->template accessComponent < PushdownStoreAlphabet > ( ).remove ( symbol );
	}

	/**
	 * Getter of the initial pushdown store symbol.
	 *
	 * \returns the initial pushdown store symbol of the automaton
	 */
	const PushdownStoreSymbolType & getInitialSymbol ( ) const & {
		return this->template accessComponent < InitialSymbol > ( ).get ( );
	}

	/**
	 * Getter of the initial pushdown store symbol.
	 *
	 * \returns the initial pushdown store symbol of the automaton
	 */
	PushdownStoreSymbolType && getInitialSymbol ( ) && {
		return std::move ( this->template accessComponent < InitialSymbol > ( ).get ( ) );
	}

	/**
	 * Setter of the initial pushdown store symbol.
	 *
	 * \param symbol new initial pushdown store symbol of the automaton
	 *
	 * \returns true if the initial pushdown store symbol was indeed changed
	 */
	bool setInitialSymbol ( PushdownStoreSymbolType symbol ) {
		return this->template accessComponent < InitialSymbol > ( ).set ( std::move ( symbol ) );
	}

	/**
	 * Getter of the input alphabet.
	 *
	 * \returns the input alphabet of the automaton
	 */
	const ext::set < InputSymbolType > & getInputAlphabet ( ) const & {
		return this-> template accessComponent < InputAlphabet > ( ).get ( );
	}

	/**
	 * Getter of the input alphabet.
	 *
	 * \returns the input alphabet of the automaton
	 */
	ext::set < InputSymbolType > && getInputAlphabet ( ) && {
		return std::move ( this-> template accessComponent < InputAlphabet > ( ).get ( ) );
	}

	/**
	 * Adder of a input symbol.
	 *
	 * \param symbol the new symbol to be added to an input alphabet
	 *
	 * \returns true if the symbol was indeed added
	 */
	bool addInputSymbol ( InputSymbolType symbol ) {
		return this-> template accessComponent < InputAlphabet > ( ).add ( std::move ( symbol ) );
	}

	/**
	 * Adder of input symbols.
	 *
	 * \param symbols new symbols to be added to an input alphabet
	 */
	void addInputSymbols ( ext::set < InputSymbolType > symbols ) {
		this-> template accessComponent < InputAlphabet > ( ).add ( std::move ( symbols ) );
	}

	/**
	 * Setter of input alphabet.
	 *
	 * \param symbols completely new input alphabet
	 */
	void setInputAlphabet ( ext::set < InputSymbolType > symbols ) {
		this-> template accessComponent < InputAlphabet > ( ).set ( std::move ( symbols ) );
	}

	/**
	 * Remover of an input symbol.
	 *
	 * \param symbol a symbol to be removed from an input alphabet
	 *
	 * \returns true if the symbol was indeed removed
	 */
	void removeInputSymbol ( const InputSymbolType & symbol ) {
		this-> template accessComponent < InputAlphabet > ( ).remove ( symbol );
	}

	/**
	 * Setter of pushdown store operation
	 *
	 * Pushdown store operation is a mapping from a input symbol to the pop and push pushdown store operations
	 * \param input the input symbol
	 * \param pop the vector of pushdown store symbols to pop from pushdown store
	 * \param push the vector of pushdown store symbol to push to pushdown store
	 *
	 * \returns true if the operation was set
	 */
	bool setPushdownStoreOperation ( InputSymbolType input, ext::vector < PushdownStoreSymbolType > pop, ext::vector < PushdownStoreSymbolType > push );

	/**
	 * Setter of pushdown store operations
	 *
	 * Pushdown store operation is a mapping from a input symbol to the pop and push pushdown store operations
	 * \param operations represented by mapping from input symbol to pair of vectors of pushdown store symbols to pop and pop respectively
	 */
	void setPushdownStoreOperations ( ext::map < InputSymbolType, ext::pair < ext::vector < PushdownStoreSymbolType >, ext::vector < PushdownStoreSymbolType > > > operations );

	/**
	 * Clearer of pushdown store operation
	 *
	 * \param input the input symbol for which to clear the pushdown store operation
	 * \return true if the pushdown store operaion was cleared
	 */
	bool clearPushdownStoreOperation ( const InputSymbolType & input );

	/**
	 * Get the pushdown store operations.
	 *
	 * \return the pushdown store operaions
	 */
	const ext::map < InputSymbolType, ext::pair < ext::vector < PushdownStoreSymbolType >, ext::vector < PushdownStoreSymbolType > > > & getPushdownStoreOperations ( ) const &;

	/**
	 * Get the pushdown store operations.
	 *
	 * \return the pushdown store operaions
	 */
	ext::map < InputSymbolType, ext::pair < ext::vector < PushdownStoreSymbolType >, ext::vector < PushdownStoreSymbolType > > > && getPushdownStoreOperations ( ) &&;

	/**
	 * \brief Adds a transition to the automaton.
	 *
	 * \details The transition is in a form A \times a -> B, where A, B \in Q and a \in T
	 *
	 * \param current the source state (A)
	 * \param input the input symbol (a)
	 * \param next the target state (B)
	 *
	 * \throws AutomatonException when transition contains state or symbol not present in the automaton components, or when the pushdown store operation is not set for the input symbol
	 *
	 * \returns true if the transition was indeed added
	 */
	bool addTransition ( StateType from, InputSymbolType input, StateType to );

	/**
	 * \brief Removes a transition from the automaton.
	 *
	 * \details The transition is in a form A times a -> B, where A, B \in Q and a \in T
	 *
	 * \param current the source state (A)
	 * \param input the input symbol (a)
	 * \param next the target state (B)
	 *
	 * \returns true if the transition was indeed removed
	 */
	bool removeTransition ( const StateType & from, const InputSymbolType & input, const StateType & to );

	/**
	 * Get the transition function of the automaton in its natural form.
	 *
	 * \returns transition function of the automaton
	 */
	const ext::multimap < ext::pair < StateType, InputSymbolType >, StateType > & getTransitions ( ) const &;

	/**
	 * Get the transition function of the automaton in its natural form.
	 *
	 * \returns transition function of the automaton
	 */
	ext::multimap < ext::pair < StateType, InputSymbolType >, StateType > && getTransitions ( ) &&;

	/**
	 * Get a subset of the transition function of the automaton, with the source state fixed in the transitions natural representation.
	 *
	 * \param from filter the transition function based on this state as a source state
	 *
	 * \returns a subset of the transition function of the automaton with the source state fixed
	 */
	ext::multimap < ext::pair < StateType, InputSymbolType >, StateType > getTransitionsFromState ( const StateType & from ) const;

	/**
	 * Get the transition function of the automaton, with the target state fixed in the transitions natural representation.
	 *
	 * \param to filter the transition function based on this state as a source state
	 *
	 * \returns a subset of the transition function of the automaton with the target state fixed
	 */
	ext::multimap < ext::pair < StateType, InputSymbolType >, StateType > getTransitionsToState ( const StateType & to ) const;

	/**
	 * \brief Determines whether the automaton is deterministic.
	 *
	 * the automaton is deterministic if and only if:
	 * \li \c size of transition function \delta (from state, input symbol) \leq 1
	 *
	 * \return true if the automaton is deterministic, false otherwise
	 */
	bool isDeterministic ( ) const;

	/**
	 * The actual compare method
	 *
	 * \param other the other instance
	 *
	 * \returns the actual relation between two by type same automata instances
	 */
	int compare ( const InputDrivenNPDA & other ) const;

	/**
	 * Print this object as raw representation to ostream.
	 *
	 * \param out ostream where to print
	 * \param instance object to print
	 *
	 * \returns modified output stream
	 */
	friend std::ostream & operator << ( std::ostream & out, const InputDrivenNPDA & instance ) {
		return out << "(InputDrivenNPDA "
			   << " states = " << instance.getStates ( )
			   << " inputAlphabet = " << instance.getInputAlphabet ( )
			   << " initialState = " << instance.getInitialState ( )
			   << " finalStates = " << instance.getFinalStates ( )
			   << " pushdownStoreAlphabet = " << instance.getPushdownStoreAlphabet ( )
			   << " initialSymbol = " << instance.getInitialSymbol ( )
			   << " transitions = " << instance.getTransitions ( )
			   << " inputSymbolToPushdownStoreOperation = " << instance.getPushdownStoreOperations ( )
			   << ")";
	}

	/**
	 * Casts this instance to as compact as possible string representation.
	 *
	 * \returns string representation of the object
	 */
	explicit operator std::string ( ) const;
};

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >::InputDrivenNPDA ( ext::set < StateType > states, ext::set < InputSymbolType > inputAlphabet, ext::set < PushdownStoreSymbolType > pushdownStoreAlphabet, StateType initialState, PushdownStoreSymbolType initialSymbol, ext::set < StateType > finalStates ) : core::Components < InputDrivenNPDA, ext::set < InputSymbolType >, component::Set, InputAlphabet, ext::set < PushdownStoreSymbolType >, component::Set, PushdownStoreAlphabet, PushdownStoreSymbolType, component::Value, InitialSymbol, ext::set < StateType >, component::Set, std::tuple < States, FinalStates >, StateType, component::Value, InitialState > ( std::move ( inputAlphabet ), std::move ( pushdownStoreAlphabet ), std::move ( initialSymbol ), std::move ( states ), std::move ( finalStates ), std::move ( initialState ) ) {
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >::InputDrivenNPDA(StateType initialState, PushdownStoreSymbolType initialPushdownSymbol) : InputDrivenNPDA ( ext::set < StateType > { initialState }, ext::set < InputSymbolType > { }, ext::set < PushdownStoreSymbolType > { initialPushdownSymbol }, initialState, initialPushdownSymbol, ext::set < StateType > { }) {
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
void InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >::checkPushdownStoreOperation(const InputSymbolType& input, const ext::vector<PushdownStoreSymbolType>& pop, const ext::vector<PushdownStoreSymbolType>& push) {
	if (! getInputAlphabet().count(input)) {
		throw AutomatonException("Input symbol \"" + ext::to_string ( input ) + "\" doesn't exist.");
	}

	for(const PushdownStoreSymbolType& popSymbol : pop) {
		if (! getPushdownStoreAlphabet().count(popSymbol)) {
			throw AutomatonException("Pushdown store symbol \"" + ext::to_string ( popSymbol ) + "\" doesn't exist.");
		}
	}

	for(const PushdownStoreSymbolType& pushSymbol : push) {
		if (! getPushdownStoreAlphabet().count(pushSymbol)) {
			throw AutomatonException("Pushdown store symbol \"" + ext::to_string ( pushSymbol ) + "\" doesn't exist.");
		}
	}
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
bool InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >::setPushdownStoreOperation ( InputSymbolType input, ext::vector<PushdownStoreSymbolType> pop, ext::vector<PushdownStoreSymbolType> push) {
	checkPushdownStoreOperation(input, pop, push);
	return inputSymbolToPushdownStoreOperation.insert ( input, ext::make_pair( pop, push ) ).second;
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
void InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >::setPushdownStoreOperations(ext::map < InputSymbolType, ext::pair<ext::vector<PushdownStoreSymbolType>, ext::vector<PushdownStoreSymbolType>>> operations) {
	ext::set < InputSymbolType> removed;
	std::set_difference(getInputAlphabet().begin(), getInputAlphabet().end(), ext::key_begin(operations), ext::key_end(operations), std::inserter(removed, removed.end()));

	for(const InputSymbolType& removedSymbol : removed) {
		clearPushdownStoreOperation(removedSymbol);
	}

	for(const auto& added : operations) {
		checkPushdownStoreOperation(added.first, added.second.first, added.second.second);
	}

	inputSymbolToPushdownStoreOperation = std::move(operations);
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
bool InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >::clearPushdownStoreOperation(const InputSymbolType& input) {
	for ( const std::pair < const ext::pair < StateType, InputSymbolType >, StateType > & transition : transitions) {
		if (transition.first.second == input)
			throw AutomatonException("Input symbol \"" + ext::to_string ( input ) + "\" is used.");
	}

	return inputSymbolToPushdownStoreOperation.erase(input);
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
const ext::map < InputSymbolType, ext::pair < ext::vector < PushdownStoreSymbolType >, ext::vector < PushdownStoreSymbolType > > > & InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >::getPushdownStoreOperations ( ) const & {
	return inputSymbolToPushdownStoreOperation;
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
ext::map < InputSymbolType, ext::pair < ext::vector < PushdownStoreSymbolType >, ext::vector < PushdownStoreSymbolType > > > && InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >::getPushdownStoreOperations ( ) && {
	return std::move ( inputSymbolToPushdownStoreOperation );
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
bool InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >::addTransition(StateType from, InputSymbolType input, StateType to) {
	if (! getStates().count(from))
		throw AutomatonException("State \"" + ext::to_string ( from ) + "\" doesn't exist.");

	if (! getInputAlphabet().count(input))
		throw AutomatonException("Input symbol \"" + ext::to_string ( input ) + "\" doesn't exist.");

	if (! getPushdownStoreOperations().count(input))
		throw AutomatonException("Input symbol \"" + ext::to_string ( input ) + "\" doesn't exist.");

	if (! getStates().count(to))
		throw AutomatonException("State \"" + ext::to_string ( to ) + "\" doesn't exist.");

	auto upper_bound = transitions.upper_bound ( ext::tie ( from, input ) );
	auto lower_bound = transitions.lower_bound ( ext::tie ( from, input ) );
	auto iter = std::lower_bound ( lower_bound, upper_bound, to, [ ] ( const auto & transition, const auto & target ) { return transition.second < target; } );
	if ( iter != upper_bound && to >= iter->second )
		return false;

	ext::pair < StateType, InputSymbolType > key = ext::make_pair ( std::move ( from ), std::move ( input ) );
	transitions.insert ( iter, std::make_pair ( std::move ( key ), std::move ( to ) ) );
	return true;
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
bool InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >::removeTransition(const StateType& from, const InputSymbolType& input, const StateType& to) {
	auto upper_bound = transitions.upper_bound ( ext::tie ( from, input ) );
	auto lower_bound = transitions.lower_bound ( ext::tie ( from, input ) );
	auto iter = std::find_if ( lower_bound, upper_bound, [ & ] ( const auto & transition ) { return transition.second == to; } );
	if ( iter == upper_bound )
		return false;

	transitions.erase ( iter );
	return true;
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
const ext::multimap < ext::pair < StateType, InputSymbolType >, StateType > & InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >::getTransitions ( ) const & {
	return transitions;
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
ext::multimap < ext::pair < StateType, InputSymbolType >, StateType > && InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >::getTransitions ( ) && {
	return std::move ( transitions );
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
ext::multimap<ext::pair<StateType, InputSymbolType>, StateType > InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >::getTransitionsFromState(const StateType& from) const {
	if (! getStates().count(from))
		throw AutomatonException("State \"" + ext::to_string ( from ) + "\" doesn't exist");

	ext::multimap<ext::pair<StateType, InputSymbolType>, StateType > transitionsFromState;
	for ( const std::pair < const ext::pair < StateType, InputSymbolType >, StateType > & transition : transitions ) {
		if (transition.first.first == from) {
			transitionsFromState.insert ( transition.first, transition.second );
		}
	}

	return transitionsFromState;
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
ext::multimap<ext::pair<StateType, InputSymbolType>, StateType > InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >::getTransitionsToState(const StateType& to) const {
	if (! getStates().count(to))
		throw AutomatonException("State \"" + ext::to_string ( to ) + "\" doesn't exist");

	ext::multimap<ext::pair<StateType, InputSymbolType>, StateType > transitionsToState;
	for ( const std::pair<const ext::pair<StateType, InputSymbolType>, StateType >& transition : transitions) {
		if (transition.second == to ) {
			transitionsToState.insert ( transition.first, transition.second );
		}
	}

	return transitionsToState;
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
bool InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >::isDeterministic() const {
	if ( transitions.empty ( ) )
		return true;

	for ( auto iter = transitions.begin ( ); std::next ( iter ) != transitions.end ( ); ++ iter )
		if ( iter->first == std::next ( iter )->first )
			return false;

	return true;
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
int InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >::compare(const InputDrivenNPDA& other) const {
	auto first = ext::tie(getStates(), getInputAlphabet(), getInitialState(), getFinalStates(), getPushdownStoreAlphabet(), getInitialSymbol(), getPushdownStoreOperations(), transitions);
	auto second = ext::tie(other.getStates(), other.getInputAlphabet(), other.getInitialState(), other.getFinalStates(), other.getPushdownStoreAlphabet(), other.getInitialSymbol(), other.getPushdownStoreOperations(), other.transitions);

	static ext::compare<decltype(first)> comp;
	return comp(first, second);
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >::operator std::string ( ) const {
	std::stringstream ss;
	ss << *this;
	return ss.str();
}

} /* namespace automaton */

namespace core {

/**
 * Helper class specifying constraints for the automaton's internal input alphabet component.
 *
 * \tparam InputSymbolType used for the terminal alphabet of the automaton.
 * \tparam PushdownSymbolType used for the pushdown store alphabet of the automaton.
 * \tparam StateType used for the terminal alphabet of the automaton.
 */
template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
class SetConstraint< automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >, InputSymbolType, automaton::InputAlphabet > {
public:
	/**
	 * Returns true if the symbol is still used in some transition of the automaton.
	 *
	 * \param automaton the tested automaton
	 * \param symbol the tested symbol
	 *
	 * \returns true if the symbol is used, false othervise
	 */
	static bool used ( const automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const InputSymbolType & symbol ) {
		for ( const std::pair < const ext::pair < StateType, InputSymbolType >, StateType > & transition : automaton.getTransitions ( ) )
			if (transition.first.second == symbol)
				return true;

		return false;
	}

	/**
	 * Returns true as all symbols are possibly available to be elements of the input alphabet.
	 *
	 * \param automaton the tested automaton
	 * \param symbol the tested symbol
	 *
	 * \returns true
	 */
	static bool available ( const automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType > &, const InputSymbolType & ) {
		return true;
	}

	/**
	 * All symbols are valid as input symbols.
	 *
	 * \param automaton the tested automaton
	 * \param symbol the tested symbol
	 */
	static void valid ( const automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType > &, const InputSymbolType & ) {
	}
};

/**
 * Helper class specifying constraints for the automaton's internal pushdown store alphabet component.
 *
 * \tparam InputSymbolType used for the terminal alphabet of the automaton.
 * \tparam PushdownSymbolType used for the pushdown store alphabet of the automaton.
 * \tparam StateType used for the terminal alphabet of the automaton.
 */
template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
class SetConstraint< automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >, PushdownStoreSymbolType, automaton::PushdownStoreAlphabet > {
public:
	/**
	 * Returns true if the symbol is still used in some transition of the automaton.
	 *
	 * \param automaton the tested automaton
	 * \param symbol the tested symbol
	 *
	 * \returns true if the symbol is used, false othervise
	 */
	static bool used ( const automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const PushdownStoreSymbolType & symbol ) {
		for (const auto& pushdownStoreOperation : automaton.getPushdownStoreOperations()) {
			if (std::find(pushdownStoreOperation.second.first.begin(), pushdownStoreOperation.second.first.end(), symbol) != pushdownStoreOperation.second.first.end())
				return true;
			if (std::find(pushdownStoreOperation.second.second.begin(), pushdownStoreOperation.second.second.end(), symbol) != pushdownStoreOperation.second.second.end())
				return true;
		}

		if(automaton.getInitialSymbol() == symbol)
			return true;

		return false;
	}

	/**
	 * Returns true as all symbols are possibly available to be elements of the pushdown store alphabet.
	 *
	 * \param automaton the tested automaton
	 * \param symbol the tested symbol
	 *
	 * \returns true
	 */
	static bool available ( const automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType > &, const PushdownStoreSymbolType & ) {
		return true;
	}

	/**
	 * All symbols are valid as pushdown store symbols.
	 *
	 * \param automaton the tested automaton
	 * \param symbol the tested symbol
	 */
	static void valid ( const automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType > &, const PushdownStoreSymbolType & ) {
	}
};

/**
 * Helper class specifying constraints for the automaton's internal pushdown store initial element.
 *
 * \tparam InputSymbolType used for the terminal alphabet of the automaton.
 * \tparam PushdownSymbolType used for the pushdown store alphabet of the automaton.
 * \tparam StateType used for the terminal alphabet of the automaton.
 */
template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
class ElementConstraint< automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >, PushdownStoreSymbolType, automaton::InitialSymbol > {
public:
	/**
	 * Determines whether the initial pushdown store symbol is available in the automaton's pushdown store alphabet.
	 *
	 * \param automaton the tested automaton
	 * \param symbol the tested symbol
	 *
	 * \returns true if the pushdown store symbol is already in the pushdown store alphabet of the automaton
	 */
	static bool available ( const automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const PushdownStoreSymbolType & symbol ) {
		return automaton.template accessComponent < automaton::PushdownStoreAlphabet > ( ).get ( ).count ( symbol );
	}

	/**
	 * All pushdown store symbols are valid as an initial pusdown store symbol of the automaton.
	 *
	 * \param automaton the tested automaton
	 * \param symbol the tested symbol
	 */
	static void valid ( const automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType > &, const PushdownStoreSymbolType & ) {
	}
};

/**
 * Helper class specifying constraints for the automaton's internal states component.
 *
 * \tparam InputSymbolType used for the terminal alphabet of the automaton.
 * \tparam PushdownSymbolType used for the pushdown store alphabet of the automaton.
 * \tparam StateType used for the terminal alphabet of the automaton.
 */
template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
class SetConstraint< automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >, StateType, automaton::States > {
public:
	/**
	 * Returns true if the state is still used in some transition of the automaton.
	 *
	 * \param automaton the tested automaton
	 * \param state the tested state
	 *
	 * \returns true if the state is used, false othervise
	 */
	static bool used ( const automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const StateType & state ) {
		if ( automaton.getInitialState ( ) == state )
			return true;

		if ( automaton.getFinalStates ( ).count ( state ) )
			return true;

		for ( const std::pair < const ext::pair < StateType, InputSymbolType >, StateType > & transition : automaton.getTransitions ( ) )
			if (transition.first.first == state || transition.second == state )
				return true;

		return false;
	}

	/**
	 * Returns true as all states are possibly available to be elements of the states.
	 *
	 * \param automaton the tested automaton
	 * \param state the tested state
	 *
	 * \returns true
	 */
	static bool available ( const automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType > &, const StateType & ) {
		return true;
	}

	/**
	 * All states are valid as a state of the automaton.
	 *
	 * \param automaton the tested automaton
	 * \param state the tested state
	 */
	static void valid ( const automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType > &, const StateType & ) {
	}
};

/**
 * Helper class specifying constraints for the automaton's internal final states component.
 *
 * \tparam InputSymbolType used for the terminal alphabet of the automaton.
 * \tparam PushdownSymbolType used for the pushdown store alphabet of the automaton.
 * \tparam StateType used for the terminal alphabet of the automaton.
 */
template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
class SetConstraint< automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >, StateType, automaton::FinalStates > {
public:
	/**
	 * Returns false. Final state is only a mark that the automaton itself does require further.
	 *
	 * \param automaton the tested automaton
	 * \param state the tested state
	 *
	 * \returns false
	 */
	static bool used ( const automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType > &, const StateType & ) {
		return false;
	}

	/**
	 * Determines whether the state is available in the automaton's states set.
	 *
	 * \param automaton the tested automaton
	 * \param state the tested state
	 *
	 * \returns true if the state is already in the set of states of the automaton
	 */
	static bool available ( const automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const StateType & state ) {
		return automaton.template accessComponent < automaton::States > ( ).get ( ).count ( state );
	}

	/**
	 * All states are valid as a final state of the automaton.
	 *
	 * \param automaton the tested automaton
	 * \param state the tested state
	 */
	static void valid ( const automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType > &, const StateType & ) {
	}
};

/**
 * Helper class specifying constraints for the automaton's internal initial state element.
 *
 * \tparam InputSymbolType used for the terminal alphabet of the automaton.
 * \tparam PushdownSymbolType used for the pushdown store alphabet of the automaton.
 * \tparam StateType used for the terminal alphabet of the automaton.
 */
template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
class ElementConstraint< automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType >, StateType, automaton::InitialState > {
public:
	/**
	 * Determines whether the state is available in the automaton's states set.
	 *
	 * \param automaton the tested automaton
	 * \param state the tested state
	 *
	 * \returns true if the state is already in the set of states of the automaton
	 */
	static bool available ( const automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const StateType & state ) {
		return automaton.template accessComponent < automaton::States > ( ).get ( ).count ( state );
	}

	/**
	 * All states are valid as an initial state of the automaton.
	 *
	 * \param automaton the tested automaton
	 * \param state the tested state
	 */
	static void valid ( const automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType > &, const StateType & ) {
	}
};

/**
 * Helper for normalisation of types specified by templates used as internal datatypes of symbols and states.
 *
 * \returns new instance of the automaton with default template parameters or unmodified instance if the template parameters were already the default ones
 */
template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
struct normalize < automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType > > {
	static automaton::InputDrivenNPDA < > eval ( automaton::InputDrivenNPDA < InputSymbolType, PushdownStoreSymbolType, StateType > && value ) {
		ext::set < DefaultSymbolType > alphabet = alphabet::SymbolNormalize::normalizeAlphabet ( std::move ( value ).getInputAlphabet ( ) );
		ext::set < DefaultSymbolType > pushdownAlphabet = alphabet::SymbolNormalize::normalizeAlphabet ( std::move ( value ).getPushdownStoreAlphabet ( ) );
		DefaultSymbolType initialSymbol = alphabet::SymbolNormalize::normalizeSymbol ( std::move ( value ).getInitialSymbol ( ) );
		ext::set < DefaultStateType > states = automaton::AutomatonNormalize::normalizeStates ( std::move ( value ).getStates ( ) );
		DefaultStateType initialState = automaton::AutomatonNormalize::normalizeState ( std::move ( value ).getInitialState ( ) );
		ext::set < DefaultStateType > finalStates = automaton::AutomatonNormalize::normalizeStates ( std::move ( value ).getFinalStates ( ) );

		automaton::InputDrivenNPDA < > res ( std::move ( states ), std::move ( alphabet ), std::move ( pushdownAlphabet ), std::move ( initialState ), std::move ( initialSymbol ), std::move ( finalStates ) );

		for ( std::pair < InputSymbolType, ext::pair < ext::vector < PushdownStoreSymbolType >, ext::vector < PushdownStoreSymbolType > > > && pushdownOperation : ext::make_mover ( std::move ( value ).getPushdownStoreOperations ( ) ) ) {
			DefaultSymbolType target = alphabet::SymbolNormalize::normalizeSymbol ( std::move ( pushdownOperation.first ) );
			ext::vector < DefaultSymbolType > pop = alphabet::SymbolNormalize::normalizeSymbols ( std::move ( pushdownOperation.second.first ) );
			ext::vector < DefaultSymbolType > push = alphabet::SymbolNormalize::normalizeSymbols ( std::move ( pushdownOperation.second.second ) );

			res.setPushdownStoreOperation ( std::move ( target ), std::move ( pop ), std::move ( push ) );
		}

		for ( std::pair < ext::pair < StateType, InputSymbolType >, StateType > && transition : ext::make_mover ( std::move ( value ).getTransitions ( ) ) ) {
			DefaultStateType targets = automaton::AutomatonNormalize::normalizeState ( std::move ( transition.second ) );

			DefaultStateType from = automaton::AutomatonNormalize::normalizeState ( std::move ( transition.first.first ) );
			DefaultSymbolType input = alphabet::SymbolNormalize::normalizeSymbol ( std::move ( transition.first.second ) );

			res.addTransition ( std::move ( from ), std::move ( input ), std::move ( targets ) );
		}

		return res;
	}
};

} /* namespace core */

extern template class automaton::InputDrivenNPDA < >;

#endif /* INPUT_DRIVEN_NPDA_H_ */
