#include <catch2/catch.hpp>

#include "grammar/simplify/EpsilonRemover.h"

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

TEST_CASE ( "Epsilon remover", "[unit][algo][grammar][simplify]" ) {
	SECTION ( "Test 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::CFG < > grammar1(S);
			grammar1.setNonterminalAlphabet({S, A, B, C, D});
			grammar1.setTerminalAlphabet({a, b});

			grammar::EpsilonFreeCFG < > grammar2 = grammar::simplify::EpsilonRemover::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');
			DefaultSymbolType d = DefaultSymbolType('d');

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

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

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

			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');
			DefaultSymbolType d = DefaultSymbolType('d');

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

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

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

			CHECK(grammar2 == grammar3);
		}
	}
}
