#include <catch2/catch.hpp>

#include "grammar/ContextFree/CFG.h"
#include "grammar/parsing/CornerSubstitution.h"

TEST_CASE ( "Corner Substitution", "[unit][grammar]" ) {
	SECTION ( "Test 1" ) {
		DefaultSymbolType A = DefaultSymbolType ( "A" );
		DefaultSymbolType B = DefaultSymbolType ( "B" );
		DefaultSymbolType C = DefaultSymbolType ( "C" );

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

		grammar::CFG < > grammar ( A );

		grammar.setTerminalAlphabet ( { a, b, c, d } );
		grammar.setNonterminalAlphabet ( { A, B, C } );
		grammar.setInitialSymbol ( A );

		grammar.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, B } );
		grammar.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { C, B } );
		grammar.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { c, B } );
		grammar.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { d } );
		grammar.addRule ( C, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, C } );
		grammar.addRule ( C, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { b, B } );

		grammar::CFG < > res = grammar;
		grammar::parsing::CornerSubstitution::cornerSubstitution ( res, a, A );

		grammar::CFG < > comp ( A );

		comp.setTerminalAlphabet ( { a, b, c, d } );
		comp.setNonterminalAlphabet ( { A, B, C } );
		comp.setInitialSymbol ( A );

		comp.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, B } );
		comp.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, C, B } );
		comp.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { b, B, B } );
		comp.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { c, B } );
		comp.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { d } );
		comp.addRule ( C, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, C } );
		comp.addRule ( C, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { b, B } );

		INFO ( "Res: " << res << ", " << comp );
		CHECK ( res == comp );
	}

	SECTION ( "Test 2" ) {
		DefaultSymbolType A = DefaultSymbolType ( "A" );
		DefaultSymbolType B = DefaultSymbolType ( "B" );
		DefaultSymbolType C = DefaultSymbolType ( "C" );

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

		grammar::CFG < > grammar ( A );

		grammar.setTerminalAlphabet ( { a, b, c, d } );
		grammar.setNonterminalAlphabet ( { A, B, C } );
		grammar.setInitialSymbol ( A );

		grammar.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, B } );
		grammar.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { C, B } );
		grammar.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { c, B } );
		grammar.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a } );
		grammar.addRule ( C, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, C } );
		grammar.addRule ( C, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { B, b } );

		grammar::CFG < > res = grammar;
		grammar::parsing::CornerSubstitution::cornerSubstitution ( res, a, A );
		grammar::parsing::CornerSubstitution::cornerSubstitution ( res, a, A );

		grammar::CFG < > comp ( A );

		comp.setTerminalAlphabet ( { a, b, c, d } );
		comp.setNonterminalAlphabet ( { A, B, C } );
		comp.setInitialSymbol ( A );

		comp.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, B } );
		comp.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, C, B } );
		comp.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, b, B } );
		comp.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { c, B, b, B } );
		comp.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { c, B } );
		comp.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a } );
		comp.addRule ( C, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, C } );
		comp.addRule ( C, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { B, b } );

		INFO ( "Res: " << res << ", " << comp );
		CHECK ( res == comp );
	}
}
