#include <catch2/catch.hpp>

#include <alib/list>

#include "automaton/simplify/Trim.h"

#include "automaton/FSM/DFA.h"
#include "automaton/TA/DFTA.h"

TEST_CASE ( "Trim Automaton", "[unit][algo][automaton][simplify]" ) {
	SECTION ( "DFA" ) {
		automaton::DFA < > automaton(DefaultStateType(1));

		automaton.addState(DefaultStateType(1));
		automaton.addState(DefaultStateType(2));
		automaton.addState(DefaultStateType(3));
		automaton.addInputSymbol(DefaultSymbolType("a"));
		automaton.addInputSymbol(DefaultSymbolType("b"));

		automaton.addTransition(DefaultStateType(1), DefaultSymbolType("a"), DefaultStateType(2));
		automaton.addTransition(DefaultStateType(2), DefaultSymbolType("b"), DefaultStateType(1));
		automaton.addTransition(DefaultStateType(3), DefaultSymbolType("b"), DefaultStateType(1));

		automaton.addFinalState(DefaultStateType(1));

		automaton::DFA<> trimed = automaton::simplify::Trim::trim(automaton);

		CHECK (trimed.getStates().size() == 2);
	}


	SECTION ( "DFTA" ) {
		automaton::DFTA < > automaton;

		ext::vector<DefaultStateType> q;
		for (int i = 0; i <= 11; ++i) {
			DefaultStateType state (i);
			q.push_back(state);
			automaton.addState(state);
		}
		automaton.addFinalState(q[2]);
		automaton.addFinalState(q[11]);

		const common::ranked_symbol < > a (DefaultSymbolType("a"), 2);
		const common::ranked_symbol < > b (DefaultSymbolType("b"), 1);
		const common::ranked_symbol < > c (DefaultSymbolType("c"), 0);
		automaton.addInputSymbol(a);
		automaton.addInputSymbol(b);
		automaton.addInputSymbol(c);

		automaton.addTransition(c, {}, q[0]);
		automaton.addTransition(a, {q[0], q[0]}, q[1]);
		automaton.addTransition(b, {q[1]}, q[2]);

		//unreachable and useless
		automaton.addTransition(a, {q[3], q[4]}, q[5]);
		automaton.addTransition(b, {q[5]}, q[6]);

		//useless
		automaton.addTransition(a, {q[2], q[2]}, q[7]);
		automaton.addTransition(a, {q[7], q[7]}, q[8]);

		//unreachable
		automaton.addTransition(a, {q[9], q[9]}, q[10]);
		automaton.addTransition(a, {q[10], q[10]}, q[11]);

		automaton::DFTA<> trimed = automaton::simplify::Trim::trim(automaton);

		automaton::DFTA<> correct;
		correct.addState(q[0]);
		correct.addState(q[1]);
		correct.addState(q[2]);
		correct.addFinalState(q[2]);
		correct.addInputSymbol(a);
		correct.addInputSymbol(b);
		correct.addInputSymbol(c);
		correct.addTransition(c, {}, q[0]);
		correct.addTransition(a, {q[0], q[0]}, q[1]);
		correct.addTransition(b, {q[1]}, q[2]);

		CHECK (trimed == correct);
	}
}
