#include <iostream>
#include "EspressoLib.h"

#ifdef WIN32
#include "vld.h"
#endif //WIN32 VISUAL LEAK DETECTOR-> pre testovacie ucely

using namespace std;

char placko[] = ".i 4\n.o 3\n0000  000\n0001  001\n0010  010\n0011  011\n0100  001\n0101  010\n0110  011\n0111  100\n1000  010\n1001  011\n1010  100\n1011  101\n1100  011\n1101  100\n1110  101\n1111  110";

#define EXECUTE(plaptr, plafunct, outputstring) \
try{\
    plaptr->loadFromFile(argv[1]);\
    pla2->loadFromString(placko);\
    pla2->espresso();\
    plaptr->plafunct;\
    outstring = filename; outstring += outputstring ; outstring+= ".TST";\
    plaptr->print(outstring.c_str());\
} catch (EspressoException &e){\
    cout << "EspressoException: " << e.what() << endl;\
} catch (exception &e){\
    cout << "Another exception: " << e.what() << endl;\
} catch (...){\
    cout << "Bolo vyhodene nieco dalsie" << endl;\
}
    
#define EXECUTE2(plaptr1, plaptr2, ret, plafunct, outputstring) \
try{\
    outstring = filename; outstring += outputstring ; outstring+= ".TST";\
    fp = fopen(outstring.c_str(), "w");\
    opt.setStandardOutput(fp);\
    PLA::setOptions(opt);\
    plaptr1->loadFromFile(argv[1]);\
    pla4->loadFromString(placko);\
    plaptr2->loadFromFile(argv[2]);\
    pla4->espresso();\
    ret = plaptr1->plafunct( plaptr2 );\
    if (pla3!=NULL) {pla3->print(fp);\
    delete pla3; pla3 = NULL;}\
    fclose(fp);\
} catch (EspressoException &e){\
    cout << "EspressoException: " << e.what() << endl;\
} catch (exception &e){\
    cout << "Another exception: " << e.what() << endl;\
} catch (...){\
    cout << "Bolo vyhodene nieco dalsie" << endl;\
}
    
#define EXECUTECOPY(plaptr, plafunct, outputstring) \
try{\
    plaptr->loadFromFile(argv[1]);\
    pla3 = plaptr->copy();\
    plaptr->loadFromFile("nezmyselnysubor");\
    pla2->loadFromString(placko);\
    pla2->espresso();\
    pla3->plafunct;\
    outstring = filename; outstring += outputstring ; outstring+= ".TST";\
    pla3->print(outstring.c_str());\
} catch (EspressoException &e){\
    cout << "EspressoException: " << e.what() << endl;\
} catch (exception &e){\
    cout << "Another exception: " << e.what() << endl;\
} catch (...){\
    cout << "Bolo vyhodene nieco dalsie" << endl;\
}
    
#define EXECUTEOPTION(nastavenie, outputstring)\
try{\
    opt.nastavenie;\
    PLA::setOptions(opt);\
    pla1->loadFromFile(argv[1]);\
    pla2->loadFromString(placko);\
    pla2->espresso();\
    pla1->espresso();\
    outstring = filename; outstring += outputstring ; outstring+= ".TST";\
    pla1->print(outstring.c_str());\
    opt = opt2;\
} catch (EspressoException &e){\
    cout << "EspressoException: " << e.what() << endl;\
    opt = opt2;\
} catch (exception &e){\
    cout << "Another exception: " << e.what() << endl;\
    opt = opt2;\
} catch (...){\
    cout << "Bolo vyhodene nieco dalsie" << endl;\
    opt = opt2;\
}

