/*
 * MinimizeDistinguishableStates.cpp
 *
 * 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: 27. 3. 2019
 *	  Author: Tomas Pecka
 */

#ifndef MINIMIZE_DISTINGUISHABLE_STATES_H_
#define MINIMIZE_DISTINGUISHABLE_STATES_H_

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

#include <automaton/FSM/DFA.h>

#include <automaton/properties/DistinguishableStates.h>
#include <automaton/properties/UndistinguishableStates.h>

namespace automaton {

namespace simplify {

/**
 * Minimization of finite automata using distinguishable states method from Hopcroft 4.4.1 - 4.4.3
 *
 * @sa automaton::simplify::Minimize
 * @sa automaton::simplify::MinimizeBrzozowski
 */
class MinimizeDistinguishableStates {
public:
	/**
	 * Minimizes the given automaton using Distinguishable States method.
	 *
	 * @tparam SymbolType Type for input symbols.
	 * @tparam StateType Type for states.
	 *
	 * @param dfa deterministic finite automaton to minimize.
	 *
	 * @return Minimal deterministic finite automaton equivalent to @p dfa
	 */
	template < class SymbolType, class StateType >
	static automaton::DFA < SymbolType, ext::set < StateType > > minimize ( const automaton::DFA < SymbolType, StateType > & dfa );

	/**
	 * Minimizes the given automaton using Distinguishable States method.
	 *
	 * @tparam SymbolType Type for input symbols.
	 * @tparam StateType Type for states.
	 *
	 * @param dfta deterministic finite tree automaton to minimize.
	 *
	 * @return Minimal deterministic tree finite automaton equivalent to @p dfa
	 */
	template < class SymbolType, class StateType >
	static automaton::DFTA < SymbolType, ext::set < StateType > > minimize ( const automaton::DFTA < SymbolType, StateType > & dfta );

private:
	/**
	 * Helper method to create mapping from state to the corresponding partition
	 *
	 * @tparam SymbolType Type for input symbols.
	 * @tparam StateType Type for states.
	 * @param partitions state partitioning
	 *
	 * @sa DistinguishableStates
	 *
	 * @return state partitioning of undistinguishable states
	 */
	template < class StateType >
	static ext::map < StateType, ext::set < StateType > > partitionMap ( const ext::set < ext::set < StateType > > & partitions );
};

template < class StateType >
ext::map < StateType, ext::set < StateType > > MinimizeDistinguishableStates::partitionMap ( const ext::set < ext::set < StateType > > & partitions ) {
	ext::map < StateType, ext::set < StateType > > res;

	for ( const auto & partition : partitions )
		for ( const StateType & state : partition )
			res [ state ] = partition;

	return res;
}

template < class SymbolType, class StateType >
automaton::DFA < SymbolType, ext::set < StateType > > MinimizeDistinguishableStates::minimize ( const automaton::DFA < SymbolType, StateType > & dfa ) {
	const ext::set < ext::set < StateType > > partitions = automaton::properties::UndistinguishableStates::undistinguishable ( dfa );
	const ext::map < StateType, ext::set < StateType > > statePartitionMap = partitionMap ( partitions );

	automaton::DFA < SymbolType, ext::set < StateType > > res ( statePartitionMap.at ( dfa.getInitialState ( ) ) );
	res.setStates ( partitions );
	res.addInputSymbols ( dfa.getInputAlphabet ( ) );
	for ( const StateType & state : dfa.getFinalStates ( ) )
		res.addFinalState ( statePartitionMap.at ( state ) );

	for ( const auto & transition : dfa.getTransitions ( ) )
		res.addTransition ( statePartitionMap.at ( transition.first.first ), transition.first.second, statePartitionMap.at ( transition.second ) );

	return res;
}

template < class SymbolType, class StateType >
automaton::DFTA < SymbolType, ext::set < StateType > > MinimizeDistinguishableStates::minimize ( const automaton::DFTA < SymbolType, StateType > & dfta ) {
	const ext::set < ext::set < StateType > > partitions = automaton::properties::UndistinguishableStates::undistinguishable ( dfta );
	const ext::map < StateType, ext::set < StateType > > statePartitionMap = partitionMap ( partitions );

	automaton::DFTA < SymbolType, ext::set < StateType > > res;
	res.setStates ( partitions );
	res.addInputSymbols ( dfta.getInputAlphabet ( ) );
	for ( const StateType & state : dfta.getFinalStates ( ) )
		res.addFinalState ( statePartitionMap.at ( state ) );

	for ( const auto & transition : dfta.getTransitions ( ) ) {
		ext::vector < ext::set < StateType > > minimalFrom;
		for ( const StateType & from : transition.first.second )
			minimalFrom.push_back ( statePartitionMap.at ( from ) );
		res.addTransition ( transition.first.first, minimalFrom, statePartitionMap.at ( transition.second ) );
	}

	return res;
}

} /* namespace simplify */

} /* namespace automaton */

#endif /* MINIMIZE_DISTINGUISHABLE_STATES_H_ */
