#ifndef ESPRESSO_LIBH_321655746546465631564321
#define ESPRESSO_LIBH_321655746546465631564321

#include <windows.h>
#include <iostream>
#include <vector>
#include <string>
#include "EspressoStructs.h" //obsahuje struktury Espressa, ktore su obsiahnute tymito triedami */

/*  To use this exported function of dll, include this header
 *  in your project.
 */

#ifdef BUILD_DLL
    #define DLL_EXPORT __declspec(dllexport)
#else
    #define DLL_EXPORT __declspec(dllimport)
#endif

/* forward deklaracia */
class DLL_EXPORT EspressoOptions;


class DLL_EXPORT EspressoException : public std::exception{
public:
    virtual const char *what() const throw();
};

class DLL_EXPORT UninitializedPLAException : public EspressoException{
public:
    virtual const char *what() const throw();
};

class DLL_EXPORT EspressoOperationException : public EspressoException{
};


/* struktura obsahujuca cenu setu */
/* pocet cubes, pocet primary_cubes, inputs, outputs, multiple-valued variables, total variables? */
struct DLL_EXPORT SetCost{
    int cubes; /* number of cubes in the cover */
    int primes; /* number of prime cubes */
    int in; /* transistor count, binary-valued variables */
    int out; /* transistor count, output part */
    int mv; /* transistor count, multiple-valued vars */
    int total; /* total number of transistors */
};

/* struktura, ktoru vrati funkcia stats (prepinac -Dstats) */
struct DLL_EXPORT PLAStats{
    std::string source; /* zdroj z ktoreho PLA pochadza - (stream) alebo nazov_suboru */
    int variables; /* pocet premennych */
    int binary_variables; /* pocet binárnych premenných. Ak je počet rovný (variables-1), tak sa tam nenachádzajú mv premenné */
    int multiplevalued_variables;
    std::vector<int> mv_sizes; /* vektor o velkosti "multiplevalued_variables" obsahujuci velkosti viachodnotovych premennych */
    int outputs; /* velkost/pocet vystupov */
    SetCost ON_set; /* cena ON-setu */
    SetCost OFF_set; /* cena OFF-setu */
    SetCost DC_set; /* cena DC-setu */
    std::string phase; /* faza */
    std::vector< std::pair<int, int> > two_bit_decoders; /* vektor dvojic dvojbitoveho dekodera */
    std::vector< std::vector<int> > symbolic; /* symbolicke premenne? */
    std::vector< std::vector<int> > symbolic_output; /* symbolicke premenne? */
};

class DLL_EXPORT PLA{
private:
    pPLA pla; /* musim zaistit, ze v okamziku spracovania bude nacitany -> bool loaded */
    cube_struct mycube;
    cdata_struct mycdata;

    int output_type; /* dafaultny output_type je F_type */
    bool loaded; /* informuje, ci bola funkcia uz nacitana = jeden z vnutornych stavov*/
    bool paired, symboliced, symbolic_outputed, phased; /* indikuju, ci uz bolo vykonane parovanie,... ktore sa mozu maximalne raz v computeSets */
    static int numInstances; /* staticka premenna na zistenie poctu vytvorenych instancii a inicializaciu randomu */
    static bool optionsSet; /* ak nie su nastavene Options, tak prva instancia ich nastavi na reset() */

    PLA(const PLA&); /* zatial zakazany kopirujuci konstruktor; treba pouzit metodu copy() */
    void intro(); /* inicializuje ukazatele na globalne struktury */
    void outro(); /* ukazatele na globalne struktury nastavi na NULL */
    void invalidatePLA(); /* pomocna metoda na zmazanie vsetkych dat, ak doslo k chybe pocas Espresso funkcie */
    void invalidatePLA(PLA *second); /* ak chyba vypoctov pokryti v druhom PLA; pre metody pracujuce s dvomi PLA */
    int copyPLA(pPLA pla, bool copyCovers = true); /* pomocne kopirovacie metody pre copy() */
    int copyCube(cube_struct *cub, cdata_struct *cdta); /* pomocne kopirovacie metody pre copy() */
    int computeSets(bool3 needs_offset, bool3 needs_dcset); /* pre danu funkciu dopocita potrebne pokrytia */
    int computeSets(bool3 needs_offset, bool3 needs_dcset, PLA* second); /* dopocita sety pre dane PLA aj pre second */
    int loadForeword(); /* spolocny zaklad vsetkych nacitacich funkcii */
    int twoPLAsForeword(PLA* second); /* pomocna metoda, ktora urci, ci sa moze pristupit k samotnej operacii s 2 PLA */
    void minimizeExact(int exact_cover, const char* function) throw (EspressoException); /* spolocna cast pre exact() aj qm() */
    PLA* fillUpPLA(PLA* toFill); /* do toFill skopiruje kocku a ostatne struktury PLA okrem pokrytia F */
public:
    /* KONSTRUKTORY & DESTRUKTOR */
    PLA();
    ~PLA();