#define EXECUTEOUTPUT(outputtype)\
try{\
    pla1->loadFromFile(argv[1]);\
    pla2->loadFromString(placko);\
    pla2->espresso();\
    pla1->espresso();\
    pla1->seto(outputtype);\
    outstring = filename; outstring += outputtype ; outstring+= ".TST";\
    pla1->print(outstring.c_str());\
} catch (EspressoException &e){\
    cout << "EspressoException: " << e.what() << endl;\
} catch (exception &e){\
    cout << "Another exception: " << e.what() << endl;\
} catch (...){\
    cout << "Bolo vyhodene nieco dalsie" << endl;\
}


int mainComplexFunctions(int argc, char **argv){
    
    if (argc != 2) return -1;
    
    PLA *pla1;
    PLA *pla2;
    PLA *pla3;
    
    char *pos = strstr(argv[1], ".pla");
    if (pos==NULL) return -1;
    
    EspressoOptions opt;
    EspressoOptions opt2;
    /* echo_comments, echo_unknown_commands, remove_essential, force_irredundant, unwrap_onset su TRUE3 */    
    opt.setEspressoDefaults();
    opt.setDebugOutput(NULL);
    opt.setEatOutput(NULL);
    opt.setEatdotsOutput(NULL);
    opt.setErrorOutput(NULL);
    opt.setStandardOutput(NULL);
    opt.setTraceSummaryOutput(NULL);
    
    opt2 = opt; /* opt2 je zaloha options */
    PLA::setOptions(opt);
    

    
    string filename(argv[1], (pos-argv[1])/sizeof(char) );
//    cout << "Filename bez .pla je " << filename << endl;
    string outstring;
    
    pla1 = new PLA();
    pla2 = new PLA();
    pla3 = new PLA();
    
    try{
        EXECUTE(pla1, espresso(), "")
        EXECUTE(pla1, exact(), "exact")
        EXECUTE(pla1, qm(), "qm")
        EXECUTE(pla1, so(), "so")
        EXECUTE(pla1, so(), "single_output")
        EXECUTE(pla1, so_both(), "so_both")
        EXECUTE(pla1, simplify(), "simplify")
        EXECUTE(pla1, signature(), "signature")
        EXECUTE(pla1, opo(), "opo")
        //EXECUTE(pla1, opoall(), "opoall")
        EXECUTE(pla1, pair(), "pair")
        //EXECUTE(pla1, pairall(), "pairall")
        EXECUTE(pla1, map_dcset(), "mapdc")
        EXECUTE(pla1, contain(), "contain")
        EXECUTE(pla1, d1merge(), "d1merge")
        EXECUTE(pla1, d1merge_in(), "d1merge_in")
        EXECUTE(pla1, disjoint(), "disjoint")
        EXECUTE(pla1, minterms(), "minterms")
        EXECUTE(pla1, primes(), "primes")
        EXECUTE(pla1, separate(), "separate")
        EXECUTE(pla1, essen(), "essen")
        EXECUTE(pla1, expand(), "expand")
        EXECUTE(pla1, gasp(), "gasp")
        EXECUTE(pla1, irred(), "irred")
        EXECUTE(pla1, make_sparse(), "make_sparse")
        EXECUTE(pla1, reduce(), "reduce")
        EXECUTE(pla1, super_gasp(), "super_gasp")
        EXECUTE(pla1, lexsort(), "lexsort")
        EXECUTE(pla1, test(), "test")
    
        /* echo */
        pla1->loadFromFile(argv[1]);
        pla2->loadFromString(placko);
        pla2->espresso();
        outstring = filename; outstring += "echo.TST";
        pla1->print(outstring.c_str());
    
        /* stats */
        pla1->loadFromFile(argv[1]);
        pla2->loadFromString(placko);
        pla2->espresso();
        outstring = filename; outstring += "stats.TST";
        pla1->printStats(outstring.c_str());
    
        /* check */
        pla1->loadFromFile(argv[1]);
        pla2->loadFromString(placko);
        pla2->espresso();
        outstring = filename; outstring += "check.TST";
        pla1->check(outstring.c_str());
    
        /* equiv */ 
        pla1->loadFromFile(argv[1]);
        pla2->loadFromString(placko);
        pla2->espresso();
        outstring = filename; outstring += "equiv.TST";
        pla1->printEquiv(outstring.c_str());
    
        /* map */
        pla1->loadFromFile(argv[1]);
        pla2->loadFromString(placko);
        pla2->espresso();
        outstring = filename; outstring += "map.TST";
        pla1->map(outstring.c_str());
    
        /* taut */
        pla1->loadFromFile(argv[1]);
        pla2->loadFromString(placko);
        pla2->espresso();
        outstring = filename; outstring += "taut.TST";
        pla1->printTaut(outstring.c_str());
        
        /* ESPRESSO SO ZMENENYMI NASTAVENIAMI */
        EXECUTEOPTION(setEat(false), "eat")
        EXECUTEOPTION(setEatdots(false), "eatdots")
        EXECUTEOPTION(setFast(true), "fast")
        EXECUTEOPTION(setKiss(true), "kiss")
        EXECUTEOPTION(setNess(false), "ness")
        EXECUTEOPTION(setNirr(false), "nirr")
        EXECUTEOPTION(setNunwrap(false), "nunwrap")
        EXECUTEOPTION(setOnset(true), "onset")
        
        /* -epos sa testuje tak, ze najskor load, potom pos() a potom espresso() */
    	PLA::setOptions(opt); /* nezabudnut na epilog */
        pla1->loadFromFile(argv[1]);
        pla2->loadFromString(placko);
        pla1->pos();
        pla2->espresso();
        pla1->espresso();
        outstring = filename; outstring += "pos.TST";
        pla1->print(outstring.c_str());
    	opt = opt2; /* nezabudnut na prolog */
        /* koniec -epos */
      
        EXECUTEOPTION(setRandom(true), "random")
        EXECUTEOPTION(setStrong(true), "strong")
        opt = opt2;
        PLA::setOptions(opt);
        
        EXECUTEOUTPUT("f");
        EXECUTEOUTPUT("d");
        EXECUTEOUTPUT("r");
        EXECUTEOUTPUT("fd");
        EXECUTEOUTPUT("fr");
        EXECUTEOUTPUT("dr");
        EXECUTEOUTPUT("fdr");
        EXECUTEOUTPUT("fc");
        EXECUTEOUTPUT("rc");
        EXECUTEOUTPUT("dc");
        EXECUTEOUTPUT("fdc");
        EXECUTEOUTPUT("frc");
        EXECUTEOUTPUT("drc");
        EXECUTEOUTPUT("fdrc");
        EXECUTEOUTPUT("pleasure");
        EXECUTEOUTPUT("eqn");
        EXECUTEOUTPUT("eqntott");
        EXECUTEOUTPUT("kiss");
        EXECUTEOUTPUT("cons");
        EXECUTEOUTPUT("scons");
    } catch (EspressoException &e){
        cout << "EspressoException: " << e.what() << endl;
    } catch (exception &e){
        cout << "Another exception: " << e.what() << endl;
    } catch (...){
        cout << "Bolo vyhodene nieco dalsie" << endl;
    }

    delete pla1;
    delete pla2;
    delete pla3;
    
    return 0;
}

