/*
 * RegExpEpsilon.h
 *
 *  Created on: 19. 1. 2014
 *      Author: Jan Travnicek
 */

#ifndef REG_EXP_EPSILON_H_
#define REG_EXP_EPSILON_H_

#include <regexp/formal/FormalRegExp.h>
#include <regexp/formal/FormalRegExpElements.h>
#include <regexp/unbounded/UnboundedRegExp.h>
#include <regexp/unbounded/UnboundedRegExpElements.h>

namespace regexp {

namespace properties {

/**
 * Determines whether regular expression (or its subtree) describes a language containing epsilon (\eps \in h(regexp)).
 *
 */
class RegExpEpsilon {
public:
	/**
	 * Determines whether regular expression describes a language containing epsilon (\eps \in h(regexp)).
	 *
	 * \tparam SymbolType the type of symbol in the tested regular expression
	 *
	 * \param regexp the regexp to test
	 *
	 * \return true of the language described by the regular expression contains epsilon
	 */
	template < class SymbolType >
	static bool languageContainsEpsilon(const regexp::FormalRegExpElement < SymbolType > & regexp);

	/**
	 * \overload
	 */
	template < class SymbolType >
	static bool languageContainsEpsilon(const regexp::FormalRegExpStructure < SymbolType > & regexp);

	/**
	 * \overload
	 */
	template < class SymbolType >
	static bool languageContainsEpsilon(const regexp::FormalRegExp < SymbolType > & regexp);

	/**
	 * \overload
	 */
	template < class SymbolType >
	static bool languageContainsEpsilon(const regexp::UnboundedRegExpElement < SymbolType > & regexp);

	/**
	 * \overload
	 */
	template < class SymbolType >
	static bool languageContainsEpsilon(const regexp::UnboundedRegExpStructure < SymbolType > & regexp);

	/**
	 * \overload
	 */
	template < class SymbolType >
	static bool languageContainsEpsilon(const regexp::UnboundedRegExp < SymbolType > & regexp);

	template < class SymbolType >
	class Unbounded {
	public:
		static bool visit(const regexp::UnboundedRegExpAlternation < SymbolType > & alternation);
		static bool visit(const regexp::UnboundedRegExpConcatenation < SymbolType > & concatenation);
		static bool visit(const regexp::UnboundedRegExpIteration < SymbolType > & iteration);
		static bool visit(const regexp::UnboundedRegExpSymbol < SymbolType > & symbol);
		static bool visit(const regexp::UnboundedRegExpEmpty < SymbolType > & empty);
		static bool visit(const regexp::UnboundedRegExpEpsilon < SymbolType > & epsilon);
	};

