/*
 * InfiniteLanguage.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: 26. 3. 2019
 *	  Author: Tomas Pecka
 */

#pragma once

#include <queue>
#include <set>

#include <automaton/FSM/NFA.h>
#include <automaton/FSM/DFA.h>

#include <automaton/properties/UsefulStates.h>

namespace automaton {

namespace properties {

/**
 * Determine whether language defined by DFA is infinite.
 */
class InfiniteLanguage {
public:
	/**
	 * Detects whether the language accepted by the automaton is finite or infinite.
	 *
	 * @T the type of tested automaton
	 *
	 * @param fsm automaton
	 *
	 * @return boolean indicating infinitness of the language
	 */
	template < class T >
	static ext::require < isDFA < T > || isNFA < T >, bool > infinite ( const T & fsm );
};

template < class T >
ext::require < isDFA < T > || isNFA < T >, bool > InfiniteLanguage::infinite ( const T & fsm ) {
	using StateType = typename T::StateType;

	const ext::set < StateType > usefulStates = automaton::properties::UsefulStates::usefulStates ( fsm );

	std::queue < StateType > q;
	std::set < StateType > visited;

	q.push ( fsm.getInitialState ( ) );
	visited.insert ( fsm.getInitialState ( ) );

	while ( ! q.empty ( ) ) {
		const StateType state = std::move ( q.front ( ) );
		q.pop ( );

		for ( const auto & transition : fsm.getTransitionsFromState ( state ) ) {
			auto it = visited.find ( transition.second );
			if ( it != visited.end ( ) && usefulStates.count ( transition.second ) ) {
				return true;
			} else if ( it == visited.end ( ) ) {
				visited.insert ( transition.second );
				q.push ( transition.second );
			}
		}
	}

	return false;
}

} /* namespace properties */

} /* namespace automaton */