int mainTwoPLAs(int argc, char **argv){
    if (argc != 3) return -1;
    
    PLA *pla1;
    PLA *pla2;
    PLA *pla3;
    PLA *pla4;
    FILE* fp;
    
    char *pos = strstr(argv[1], ".pla");
    if (pos==NULL) return -1;
    
    EspressoOptions opt;
    /* echo_comments, echo_unknown_commands, remove_essential, force_irredundant, unwrap_onset su TRUE3 */    
    opt.setEspressoDefaults();
    opt.setDebugOutput(NULL);
    opt.setEatOutput(NULL);
    opt.setEatdotsOutput(NULL);
    opt.setErrorOutput(NULL);
    opt.setStandardOutput(NULL);
    opt.setTraceSummaryOutput(NULL);
    PLA::setOptions(opt);
    
    string filename(argv[1], (pos-sizeof(char)-argv[1])/sizeof(char) ); /* posunutie o jeden char, lebo sa predpoklada, ze posledny znak je cislo */
//    cout << "Filename bez .pla je " << filename << endl;
    string outstring;
    
    pla1 = new PLA();
    pla2 = new PLA();
    pla3 = NULL;
    pla4 = new PLA();
    
    int navrat;
    
    try {
        EXECUTE2(pla1, pla2, navrat, verify, "verify");
        EXECUTE2(pla1, pla2, navrat, PLAverify, "PLAverify");
        EXECUTE2(pla1, pla2, pla3, dsharp, "dsharp");
        EXECUTE2(pla1, pla2, pla3, sharp, "sharp");
        EXECUTE2(pla1, pla2, pla3, _union_, "union");
        EXECUTE2(pla1, pla2, pla3, _xor_, "xor");
        EXECUTE2(pla1, pla2, pla3, intersect, "intersect");
    } catch (EspressoException &e){
        cout << "EspressoException: " << e.what() << endl;
    } catch (exception &e){
        cout << "Another exception: " << e.what() << endl;
    } catch (...){
        cout << "Bolo vyhodene nieco dalsie" << endl;
    }
    
    delete pla1;
    delete pla2;
    //delete pla3; - mazane makrom
    delete pla4;
    
    return 0;
}


