#include <catch2/catch.hpp>

#include <alib/list>

#include "automaton/transform/Compaction.h"
#include <automaton/FSM/DFA.h>
#include <automaton/FSM/CompactDFA.h>

TEST_CASE ( "Automata Compaction", "[unit][algo][automaton][transform]" ) {
	SECTION ( "DFA" ) {
		{
			DefaultStateType q0 = DefaultStateType("0");
			DefaultStateType q1 = DefaultStateType("1");
			DefaultStateType q2 = DefaultStateType("2");
			DefaultStateType q3 = DefaultStateType("3");
			DefaultStateType q4 = DefaultStateType("4");
			DefaultStateType q5 = DefaultStateType("5");
			DefaultSymbolType a(DefaultSymbolType('a')), b(DefaultSymbolType('b'));

			automaton::DFA<> m1(q0);
			automaton::CompactDFA < > m2(q0);

			m1.setInputAlphabet({a, b});
			m1.setStates({q0, q1, q2, q3, q4, q5});
			m1.addTransition(q0, a, q1);
			m1.addTransition(q1, b, q2);
			m1.addTransition(q2, a, q3);
			m1.addTransition(q3, b, q4);
			m1.addTransition(q4, a, q5);
			m1.addFinalState(q5);

			m2.setInputAlphabet({a, b});
			m2.setStates({q0, q5});
			m2.addTransition(q0, ext::vector <DefaultSymbolType > {a, b, a, b, a}, q5);
			m2.addFinalState(q5);

			automaton::CompactDFA < > c1 = automaton::transform::Compaction::convert(m1);

			CHECK (c1 == m2);
		}{
			DefaultStateType q0 = DefaultStateType("0");
			DefaultStateType q1 = DefaultStateType("1");
			DefaultStateType q2 = DefaultStateType("2");
			DefaultStateType q3 = DefaultStateType("3");
			DefaultStateType q4 = DefaultStateType("4");
			DefaultStateType q5 = DefaultStateType("5");
			DefaultSymbolType a(DefaultSymbolType('a')), b(DefaultSymbolType('b'));

			automaton::DFA<> m1(q0);
			automaton::CompactDFA < > m2(q0);

			m1.setInputAlphabet({a, b});
			m1.setStates({q0, q1, q2, q3, q4, q5});
			m1.addTransition(q0, a, q1);
			m1.addTransition(q1, b, q2);
			m1.addTransition(q2, a, q3);
			m1.addTransition(q3, b, q4);
			m1.addTransition(q4, a, q5);
			m1.addTransition(q5, a, q1);
			m1.addFinalState(q5);

			m2.setInputAlphabet({a, b});
			m2.setStates({q0, q5});
			m2.addTransition(q0, ext::vector < DefaultSymbolType > { a, b, a, b, a }, q5);
			m2.addTransition(q5, ext::vector < DefaultSymbolType > { a, b, a, b, a }, q5);
			m2.addFinalState(q5);

			automaton::CompactDFA < > c1 = automaton::transform::Compaction::convert(m1);

			CHECK (c1 == m2);

		}
		{
			DefaultStateType q0 = DefaultStateType("0");
			DefaultStateType q1 = DefaultStateType("1");
			DefaultStateType q2 = DefaultStateType("2");
			DefaultStateType q3 = DefaultStateType("3");
			DefaultStateType q4 = DefaultStateType("4");
			DefaultStateType q5 = DefaultStateType("5");
			DefaultStateType q6 = DefaultStateType("6");
			DefaultSymbolType a(DefaultSymbolType('a')), b(DefaultSymbolType('b'));

			automaton::DFA<> m1(q0);
			automaton::CompactDFA < > m2(q0);

			m1.setInputAlphabet({a, b});
			m1.setStates({q0, q1, q2, q3, q4, q5, q6});
			m1.addTransition(q0, a, q1);
			m1.addTransition(q1, a, q2);
			m1.addTransition(q2, a, q3);
			m1.addTransition(q3, a, q4);
			m1.addTransition(q4, a, q2);
			m1.addTransition(q1, b, q5);
			m1.addTransition(q5, b, q6);
			m1.addTransition(q6, a, q0);
			m1.addFinalState(q3);
			m1.addFinalState(q5);

			m2.setInputAlphabet({a, b});
			m2.setStates({q0, q1, q3, q5});
			m2.addTransition(q0, ext::vector < DefaultSymbolType > {a}, q1);
			m2.addTransition(q1, ext::vector < DefaultSymbolType > {a, a}, q3);
			m2.addTransition(q3, ext::vector < DefaultSymbolType > {a, a, a}, q3);
			m2.addTransition(q1, ext::vector < DefaultSymbolType > {b}, q5);
			m2.addTransition(q5, ext::vector < DefaultSymbolType > {b, a, a}, q1);
			m2.addFinalState(q3);
			m2.addFinalState(q5);

			automaton::CompactDFA < > c1 = automaton::transform::Compaction::convert(m1);

			CHECK (c1 == m2);
		}
	}
}