    /* METODY NA NACITANIE LOGICKEJ FUNKCIE */
    void loadFromFile(const char* filename) throw (EspressoException); /* nacita PLA zo suboru daneho nazvom */
    void loadFromFile(FILE* fdescr) throw (EspressoException); /* nacita PLA z daneho vstupu */
    void loadFromString(const char *function) throw (EspressoException); /* funkcia, ktora poklada zadany retazec ako predpis funkcie */
    bool isLoaded() const; /* true, ak je PLA uspesne nacitane */
    bool hasPhase() const throw (UninitializedPLAException); /* true, ak PLA obsahuje explicitny zaznam o svojej fazi (phase) */

    /* STATICKE METODY NA NASTAVENIE ESPRESSOOPTIONS */
    static void setOptions(const EspressoOptions &opt); /* nastavi premenne pre Espresso - debug, verbose, fast,...; */
    static void setDefaultOptions();
    static const char* copyright(); /* OCCTOOLS copyright */

    /* METODY VYKONAVAJUCE ESPRESSO ALGORITMY */
    void espresso() throw (EspressoException); /* hlavna minimalizacna metoda */
    void exact() throw (EspressoException); /* spusti exaktny minimalizacny algoritmus */
    void qm() throw (EspressoException); /*rychlejsia verzia exact(), ktora vyraba len jedno pokrytie */
    void so(int strategy = 0) throw (EspressoException); /* kazdu funkciu minimalizuje ako single-output funkciu */
            /* strategy: 0=minimize, 1=exact */
    void so_both(int strategy = 0) throw (EspressoException); /* so() s vyberom mensieho pokrytia - vybera mensiu funkciu z ON-setu alebo OFF-setu */
            /* strategy: 0=minimize, 1=exact */
    void simplify() throw (EspressoException); /* rychle zjednodusenie ON-setu */
    void signature() throw (EspressoException); /*  vykona "signature cubes algorithm" */
    void opo(bool exact_minimize = false, bool repeated = false, bool skip_sparse = false) throw (EspressoException); /* vykona optimalizaciu vystupnej fazy */
    void opoall(int first = -1, int last = -1, int strategy = 0) throw (EspressoException); /* Minimalizuje funkciu so vsetkymi moznymi priradeniami fazy */
                /* first a last predstavuju rozsah vystupnych premennych; -1 a -1 pre cely rozsah */
                /* strategy: 0=espresso minimalizacia; 1=exaktna minimalizacia; defaultna je espresso */
    void pair(int strategy = 0) throw (EspressoException);
            /* strategy: 0=algebraic, 1=strong, 2=espresso, 3=exact */
    void pairall(int strategy = 0) throw (EspressoException);
            /* strategy: 0=minimize, 1=exact, 2=opo */

    /* METODY NAD DVOMI LOGICKYMI FUNKCIAMI */
    PLA* _union_(PLA* second) throw (EspressoException);
    PLA* _xor_(PLA* second) throw (EspressoException);
    PLA* sharp(PLA* second) throw (EspressoException);
    PLA* dsharp(PLA* second) throw (EspressoException);
    PLA* intersect(PLA* second) throw (EspressoException);
    bool verify(PLA* second) throw (EspressoException);
    bool PLAverify(PLA* second) throw (EspressoException);

    /* JEDNOTLIVE KROKY ESPRESSO ALGORITMU */
    void essen() throw (EspressoException); /* "check for essential primes" */
    void expand() throw (EspressoException); /* "execute expand" */
    void gasp() throw (EspressoException);
    void super_gasp() throw (EspressoException);
    void irred() throw (EspressoException); /* extract minimal irredundant subset */
    void make_sparse() throw (EspressoException); /* "make_sparse step of Espresso" - sparse = roztrusenie, rozptylenie; redukuje celkovy pocet literalov v pokryti */
    void reduce() throw (EspressoException); /* perform reduction */
    void test() throw (EspressoException); /* 1 testovacia iteracia Espresso algoritmu */

