#include <catch2/catch.hpp>

#include <alib/list>
#include <alib/pair>

#include "regexp/convert/ToAutomatonDerivation.h"
#include "regexp/convert/ToAutomatonGlushkov.h"
#include "regexp/convert/ToAutomatonThompson.h"
#include "automaton/convert/ToRegExpAlgebraic.h"
#include "automaton/determinize/Determinize.h"
#include "automaton/simplify/Minimize.h"
#include "automaton/simplify/Normalize.h"
#include "automaton/simplify/EpsilonRemoverOutgoing.h"

#include "regexp/unbounded/UnboundedRegExp.h"

#include "automaton/FSM/NFA.h"
#include <factory/StringDataFactory.hpp>
#include <regexp/string/UnboundedRegExp.h>


TEST_CASE ( "RegExp to automaton", "[unit][algo][regexp][convert]" ) {
	const std::string input  = "a+a* b*";

	SECTION ( "Thompson" ) {
		regexp::UnboundedRegExp < > regexp1 = factory::StringDataFactory::fromString (input);

		automaton::EpsilonNFA < > enfa1 = regexp::convert::ToAutomatonThompson::convert(regexp1);

		regexp::UnboundedRegExp < > regexp2 ( automaton::convert::ToRegExpAlgebraic::convert( enfa1) );

		automaton::EpsilonNFA < > enfa2 = regexp::convert::ToAutomatonThompson::convert(regexp2);

		automaton::MultiInitialStateNFA < > nfa1 = automaton::simplify::EpsilonRemoverOutgoing::remove(enfa1);
		automaton::MultiInitialStateNFA < > nfa2 = automaton::simplify::EpsilonRemoverOutgoing::remove(enfa2);

		automaton::DFA < DefaultSymbolType, ext::set < DefaultStateType > > dfa1 = automaton::determinize::Determinize::determinize(nfa1);
		automaton::DFA < DefaultSymbolType, ext::set < DefaultStateType > > dfa2 = automaton::determinize::Determinize::determinize(nfa2);

		automaton::DFA< DefaultSymbolType, unsigned > mdfa1 = automaton::simplify::Normalize::normalize(automaton::simplify::Minimize::minimize(dfa1));
		automaton::DFA< DefaultSymbolType, unsigned > mdfa2 = automaton::simplify::Normalize::normalize(automaton::simplify::Minimize::minimize(dfa2));

		CHECK( mdfa1 == mdfa2);
	}

	SECTION ( "Glushkov" ) {
		regexp::UnboundedRegExp < > regexp1 = factory::StringDataFactory::fromString ( input );

		automaton::NFA < DefaultSymbolType, ext::pair < DefaultSymbolType, unsigned > > nfa1 = regexp::convert::ToAutomatonGlushkov::convert ( regexp1 );

		regexp::UnboundedRegExp < > regexp2 = automaton::convert::ToRegExpAlgebraic::convert ( nfa1 );

		automaton::NFA < DefaultSymbolType, ext::pair < DefaultSymbolType, unsigned > > nfa2 = regexp::convert::ToAutomatonGlushkov::convert(regexp2);

		automaton::DFA < DefaultSymbolType, ext::set < ext::pair < DefaultSymbolType, unsigned > > > dfa1 = automaton::determinize::Determinize::determinize(nfa1);
		automaton::DFA < DefaultSymbolType, ext::set < ext::pair < DefaultSymbolType, unsigned > > > dfa2 = automaton::determinize::Determinize::determinize(nfa2);

		automaton::DFA< DefaultSymbolType, unsigned > mdfa1 = automaton::simplify::Normalize::normalize(automaton::simplify::Minimize::minimize(dfa1));
		automaton::DFA< DefaultSymbolType, unsigned > mdfa2 = automaton::simplify::Normalize::normalize(automaton::simplify::Minimize::minimize(dfa2));

		CHECK( mdfa1 == mdfa2);
	}

	SECTION ( "Brzozowski" ) {
		regexp::UnboundedRegExp < > regexp1 = factory::StringDataFactory::fromString (input);

		automaton::DFA < DefaultSymbolType, unsigned > dfa1 = regexp::convert::ToAutomatonDerivation::convert(regexp1);

		regexp::UnboundedRegExp < > regexp2( automaton::convert::ToRegExpAlgebraic::convert ( core::normalize < automaton::DFA < DefaultSymbolType, unsigned > >::eval ( automaton::DFA < DefaultSymbolType, unsigned > ( dfa1 ) ) ) );

		automaton::DFA < DefaultSymbolType, unsigned > dfa2 = regexp::convert::ToAutomatonDerivation::convert(regexp2);

		automaton::DFA < DefaultSymbolType, unsigned > mdfa1_2 = automaton::simplify::Minimize::minimize(dfa1);
		automaton::DFA < DefaultSymbolType, unsigned > mdfa1_3 = automaton::simplify::Normalize::normalize(mdfa1_2);

		automaton::DFA < DefaultSymbolType, unsigned > mdfa2_2 = automaton::simplify::Minimize::minimize(dfa2);
		automaton::DFA < DefaultSymbolType, unsigned > mdfa2_3 = automaton::simplify::Normalize::normalize(mdfa2_2);

		CHECK( mdfa1_3 == mdfa2_3);
	}
}
