#include <catch2/catch.hpp>

#include "grammar/simplify/LeftRecursionRemover.h"
#include "grammar/generate/GenerateUpToLength.h"

#include "grammar/ContextFree/CFG.h"
#include "grammar/ContextFree/EpsilonFreeCFG.h"

#include "grammar/string/ContextFree/CFG.h"
#include "grammar/string/ContextFree/EpsilonFreeCFG.h"

#include <factory/StringDataFactory.hpp>
#include <common/createUnique.hpp>

TEST_CASE ( "Left recursion remover", "[unit][algo][grammar][simplify]" ) {
	SECTION ( "Test eps-free CFG" ) {
		{
			DefaultSymbolType S = DefaultSymbolType("S");
			DefaultSymbolType A = DefaultSymbolType("A");
			DefaultSymbolType B = DefaultSymbolType("B");
			DefaultSymbolType C = DefaultSymbolType("C");
			DefaultSymbolType D = DefaultSymbolType("D");

			DefaultSymbolType a = DefaultSymbolType('a');
			DefaultSymbolType b = DefaultSymbolType('b');

			grammar::EpsilonFreeCFG < > grammar1(S);
			grammar1.setNonterminalAlphabet({S, A, B, C, D});
			grammar1.setTerminalAlphabet({a, b});

			grammar::EpsilonFreeCFG < > grammar2 = grammar::simplify::LeftRecursionRemover::remove(grammar1);

			grammar::EpsilonFreeCFG < > grammar3(S);
			grammar3.setNonterminalAlphabet({S, A, B, C, D});
			grammar3.setTerminalAlphabet({a, b});

			CHECK(grammar2 == grammar3);
		}
		{
			DefaultSymbolType S = DefaultSymbolType("S");
			DefaultSymbolType A = DefaultSymbolType("A");
			DefaultSymbolType B = DefaultSymbolType("B");
			DefaultSymbolType C = DefaultSymbolType("C");
			DefaultSymbolType D = DefaultSymbolType("D");

			DefaultSymbolType a = DefaultSymbolType('a');
			DefaultSymbolType b = DefaultSymbolType('b');
			DefaultSymbolType c = DefaultSymbolType('c');

			grammar::EpsilonFreeCFG < > grammar1(S);
			grammar1.setNonterminalAlphabet({S, A, B, C, D});
			grammar1.setTerminalAlphabet({a, b, c});
			grammar1.addRule(S, {A});
			grammar1.addRule(A, {A, a});
			grammar1.addRule(A, {A, b});
			grammar1.addRule(A, {c});

			DefaultSymbolType Aprimed = common::createUnique(A, grammar1.getTerminalAlphabet(), grammar1.getNonterminalAlphabet());

			grammar::EpsilonFreeCFG < > grammar2 = grammar::simplify::LeftRecursionRemover::remove(grammar1);

			grammar::EpsilonFreeCFG < > grammar3(S);
			grammar3.setNonterminalAlphabet({S, A, Aprimed, B, C, D});
			grammar3.setTerminalAlphabet({a, b, c});
			grammar3.addRule(S, {c, Aprimed});
			grammar3.addRule(S, {c});
			grammar3.addRule(A, {c, Aprimed});
			grammar3.addRule(A, {c});
			grammar3.addRule(Aprimed, {a, Aprimed});
			grammar3.addRule(Aprimed, {a});
			grammar3.addRule(Aprimed, {b, Aprimed});
			grammar3.addRule(Aprimed, {b});

			CHECK(grammar2 == grammar3);
		}
		{
			DefaultSymbolType S = DefaultSymbolType("S");
			DefaultSymbolType A = DefaultSymbolType("A");
			DefaultSymbolType B = DefaultSymbolType("B");
			DefaultSymbolType C = DefaultSymbolType("C");

			DefaultSymbolType a = DefaultSymbolType('a');
			DefaultSymbolType b = DefaultSymbolType('b');
			DefaultSymbolType c = DefaultSymbolType('c');

			grammar::EpsilonFreeCFG < > grammar1(S);
			grammar1.setNonterminalAlphabet({S, A, B, C});
			grammar1.setTerminalAlphabet({a, b, c});
			grammar1.addRule(S, {A});
			grammar1.addRule(A, {B, a});
			grammar1.addRule(B, {A, b});
			grammar1.addRule(A, {c});

			DefaultSymbolType Bprimed = common::createUnique(B, grammar1.getTerminalAlphabet(), grammar1.getNonterminalAlphabet());

			grammar::EpsilonFreeCFG < > grammar2 = grammar::simplify::LeftRecursionRemover::remove(grammar1);

			grammar::EpsilonFreeCFG < > grammar3(S);
			grammar3.setNonterminalAlphabet({S, A, B, Bprimed, C});
			grammar3.setTerminalAlphabet({a, b, c});
			grammar3.addRule(S, {c});
			grammar3.addRule(S, {c, b, Bprimed, a});
			grammar3.addRule(S, {c, b, a});
			grammar3.addRule(A, {B, a});
			grammar3.addRule(A, {c});
			grammar3.addRule(B, {c, b});
			grammar3.addRule(B, {c, b, Bprimed});
			grammar3.addRule(Bprimed, {a, b, Bprimed});
			grammar3.addRule(Bprimed, {a, b});

			CAPTURE ( factory::StringDataFactory::toString( grammar1 ) );
			CAPTURE ( factory::StringDataFactory::toString( grammar2 ) );
			CAPTURE ( factory::StringDataFactory::toString( grammar3 ) );

			CHECK(grammar2 == grammar3);
		}
		{
			DefaultSymbolType A = DefaultSymbolType("A");
			DefaultSymbolType B = DefaultSymbolType("B");
			DefaultSymbolType C = DefaultSymbolType("C");

			DefaultSymbolType a = DefaultSymbolType('a');
			DefaultSymbolType b = DefaultSymbolType('b');

			grammar::EpsilonFreeCFG < > grammar1(A);
			grammar1.setNonterminalAlphabet({A, B, C});
			grammar1.setTerminalAlphabet({a, b});
			grammar1.addRule(A, {B, C});
			grammar1.addRule(A, {a});
			grammar1.addRule(B, {C, A});
			grammar1.addRule(B, {A, b});
			grammar1.addRule(C, {A, B});
			grammar1.addRule(C, {C, C});
			grammar1.addRule(C, {a});

			DefaultSymbolType Bprimed = common::createUnique(B, grammar1.getTerminalAlphabet(), grammar1.getNonterminalAlphabet());
			DefaultSymbolType Cprimed = common::createUnique(C, grammar1.getTerminalAlphabet(), grammar1.getNonterminalAlphabet());

			CAPTURE ( factory::StringDataFactory::toString( grammar1 ) );

			grammar::EpsilonFreeCFG < > grammar2 = grammar::simplify::LeftRecursionRemover::remove(grammar1);

			grammar::EpsilonFreeCFG < > grammar3(A);
			grammar3.setNonterminalAlphabet({A, B, Bprimed, C, Cprimed});
			grammar3.setTerminalAlphabet({a, b});
			grammar3.addRule(A, {B, C});
			grammar3.addRule(A, {a});
			grammar3.addRule(B, {C, A});
			grammar3.addRule(B, {a, b});
			grammar3.addRule(B, {C, A, Bprimed});
			grammar3.addRule(B, {a, b, Bprimed});
			grammar3.addRule(Bprimed, {C, b, Bprimed});
			grammar3.addRule(Bprimed, {C, b});
			grammar3.addRule(C, {a, b, C, B});
			grammar3.addRule(C, {a, b, Bprimed, C, B});
			grammar3.addRule(C, {a, B});
			grammar3.addRule(C, {a});
			grammar3.addRule(C, {a, b, C, B, Cprimed});
			grammar3.addRule(C, {a, b, Bprimed, C, B, Cprimed});
			grammar3.addRule(C, {a, B, Cprimed});
			grammar3.addRule(C, {a, Cprimed});
			grammar3.addRule(Cprimed, {A, C, B, Cprimed});
			grammar3.addRule(Cprimed, {A, Bprimed, C, B, Cprimed});
			grammar3.addRule(Cprimed, {C, Cprimed});
			grammar3.addRule(Cprimed, {A, C, B});
			grammar3.addRule(Cprimed, {A, Bprimed, C, B});
			grammar3.addRule(Cprimed, {C});

			CAPTURE ( factory::StringDataFactory::toString( grammar2 ) );
			CAPTURE ( factory::StringDataFactory::toString( grammar3 ) );

			CHECK(grammar2 == grammar3);
		}
		{
			DefaultSymbolType A = DefaultSymbolType("A");
			DefaultSymbolType B = DefaultSymbolType("B");
			DefaultSymbolType C = DefaultSymbolType("C");

			DefaultSymbolType a = DefaultSymbolType('a');
			DefaultSymbolType b = DefaultSymbolType('b');

			grammar::EpsilonFreeCFG < > grammar1(A);
			grammar1.setNonterminalAlphabet({A, B, C});
			grammar1.setTerminalAlphabet({a, b});
			grammar1.addRule(A, {B, C});
			grammar1.addRule(A, {a});
			grammar1.addRule(B, {C, A});
			grammar1.addRule(B, {A, b});
			grammar1.addRule(C, {A, B});
			grammar1.addRule(C, {C, C});
			grammar1.addRule(C, {a});

			grammar::EpsilonFreeCFG < > grammar2 = grammar::simplify::LeftRecursionRemover::remove(grammar1);

			CHECK(grammar::generate::GenerateUpToLength::generate(grammar1, 7) == grammar::generate::GenerateUpToLength::generate(grammar2, 7));
		}
	}
}
