#include "playTest.h"

#include <iostream>
#include <cstdlib>
#include <ctime>

#include "determinize/nfa/NFADeterminizer.h"
#include "minimize/dfa/MinimizeDFA.h"

#include "generator/RandomAutomatonFactory.h"
#include "normalize/dfa/NormalizeDFA.h"
#include "trim/automaton/TrimFSM.h"
#include "epsilon/epsilonNfa/EpsilonNFAEpsilonRemover.h"
#include "minimize/dfa/MinimizeDFA.h"

//#include "conversions/fa2re/StateElimination.h"
//#include "conversions/fa2re/BrzozowskiAlgebraic.h"
//#include "conversions/re2fa/Glushkov.h"
//#include "conversions/re2fa/Thompson.h"
//#include "conversions/re2fa/Brzozowski.h"
#include "conversions/fa2rg/fa2lrg/FAtoLRGConverter.h"
#include "conversions/fa2rg/fa2rrg/FAtoRRGConverter.h"
#include "conversions/rg2fa/lrg2fa/LRGtoFAConverter.h"
#include "conversions/rg2fa/rrg2fa/RRGtoFAConverter.h"
//#include "conversions/rg2re/rrg2re/RRGAlgebraic.h"
//#include "conversions/rg2re/lrg2re/LRGAlgebraic.h"
//#include "conversions/re2rg/re2rrg/GlushkovRRG.h"
//#include "conversions/re2rg/re2rrg/BrzozowskiDerivationRRG.h"
#include "conversions/rg2rg/lrg2rrg/LeftToRightRegularGrammar.h"
#include "conversions/rg2rg/rrg2lrg/RightToLeftRegularGrammar.h"


#define CPPUNIT_IMPLY(x, y) CPPUNIT_ASSERT(!(x) || (y))

#define TEST_ITERATIONS 100
#define TEST_AUTOMATON_STATES_MAX 18
#define TEST_AUTOMATON_DENSITY_MAX 2.5
#define TEST_AUTOMATON_ALPHABET_MAX 4

CPPUNIT_TEST_SUITE_REGISTRATION( playTest );

void playTest::setUp()
{
    srand(time(NULL));
}

void playTest::tearDown(){}

automaton::NFA playTest::randomNFA(void) const
{
    return generator::RandomAutomatonFactory::generateNFA(
            rand() % TEST_AUTOMATON_STATES_MAX + 1,
            rand() % TEST_AUTOMATON_ALPHABET_MAX + 1,
            static_cast<double> (rand()) / (static_cast<double> (RAND_MAX/TEST_AUTOMATON_DENSITY_MAX))
            );
}

automaton::DFA playTest::mDFA(const automaton::NFA& automaton) const
{
    automaton::NFA nfa = epsilon::EpsilonNFAEpsilonRemover::remove(automaton);
    nfa = trim::TrimFSM::trim(nfa);
    automaton::DFA dfa = determinize::NFADeterminizer::determinize(nfa);
    dfa = trim::TrimFSM::trim(dfa);
    dfa = minimize::MinimizeDFA::minimize(dfa);
    dfa = normalize::NormalizeDFA::normalize(dfa);
    return dfa;
}

/**
 * Test case 1:
 *  - covers: FA -> LRG, FA -> RRG, RRG <-> LRG, RRG -> FA, LRG -> FA
 *  a. FA -> RRG -> LRG -> FA
 *  b. FA -> LRG -> RRG -> FA
 */
void playTest::testPlay1()
{
    for(int i = 0; i < TEST_ITERATIONS; i++)
        this->case1a();
    for(int i = 0; i < TEST_ITERATIONS; i++)
        this->case1b();
}

void playTest::case1a(void) const
{
    fa2rg::FAtoRRGConverter fa2rrg;
    rg2rg::RightToLeftRegularGrammar rrg2lrg;
    rg2fa::LRGtoFAConverter lrg2fa;

    automaton::NFA a1 = this->randomNFA();
    automaton::NFA a2 = lrg2fa.convert(rrg2lrg.convert(fa2rrg.convert(a1)));

    CPPUNIT_ASSERT(this->mDFA(a1) == this->mDFA(a2));
}

void playTest::case1b(void) const
{
    fa2rg::FAtoLRGConverter fa2lrg;
    rg2rg::LeftToRightRegularGrammar lrg2rrg;
    rg2fa::RRGtoFAConverter rrg2fa;

    automaton::NFA a1 = this->randomNFA();
    automaton::NFA a2 = rrg2fa.convert(lrg2rrg.convert(fa2lrg.convert(a1)));

    CPPUNIT_ASSERT(this->mDFA(a1) == this->mDFA(a2));
}