/* spusti vsetky nastavenia na 1 PLA -> testuje stabilitu programu */
int allOnOne(int argc, char **argv){
    
    if (argc < 2) return -1;
        
    PLA *pla1 = new PLA();
    PLA pla2;
    PLA *tmpPLA;
    EspressoOptions opt;
    opt.setEspressoDefaults();
    PLA::setDefaultOptions();
    
    pla1->loadFromString(placko);
    tmpPLA = new PLA();
    
    try {
    	PLA *tmpPLA2 = pla1->copy();
    	tmpPLA2->espresso();
        pla1->espresso();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
    
        
        pla1->exact();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
            
        pla1->qm();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
    
        pla1->so();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
    
        pla1->so_both();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
    
        pla1->simplify();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
    
        pla1->signature();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
    
        pla1->opo();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
    
        pla1->opoall();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
        
        pla1->pair();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
        
        pla1->pairall();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
        
        pla1->essen();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
        
        pla1->expand();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
        
        pla1->gasp();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
        
        pla1->super_gasp();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
    
        pla1->irred();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
    
        pla1->make_sparse();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
    
        pla1->reduce();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
    
        pla1->test();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
    
        pla1->printEquiv(stdout);
        tmpPLA->loadFromFile(argv[1]);
    
        pla1->map(stdout);
        tmpPLA->loadFromFile(argv[1]);
    
        pla1->map_dcset();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
    
        pla1->contain();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
    
        pla1->d1merge();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
    
        pla1->d1merge_in();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
    
        pla1->disjoint();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
    
        pla1->minterms();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
    
        pla1->primes();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
    
        pla1->separate();
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
    
        pla1->printStats(stdout);
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
    
        pla1->check(stdout);
        tmpPLA->loadFromFile(argv[1]);
        pla1->print(stdout);
    } catch (EspressoException &e){
        cout << "EspressoException: " << e.what() << endl;
    } catch (exception &e){
        cout << "Another exception: " << e.what() << endl;
    } catch (...){
        cout << "Bolo vyhodene nieco dalsie" << endl;
    }

	delete pla1;
	delete tmpPLA;
	
	return 0;	
}

