#include <catch2/catch.hpp>

#include <alib/list>

#include "automaton/simplify/Minimize.h"
#include <factory/XmlDataFactory.hpp>

TEST_CASE ( "Minimization", "[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.addFinalState(DefaultStateType(3));

		automaton::DFA<> minimized = automaton::simplify::Minimize::minimize(automaton);

		CHECK(minimized.getStates().size() == 3);

	}

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

		ext::vector<DefaultStateType> q;

		for (int i = 0; i <= 10; ++i) {
			DefaultStateType state (i);
			q.push_back(state);
			automaton.addState(state);
		}

		automaton.addFinalState(q[9]);
		automaton.addFinalState(q[10]);

		const common::ranked_symbol < > a (DefaultSymbolType("a"), 3);
		const common::ranked_symbol < > b (DefaultSymbolType("b"), 2);
		const common::ranked_symbol < > c (DefaultSymbolType("c"), 1);
		const common::ranked_symbol < > d (DefaultSymbolType("d"), 0);
		const common::ranked_symbol < > e (DefaultSymbolType("e"), 0);
		const common::ranked_symbol < > f (DefaultSymbolType("f"), 0);
		const common::ranked_symbol < > g (DefaultSymbolType("g"), 0);

		automaton.addInputSymbol(a);
		automaton.addInputSymbol(b);
		automaton.addInputSymbol(c);
		automaton.addInputSymbol(d);
		automaton.addInputSymbol(e);
		automaton.addInputSymbol(f);
		automaton.addInputSymbol(g);

		automaton.addTransition(d, {}, q[0]);
		automaton.addTransition(e, {}, q[1]);

		for (int i = 0; i <= 1; ++i) {
			for (int j = 0; j <= 1; ++j) {
				automaton.addTransition(b, {q[i], q[j]}, q[2]);
			}
		}

		automaton.addTransition(b, {q[2], q[2]}, q[6]);
		automaton.addTransition(f, {}, q[3]);
		for (int i = 3; i < 5; ++i) {
			automaton.addTransition(c, {q[i]}, q[i+1]);
		}

		automaton.addTransition(c, {q[5]}, q[7]);
		automaton.addTransition(g, {}, q[8]);

		for (int i = 6; i <= 8; ++i) {
			for (int j = 6; j <= 8; ++j) {
				for (int k = 6; k <= 8; ++k) {
					automaton.addTransition(a, {q[i], q[j], q[k]}, q[9]);
				}
			}
		}

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

		automaton::DFTA < > minimal;
		minimal.addState(q[0]);
		minimal.addState(q[2]);
		minimal.addState(q[3]);
		minimal.addState(q[4]);
		minimal.addState(q[5]);
		minimal.addState(q[6]);
		minimal.addState(q[9]);
		minimal.addState(q[10]);
		minimal.setFinalStates(automaton.getFinalStates());
		minimal.setInputAlphabet(automaton.getInputAlphabet());
		minimal.addTransition(d, {}, q[0]);
		minimal.addTransition(e, {}, q[0]);
		minimal.addTransition(b, {q[0], q[0]}, q[2]);
		minimal.addTransition(b, {q[2], q[2]}, q[6]);
		minimal.addTransition(f, {}, q[3]);

		for (int i = 3; i < 5; ++i) {
			minimal.addTransition(c, {q[i]}, q[i+1]);
		}

		minimal.addTransition(c, {q[5]}, q[6]);
		minimal.addTransition(g, {}, q[6]);
		minimal.addTransition(a, {q[6], q[6], q[6]}, q[9]);
		minimal.addTransition(a, {q[9], q[9], q[9]}, q[10]);

		automaton::DFTA<> minimized = automaton::simplify::Minimize::minimize(automaton);
		CHECK(minimized == minimal);

		/*	automaton::DFTA < > automaton;

			const DefaultStateType q0 ("0");
			const DefaultStateType q1 ("1");
			const DefaultStateType q00 ("00");
			const DefaultStateType q11 ("11");
			automaton.addState(q0);
			automaton.addState(q1);
			automaton.addState(q00);
			automaton.addState(q11);

			const common::ranked_symbol < > st ("t", 0);
			const common::ranked_symbol < > sf ("f", 0);
			const common::ranked_symbol < > snot ("not", 1);
			const common::ranked_symbol < > sor ("or", 2);
			automaton.addInputSymbol(st);
			automaton.addInputSymbol(sf);
			automaton.addInputSymbol(snot);
			automaton.addInputSymbol(sor);

			automaton.addTransition(st, {}, q1);
			automaton.addTransition(sf, {}, q0);

			automaton.addTransition(snot, {q0}, q1);
			automaton.addTransition(snot, {q1}, q0);
			automaton.addTransition(snot, {q00}, q1);
			automaton.addTransition(snot, {q11}, q0);

			automaton.addTransition(sor, {q0, q0}, q00);
			automaton.addTransition(sor, {q0, q00}, q00);
			automaton.addTransition(sor, {q00, q0}, q00);
			automaton.addTransition(sor, {q00, q00}, q00);
			automaton.addTransition(sor, {q0, q1}, q1);
			automaton.addTransition(sor, {q0, q11}, q1);
			automaton.addTransition(sor, {q00, q1}, q1);
			automaton.addTransition(sor, {q00, q11}, q1);
			automaton.addTransition(sor, {q1, q0}, q1);
			automaton.addTransition(sor, {q1, q00}, q1);
			automaton.addTransition(sor, {q11, q0}, q1);
			automaton.addTransition(sor, {q11, q00}, q1);
			automaton.addTransition(sor, {q1, q1}, q11);
			automaton.addTransition(sor, {q1, q11}, q11);
			automaton.addTransition(sor, {q11, q1}, q11);
			automaton.addTransition(sor, {q11, q11}, q11);

			automaton.addFinalState(q1);
			automaton.addFinalState(q11);

			automaton::DFTA<> minimized = automaton::simplify::Minimize::minimize(automaton);
			std::cout << minimized << std::endl;
			CPPUNIT_ASSERT(minimized.getStates().size() == 2);
			CPPUNIT_ASSERT(minimized.getFinalStates().size() == 1);
			CPPUNIT_ASSERT(minimized.getTransitions().size() == 8);
			CPPUNIT_ASSERT(minimized.getInputAlphabet().size() == 4);
			*/
	}
}