    /* ZMIESANE METODY */
    PLA* copy() throw (EspressoException); /* metoda na vytvorenie kopie */
    int seto(const char* type); /* nastavuje output_type, parameter su vsetky pripustne vystupne formaty; vrati 0 pri uspechu */
    void print(FILE* target) throw (EspressoException); /* metoda na vypisanie sa obvodu; -1 pri neuspechu */
    void print(const char *filename) throw (EspressoException); /* metoda na vypisanie sa obvodu; -1 pri neuspechu */
    void pos() throw (EspressoException); /* vymeni ON-set a OFF-set a dopocita fazu */
    void lexsort() throw (EspressoException); /* "lexical sort order" = zoradi kocky standardnym lexikalnym sposobom */

    /* METODY POSKYTUJUCE INFORMACIE O LOGICKEJ FUNKCII */
    bool check(FILE* target=NULL) throw (EspressoException);   /* urcuje kam ma vypisat 4 riadky informacii a sucasne vrati informaciu, ci bolo vsetko OK [OK==1; nejaka chyba==0; chyba alokacie == -1]*/
    bool check(const char *filename) throw (EspressoException);   /* urcuje kam ma vypisat 4 riadky informacii a sucasne vrati informaciu, ci bolo vsetko OK [OK==1; nejaka chyba==0; chyba alokacie == -1]*/
    bool ONDCdisjoint() throw (EspressoException);        /* ON-SET and DC-SET are disjoint? */
    bool ONOFFdisjoint() throw (EspressoException);       /* ON-SET and OFF-SET are disjoint? */
    bool DCOFFdisjoint() throw (EspressoException);       /* DC-SET and OFF-SET are disjoint? */
    bool universe() throw (EspressoException);            /* Union of ON-SET, OFF-SET and DC-SET is the universe? */
    void printStats(FILE* target) throw (EspressoException); /* metoda na vypis informacii do suboru */
    void printStats(const char * filename) throw (EspressoException); /* metoda na vypis informacii do suboru */
    PLAStats* getStats() throw (EspressoException); /* metoda vrati strukturu obsahujucu vsetky informacie o funkcii */
    bool taut() throw (EspressoException); /* zistuje, ci je dana fukcia tautologia; 0 = false, 1 = true, -1 = chyba */
    bool printTaut(FILE *target) throw (EspressoException); /* vypise, ci je dana funkcia tautologia, ak nedoslo k chybe */
    bool printTaut(const char *filename) throw (EspressoException); /* vypise, ci je dana funkcia tautologia, ak nedoslo k chybe */
    int outputs() throw (UninitializedPLAException); /* vrati pocet vystupov; pre pouzitie s opoall() */
    int inputs() throw (UninitializedPLAException); /* vrátí pocet premennych v logickej funkcii; pre pouzitie s minterms() a d1merge() */

    /* TZV. "HACK-Y" */
    void printEquiv(FILE* target) throw (EspressoException); /* vypise ekvivalentne vystupy */
    void printEquiv(const char* filename) throw (EspressoException); /* vypise ekvivalentne vystupy */
    std::vector< std::pair<int, int> >* equiv() throw (EspressoException); /* vrati vektor dvojic ekvivalentnych vystupov */
    void map(FILE* target) throw (EspressoException); /* do zvoleneho vystupu vypise Karnaughovu mapu funkcie; pri chybe nezmaze PLA */
    void map(const char * filename) throw (EspressoException); /* do zvoleneho vystupu vypise Karnaughovu mapu funkcie */
    void map_dcset() throw (EspressoException); /* upravi sa PLA a zmeni sa jeho output_type */
    void contain() throw (EspressoException); /* single set containment - zmaze sety, ktore su obsiahnute v nejakom vacsom sete */
    void d1merge(int first = -1, int last = -1) throw (EspressoException);
    void d1merge_in() throw (EspressoException); /* distance-1 spojenie ale len pre vstupy */
    void disjoint() throw (EspressoException);
    void minterms(int first = -1, int last = -1) throw (EspressoException);
    void primes() throw (EspressoException); /* generuje vsetky priame implikanty (pomocou konsenzu) */
    void separate() throw (EspressoException); /* odstrani DC set z ON setu */
};