	template < class SymbolType >
	class Formal {
	public:
		static bool visit(const regexp::FormalRegExpAlternation < SymbolType > & alternation);
		static bool visit(const regexp::FormalRegExpConcatenation < SymbolType > & concatenation);
		static bool visit(const regexp::FormalRegExpIteration < SymbolType > & iteration);
		static bool visit(const regexp::FormalRegExpSymbol < SymbolType > & symbol);
		static bool visit(const regexp::FormalRegExpEmpty < SymbolType > & empty);
		static bool visit(const regexp::FormalRegExpEpsilon < SymbolType > & epsilon);
	};
};

// ----------------------------------------------------------------------------

template < class SymbolType >
bool RegExpEpsilon::languageContainsEpsilon(const regexp::FormalRegExpElement < SymbolType > & regexp) {
	return regexp.template accept<bool, RegExpEpsilon::Formal < SymbolType >>();
}

template < class SymbolType >
bool RegExpEpsilon::languageContainsEpsilon(const regexp::FormalRegExpStructure < SymbolType > & regexp) {
	return languageContainsEpsilon(regexp.getStructure());
}

template < class SymbolType >
bool RegExpEpsilon::languageContainsEpsilon(const regexp::FormalRegExp < SymbolType > & regexp) {
	return languageContainsEpsilon(regexp.getRegExp());
}

// ----------------------------------------------------------------------------

template < class SymbolType >
bool RegExpEpsilon::languageContainsEpsilon(const regexp::UnboundedRegExpElement < SymbolType > & regexp) {
	return regexp.template accept<bool, RegExpEpsilon::Unbounded < SymbolType >>();
}

template < class SymbolType >
bool RegExpEpsilon::languageContainsEpsilon(const regexp::UnboundedRegExpStructure < SymbolType > & regexp) {
	return languageContainsEpsilon(regexp.getStructure());
}

template < class SymbolType >
bool RegExpEpsilon::languageContainsEpsilon(const regexp::UnboundedRegExp < SymbolType > & regexp) {
	return languageContainsEpsilon(regexp.getRegExp());
}

// ---------------------------------------------------------------------------

template < class SymbolType >
bool RegExpEpsilon::Unbounded < SymbolType >::visit(const regexp::UnboundedRegExpAlternation < SymbolType > & alternation) {
	return std::any_of ( alternation.getElements ( ).begin ( ), alternation.getElements ( ).end ( ), [ ] ( const UnboundedRegExpElement < SymbolType > & element ) {
		return element.template accept < bool, RegExpEpsilon::Unbounded < SymbolType > > ( );
	} );
}

template < class SymbolType >
bool RegExpEpsilon::Unbounded < SymbolType >::visit(const regexp::UnboundedRegExpConcatenation < SymbolType > & concatenation) {
	return std::all_of ( concatenation.getElements ( ).begin ( ), concatenation.getElements ( ).end ( ), [ ] ( const UnboundedRegExpElement < SymbolType > & element ) {
		return element.template accept < bool, RegExpEpsilon::Unbounded < SymbolType > > ( );
	} );
}

template < class SymbolType >
bool RegExpEpsilon::Unbounded < SymbolType >::visit(const regexp::UnboundedRegExpIteration < SymbolType > &) {
	return true;
}

template < class SymbolType >
bool RegExpEpsilon::Unbounded < SymbolType >::visit(const regexp::UnboundedRegExpSymbol < SymbolType > &) {
	return false;
}

template < class SymbolType >
bool RegExpEpsilon::Unbounded < SymbolType >::visit(const regexp::UnboundedRegExpEpsilon < SymbolType > &) {
	return true;
}

template < class SymbolType >
bool RegExpEpsilon::Unbounded < SymbolType >::visit(const regexp::UnboundedRegExpEmpty < SymbolType > &) {
	return false;
}

// ----------------------------------------------------------------------------

template < class SymbolType >
bool RegExpEpsilon::Formal < SymbolType >::visit(const regexp::FormalRegExpAlternation < SymbolType > & alternation) {
	return alternation.getLeftElement().template accept<bool, RegExpEpsilon::Formal < SymbolType >>() || alternation.getRightElement().template accept<bool, RegExpEpsilon::Formal < SymbolType >>();
}

template < class SymbolType >
bool RegExpEpsilon::Formal < SymbolType >::visit(const regexp::FormalRegExpConcatenation < SymbolType > & concatenation) {
	return concatenation.getLeftElement().template accept<bool, RegExpEpsilon::Formal < SymbolType >>() && concatenation.getRightElement().template accept<bool, RegExpEpsilon::Formal < SymbolType >>();
}

template < class SymbolType >
bool RegExpEpsilon::Formal < SymbolType >::visit(const regexp::FormalRegExpIteration < SymbolType > &) {
	return true;
}

template < class SymbolType >
bool RegExpEpsilon::Formal < SymbolType >::visit(const regexp::FormalRegExpSymbol < SymbolType > &) {
	return false;
}

template < class SymbolType >
bool RegExpEpsilon::Formal < SymbolType >::visit(const regexp::FormalRegExpEmpty < SymbolType > &) {
	return false;
}

template < class SymbolType >
bool RegExpEpsilon::Formal < SymbolType >::visit(const regexp::FormalRegExpEpsilon < SymbolType > &) {
	return true;
}

} /* namespace properties */

} /* namespace regexp */

#endif /* REG_EXP_EPSILON_H_ */