/* mainComplexFunctions ocakava 1 argument = nazov suboru s PLA, pre ktory vykona vsetky mozne operacie, nastavenia a vystupy */
/* pre zlozite funkcie odporucam zakomentovat operacie pairall a opoall; opoall pre ilb.pla by na mojom pocitaci trvalo 1,7*10^26 rokov... */
/* mainTwoPLAs ocakava 2 argumenty = 2 PLA, pre ktore sa vykona vsetkych 7 operacii pre 2 PLA */
int main(int argc, char **argv){

    //allOnOne(argc, argv);
    //mainComplexFunctions(argc, argv);
    mainTwoPLAs(argc, argv);

    /* dokaz, ze pri pouziti ".phase" a nevhodej postupnosti volani funkcii dojde k zmene pokryti, ktore uz nie su ekvivalentne */
//    PLA::setDefaultOptions();
//	PLA *pla1 = new PLA();
//	PLA *pla2 = new PLA();
//	bool ret;
	
//	    /* map */
//	try{
//        pla1->loadFromFile("o64.pla");
//        pla2->loadFromString(placko);
//        pla2->espresso();
//        pla1->minterms();
//    } catch (EspressoException &e){
//        cout << "EspressoException: " << e.what() << endl;
//    } catch (exception &e){
//        cout << "Another exception: " << e.what() << endl;
//    } catch (...){
//        cout << "Bolo vyhodene nieco dalsie" << endl;
//    }
//
//	/* echo */
//    pla1->loadFromFile("phasepair.pla");
//    pla1->seto("fdr");
//    pla1->print("phasepairprint.TST"); /* dojde k dopocitaniu OFF-setu a "hacku" pre ".phase" (set_phase), ktory zmeni velkost setov... */
//    pla1->pos();
//    pla1->print("phasepairprintpos.TST");
//
//
//    pla1->loadFromFile("phasepair.pla");
//    pla1->seto("f");
//    pla1->pos();
//    pla1->print("phasepairpos.TST");
//
//    pla1->loadFromFile("phasepairprintpos.TST");
//    pla2->loadFromFile("phasepairpos.TST");
//    ret = pla1->verify(pla2);
//    cout << "ret = " << ret << endl;

    /* koniec dokazu => vymena rozsirenych setov a povodnych setov nie je ekvivalentna operacia! */


	/*PLA::setDefaultOptions();
	PLA *pla1 = new PLA();
	pla1->loadFromFile("amd.pla");
	pla1->pos();
	pla1->espresso();
	pla1->print("amdpos.TST");
	pla1->print(stdout);
	delete pla1;*/

    /*EspressoOptions opt;
    opt.setEspressoDefaults();
    opt.setTraceSummaryOutput(stdout);
    opt.setTrace(true);*/

	/*PLA::setDefaultOptions();
	PLA *pla1 = new PLA();
	pla1->loadFromFile("io.pla");
	pla1->opo();
//	pla1->seto("fdr");
	pla1->print(stdout);
	pla1->print("ioopo.TST");
	delete pla1;*/


//	PLA *pla3 = NULL;
//	PLA *pla4 = new PLA();
//	FILE* fp;
//	
//	string outstring;
//	char *pos = strstr(argv[1], ".pla");
//    if (pos==NULL) return -1;
//    
//    EspressoOptions opt;
//    /* echo_comments, echo_unknown_commands, remove_essential, force_irredundant, unwrap_onset su TRUE3 */    
//    opt.setEspressoDefaults();
//    opt.setDebugOutput(NULL);
//    opt.setEatOutput(NULL);
//    opt.setEatdotsOutput(NULL);
//    opt.setErrorOutput(NULL);
//    opt.setStandardOutput(NULL);
//    opt.setTraceSummaryOutput(NULL);
//    PLA::setOptions(opt);
//    
//    string filename(argv[1], (pos-sizeof(char)-argv[1])/sizeof(char) ); /* posunutie o jeden char, lebo sa predpoklada, ze posledny znak je cislo */
//	
//	EXECUTE2(pla1, pla2, pla3, dsharp, "dsharp")

    system("PAUSE");
    return 0;
}
