#ifndef __BDD_h__
#define __BDD_h__

#include "Node.h"
#include "Term.h"
#include <string>
#include <hash_map>
#include <iostream>
using namespace std;


/******************************************************************************/
/* --- pomocna struktura pro redukci BDD --- */
/* --- pouzita jako klic do mapy uzlu --- */

struct Mkey{
  int idV;
  int loId;
  int hiId;
  Mkey(int v,int l,int h): idV(v),loId(l),hiId(h) {}
 };

/******************************************************************************/
/* --- bezkolizní hashovaci funkce --- */

struct Hashuj {
unsigned int operator()(const Mkey & first)
{
  return (pairF(first.idV,pairF(first.loId,first.hiId))) % 15485863;
}
};

/******************************************************************************/
/* --- sablona pro vypis kontejneru --- */
/* --- od iteratoru start, po iterator end, do proudu out(implicitne standatdni vystup) --- */

template <typename TI>
void printCont(TI start,TI end,ostream& out = std::cout){
  //aby se nevypisovala cara, kdyz je kontejner prazdny
  bool delimiter = false;
  for(TI it = start; it!=end; ++it)
    {
      delimiter = true;
      out << *(*it) << endl;
    }
  if(delimiter){
  out << endl;
  out << "------------------------------" << endl;
  }
  }

/******************************************************************************/
/* --- funkce pro rovnost dvou Mkey --- */
struct eqMkey
{
  bool operator()(const Mkey m1, const Mkey m2) const
  {
    return (m1.idV == m2.idV) && (m1.loId == m2.loId) && (m1.hiId == m2.hiId);
  }
};

/******************************************************************************/
/******************************************************************************/

// definice typu pro tabulku pri redukci BDD
typedef hash_map<const Mkey, Node *, Hashuj,eqMkey> Mapa;
// definice typu pro tabulku pri kopirovani BDD
typedef map<Node*,Node*> Mapa_uzlu;
// definice typu pro tabulku pri funkci apply na BDD
typedef hash_map<const pair_of_nodes, Node *, hashF,eqPofN> Mapa_apply;

class BDD {
friend class PLAtab;

   int varCt;//pocet promenych
   Node * root;//koren
   Mapa tab;
   Term * oneT;
   Term * zeroT;
   NodeList * arrayLevel;//seznamy s uzly v jednotlivych urovnich
   void   del_nodes();
   void   copy_bdd(BDD & src_BDD);

public:
        BDD(int var = 0);
        BDD(int *tt,int n);
        BDD(BDD & src);
       ~BDD();

   BDD    operator=(BDD  &src);

   void   reduce();
   bool   isTautology();
   void   vypisLevely(ostream& out = std::cout) const;
   string nacti(char * source = "pokus.pla");
   void   applyOR(BDD & sec, BDD & vysl);
   void   applyAND(BDD & sec,BDD & vysl);
   void   applyXOR(BDD & sec,BDD & vysl);

   void   print_to_file(char * dst);
   float  count_nodes();

friend ostream& operator<<(ostream & out,const BDD & dia);
friend Node * applyOp(Node * first, Node * second, BDD & diagram, Mapa_apply * map,int (*func_op)(Node *,Node *));
friend Node * rec_copy(Node * src,BDD * diagram,Mapa_uzlu * mapa);
};

#endif
