#include <catch2/catch.hpp>

#include <automaton/FSM/DFA.h>

#include "automaton/properties/DistinguishableStates.h"

TEST_CASE ( "Distringuishable States Test", "[unit][algo][automaton]" ) {
	SECTION ( "Test" ) {
		{
			DefaultStateType qA ( "qA" );
			DefaultStateType qB ( "qB" );
			DefaultStateType qC ( "qC" );
			DefaultStateType qD ( "qD" );
			DefaultStateType qE ( "qE" );
			DefaultStateType qF ( "qF" );
			DefaultStateType qG ( "qG" );
			DefaultStateType qH ( "qH" );

			automaton::DFA < > automaton ( qA );
			automaton.setStates ( { qA, qB, qC, qD, qE, qF, qG, qH } );
			automaton.setFinalStates ( { qC } );
			automaton.addInputSymbols ( { DefaultSymbolType ( 0 ), DefaultSymbolType ( 1 ) } );

			automaton.addTransition(qA, DefaultSymbolType ( 0 ), qB);
			automaton.addTransition(qA, DefaultSymbolType ( 1 ), qF);
			automaton.addTransition(qB, DefaultSymbolType ( 0 ), qG);
			automaton.addTransition(qB, DefaultSymbolType ( 1 ), qC);
			automaton.addTransition(qC, DefaultSymbolType ( 0 ), qA);
			automaton.addTransition(qC, DefaultSymbolType ( 1 ), qC);
			automaton.addTransition(qD, DefaultSymbolType ( 0 ), qC);
			automaton.addTransition(qD, DefaultSymbolType ( 1 ), qG);
			automaton.addTransition(qE, DefaultSymbolType ( 0 ), qH);
			automaton.addTransition(qE, DefaultSymbolType ( 1 ), qF);
			automaton.addTransition(qF, DefaultSymbolType ( 0 ), qC);
			automaton.addTransition(qF, DefaultSymbolType ( 1 ), qG);
			automaton.addTransition(qG, DefaultSymbolType ( 0 ), qG);
			automaton.addTransition(qG, DefaultSymbolType ( 1 ), qE);
			automaton.addTransition(qH, DefaultSymbolType ( 0 ), qG);
			automaton.addTransition(qH, DefaultSymbolType ( 1 ), qC);

			ext::set < ext::pair < DefaultStateType, DefaultStateType > > exp;
			const ext::set < ext::pair < DefaultStateType, DefaultStateType > > res = automaton::properties::DistinguishableStates::distinguishable ( automaton );
			const ext::set < ext::pair < DefaultStateType, DefaultStateType > > exp_ ( {
					ext::make_pair ( qA, qB ), ext::make_pair ( qA, qC ), ext::make_pair ( qA, qD ), ext::make_pair ( qA, qF ), ext::make_pair ( qA, qG ), ext::make_pair ( qA, qH ),
					ext::make_pair ( qB, qC ), ext::make_pair ( qB, qD ), ext::make_pair ( qB, qE ), ext::make_pair ( qB, qF ), ext::make_pair ( qB, qG ),
					ext::make_pair ( qC, qD ), ext::make_pair ( qC, qE ), ext::make_pair ( qC, qF ), ext::make_pair ( qC, qG ), ext::make_pair ( qC, qH ),
					ext::make_pair ( qD, qE ), ext::make_pair ( qD, qG ), ext::make_pair ( qD, qH ),
					ext::make_pair ( qE, qF ), ext::make_pair ( qE, qG ), ext::make_pair ( qE, qH ),
					ext::make_pair ( qF, qG ), ext::make_pair ( qF, qH ),
					ext::make_pair ( qG, qH ),
					} );

			for ( const auto & x : exp_ ) {
				exp.insert ( ext::make_pair ( x.first, x.second ) );
				exp.insert ( ext::make_pair ( x.second, x.first ) );
			}

			CHECK ( res == exp );
		}
		{
			DefaultStateType qA ( "qA" );
			DefaultStateType qB ( "qB" );
			DefaultStateType qC ( "qC" );
			DefaultStateType qD ( "qD" );
			DefaultStateType qE ( "qE" );
			DefaultStateType qF ( "qF" );

			automaton::DFA < > automaton ( qA );
			automaton.setStates ( { qA, qB, qC, qD, qE, qF } );
			automaton.setFinalStates ( { qC, qF } );
			automaton.addInputSymbols ( { DefaultSymbolType ( 0 ), DefaultSymbolType ( 1 ) } );

			automaton.addTransition(qA, DefaultSymbolType ( 0 ), qB);
			automaton.addTransition(qA, DefaultSymbolType ( 1 ), qD);
			automaton.addTransition(qB, DefaultSymbolType ( 0 ), qC);
			automaton.addTransition(qD, DefaultSymbolType ( 0 ), qE);
			automaton.addTransition(qE, DefaultSymbolType ( 0 ), qF);

			ext::set < ext::pair < DefaultStateType, DefaultStateType > > exp;
			const ext::set < ext::pair < DefaultStateType, DefaultStateType > > res = automaton::properties::DistinguishableStates::distinguishable ( automaton );
			const ext::set < ext::pair < DefaultStateType, DefaultStateType > > exp_ ( {
					ext::make_pair ( qA, qB ), ext::make_pair ( qA, qC ), ext::make_pair ( qA, qD ), ext::make_pair ( qA, qE ), ext::make_pair ( qA, qF ),
					ext::make_pair ( qB, qC ), ext::make_pair ( qB, qD ), ext::make_pair ( qB, qF ),
					ext::make_pair ( qC, qD ), ext::make_pair ( qC, qE ),
					ext::make_pair ( qD, qE ), ext::make_pair ( qD, qF ),
					ext::make_pair ( qE, qF )
					} );

			for ( const auto & x : exp_ ) {
				exp.insert ( ext::make_pair ( x.first, x.second ) );
				exp.insert ( ext::make_pair ( x.second, x.first ) );
			}

			CHECK ( res == exp );
		}
	}
}
