#include <catch2/catch.hpp>

#include "grammar/simplify/ToCNF.h"

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

#include <factory/StringDataFactory.hpp>

#include <grammar/Grammar.h>

TEST_CASE ( "Test to CNF", "[unit][algo][grammar][simplify]" ) {
	SECTION ( "Test CFG" ) {
		{
			std::string S = "S";
			std::string A = "A";
			std::string B = "B";
			std::string C = "C";
			std::string D = "D";

			char a = 'a';
			char b = 'b';

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

			grammar::CNF < char, ext::vector < ext::variant < char, std::string > > > grammar2 = grammar::simplify::ToCNF::convert ( grammar1 );

			grammar::CNF < char, ext::vector < ext::variant < char, std::string > > > grammar3 ( ext::vector < ext::variant < char, std::string > > { S } );
			grammar3.setNonterminalAlphabet({{S}, {A}, {B}, {C}, {D}, {a}, {b}});
			grammar3.setTerminalAlphabet({a, b});
			grammar3.addRule ( ext::vector < ext::variant < char, std::string > > { a }, a);
			grammar3.addRule ( ext::vector < ext::variant < char, std::string > > { b }, b);

			CAPTURE ( grammar2, grammar3 );

			CHECK(grammar2 == grammar3);
		}
		{
			std::string S = "S";
			std::string X = "X";
			std::string Y = "Y";

			char a = 'a';
			char b = 'b';
			char c = 'c';

			grammar::CFG < char, std::string > grammar1 ( S );
			grammar1.setNonterminalAlphabet({S, X, Y});
			grammar1.setTerminalAlphabet({a, b, c});
			grammar1.addRule(S, {a, X, b, X});
			grammar1.addRule(X, {a, Y});
			grammar1.addRule(X, {b, Y});
			grammar1.addRule(X, {});
			grammar1.addRule(Y, {X});
			grammar1.addRule(Y, {c});

			grammar::CNF < char, ext::vector < ext::variant < char, std::string > > > grammar2 = grammar::simplify::ToCNF::convert(grammar1);

			ext::vector < ext::variant < char, std::string > > Xb { X, b };
			ext::vector < ext::variant < char, std::string > > aX { a, X };
			ext::vector < ext::variant < char, std::string > > bX { b, X };

			grammar::CNF < char, ext::vector < ext::variant < char, std::string > > > grammar3 ( ext::vector < ext::variant < char, std::string > > { S } );
			grammar3.setNonterminalAlphabet({{S}, {X}, {Y}, {a}, {b}, {c}, {Xb}, {aX}, {bX}});
			grammar3.setTerminalAlphabet({a, b, c});
			grammar3.addRule( ext::vector < ext::variant < char, std::string > > { S }, ext::make_pair ( ext::vector < ext::variant < char, std::string > > { aX }, ext::vector < ext::variant < char, std::string > > { bX } ) );
			grammar3.addRule( ext::vector < ext::variant < char, std::string > > { S }, ext::make_pair ( ext::vector < ext::variant < char, std::string > > { a }, ext::vector < ext::variant < char, std::string > > { bX } ) );
			grammar3.addRule( ext::vector < ext::variant < char, std::string > > { S }, ext::make_pair ( ext::vector < ext::variant < char, std::string > > { a }, ext::vector < ext::variant < char, std::string > > { Xb } ) );
			grammar3.addRule( ext::vector < ext::variant < char, std::string > > { S }, ext::make_pair ( ext::vector < ext::variant < char, std::string > > { a }, ext::vector < ext::variant < char, std::string > > { b } ) );
			grammar3.addRule( ext::vector < ext::variant < char, std::string > > { aX }, ext::make_pair ( ext::vector < ext::variant < char, std::string > > { a }, ext::vector < ext::variant < char, std::string > > { X } ) );
			grammar3.addRule( ext::vector < ext::variant < char, std::string > > { bX }, ext::make_pair ( ext::vector < ext::variant < char, std::string > > { b }, ext::vector < ext::variant < char, std::string > > { X } ) );
			grammar3.addRule( ext::vector < ext::variant < char, std::string > > { Xb }, ext::make_pair ( ext::vector < ext::variant < char, std::string > > { X }, ext::vector < ext::variant < char, std::string > > { b } ) );
			grammar3.addRule( ext::vector < ext::variant < char, std::string > > { X }, ext::make_pair ( ext::vector < ext::variant < char, std::string > > { a }, ext::vector < ext::variant < char, std::string > > { Y } ) );
			grammar3.addRule( ext::vector < ext::variant < char, std::string > > { X }, ext::make_pair ( ext::vector < ext::variant < char, std::string > > { b }, ext::vector < ext::variant < char, std::string > > { Y } ) );
			grammar3.addRule( ext::vector < ext::variant < char, std::string > > { X }, a);
			grammar3.addRule( ext::vector < ext::variant < char, std::string > > { X }, b);
			grammar3.addRule( ext::vector < ext::variant < char, std::string > > { Y }, ext::make_pair ( ext::vector < ext::variant < char, std::string > > { a }, ext::vector < ext::variant < char, std::string > > { Y } ) );
			grammar3.addRule( ext::vector < ext::variant < char, std::string > > { Y }, ext::make_pair ( ext::vector < ext::variant < char, std::string > > { b }, ext::vector < ext::variant < char, std::string > > { Y } ) );
			grammar3.addRule( ext::vector < ext::variant < char, std::string > > { Y }, a);
			grammar3.addRule( ext::vector < ext::variant < char, std::string > > { Y }, b);
			grammar3.addRule( ext::vector < ext::variant < char, std::string > > { Y }, c);
			grammar3.addRule( ext::vector < ext::variant < char, std::string > > { a }, a);
			grammar3.addRule( ext::vector < ext::variant < char, std::string > > { b }, b);
			grammar3.addRule( ext::vector < ext::variant < char, std::string > > { c }, c);

			CAPTURE ( grammar2, grammar3 );

			CHECK(grammar2 == grammar3);
		}
	}
}