class DLL_EXPORT EspressoOptions{
private:
    /* kopie globalnych premennych*/
    bool3 echocomments;		 /* turned off by -eat option */
    bool3 echounknowncommands;	 /* always true ?? */
    bool3 forceirredundant;          /* -nirr command line option */
    bool3 skipmakesparse;
    bool3 kiss_;                       /* -kiss command line option */
    bool3 recomputeonset;            /* -onset command line option */
    bool3 removeessential;           /* -ness command line option */
    bool3 singleexpand;              /* -fast command line option */
    bool3 summary_;                    /* -s command line option */
    bool3 trace_;                      /* -t command line option */
    bool3 unwraponset;               /* -nunwrap command line option */
    bool3 userandomorder;		 /* -random command line option */
    bool3 usesupergasp;		 /* -strong command line option */

    unsigned int debug_; /* priznak obycajneho debugingu */
    bool3 verbose_; /* priznak verbose debuggingu */
    /* tieto filedescriptory pribudnu aj do Espressa */
    FILE* tracesumout;
    FILE* debugout;
    FILE* errorout;
    FILE* eatout;
    FILE* eatdotsout;
    FILE* standardout; /* musel som tu pridat aj takyto nic nehovoriaci vystup, lebo v jednej funkcii som narazil na vypis, ktory som nevedel zaradit do ziadnej z predchadzajucich kategorii */
public:
    EspressoOptions(); /* vsetko je FALSE3 a vystupy do NULL */
    friend class PLA;
    /* metody */
    void reset(); /* nastavi vsetky hodnoty na FALSE3 a vystupy do NULL */
    void setEspressoDefaults(); /* nastavi hodnoty a vystupy tak, ako boli v defaultne v povodnom Espresse */
    /* settry a gettry */
    bool trace() const; /* vrati hodnotu premennej trace_ */
    void setTrace(bool value); /* nastavi trace na value */
    bool summary() const; /* vrati hodnotu premennej trace_ */
    void setSummary(bool value); /* nastavi trace na value */
    bool debug() const; /* indikuje zapnutie dubugovania */
    void setDebug(bool value); /* nastavi vypisovanie debugovacich informacii; nastavenie na false vypne aj verbose debug; uz nezapina trace a summary [maju iny vystup] */
    bool verboseDebug() const; /* indikuje, ci je zapnute verbose_debug */
    void setVerboseDebug(bool value); /* nastavi vypisovanie podrobnych  debugovacich informacii; ovplyvnuje aj "debug" */
    int setVerboseDebug(const char * type); /* ak argument existuje, vrati 0 */

    /* nastavenie vystupov */
    /* musia udrziavat konzistenciu vystupov a im odpovedajucim bool3 premennym */
    void setTraceSummaryOutput(FILE* fdescr); /* vystup pre parameter -t a -s*/
    void setDebugOutput(FILE* fdescr); /* vztahuje sa aj na "verbose_debug" */
    void setErrorOutput(FILE* fdescr); /* vystup pre vsetky chybove hlasky */
    void setEatOutput(FILE* fdescr); /* tak nastavi echo_comments na TRUE3 a vystup pojde do fdescr; {"eat", &echo_comments, FALSE3} */
    void setEatdotsOutput(FILE* fdescr); /* setEat pre echo_unknown_commands; {"eatdots", &echo_unknown_commands, FALSE3} */
    void setStandardOutput(FILE* fdescr); /* vystup pre vsetky ostatne hlasenia */

    /* nastavenie cistych espresso prepinacov; volby -e; vyznam vsetkych e vysvetleny v manuale */
    void setEat(bool value); /* povoli vypisovanie komentarov; {"eat", &echo_comments, FALSE3} */
    void setEatdots(bool value); /* povoli vypisovanie neznamych prikazov {"eatdots", &echo_unknown_commands, FALSE3} */
    void setFast(bool value); /* {"fast", &single_expand, TRUE3} */
    void setKiss(bool value); /* {"kiss", &kiss, TRUE3} */
    void setNess(bool value); /* {"ness", &remove_essential, FALSE3} */
    void setNirr(bool value); /* {"nirr", &force_irredundant, FALSE3} */
    void setNunwrap(bool value); /* {"nunwrap", &unwrap_onset, FALSE3} */
    void setOnset(bool value); /* {"onset", &recompute_onset, TRUE3} */
    void setRandom(bool value); /* {"random", &use_random_order, TRUE3} */
    void setStrong(bool value); /* {"strong", &use_super_gasp, TRUE3} */
    /* metody vratia hodnotu espresso prepinacov */
    bool eat() const;
    bool eatdots() const;
    bool fast() const;
    bool kiss() const;
    bool ness() const;
    bool nirr() const;
    bool nunwrap() const;
    bool onset() const;
    bool random() const;
    bool strong() const;
};

#endif /* ESPRESSO_LIBH_321655746546465631564321 */
