/*********************************************
 * ZBDD+ Manipulator (SAPPORO-1.58) - Header *
 * (C) Shin-ichi MINATO  (Nov. 22, 2013)     *
 *********************************************/

class ZBDD;
class ZBDDV;

#ifndef _ZBDD_
#define _ZBDD_

#include "BDD.h"

class SeqBDD;

class ZBDD
{
  bddword _zbdd;

public:
  ZBDD(void) { _zbdd = bddempty; }
  ZBDD(int v) { _zbdd = (v==0)? bddempty:(v>0)? bddsingle:bddnull; }
  ZBDD(const ZBDD& f) { _zbdd = bddcopy(f._zbdd); }

  ~ZBDD(void) { bddfree(_zbdd); }

  ZBDD& operator=(const ZBDD& f) { 
    if(_zbdd != f._zbdd) { bddfree(_zbdd); _zbdd = bddcopy(f._zbdd); } 
    return *this;
  }

  ZBDD& operator&=(const ZBDD& f)
    { ZBDD h; h._zbdd = bddintersec(_zbdd, f._zbdd); return *this = h; }

  ZBDD& operator+=(const ZBDD& f)
    { ZBDD h; h._zbdd = bddunion(_zbdd, f._zbdd); return *this = h; }

  ZBDD& operator-=(const ZBDD& f)
    { ZBDD h; h._zbdd = bddsubtract(_zbdd, f._zbdd); return *this = h; }

  ZBDD& operator<<=(int s)
    { ZBDD h; h._zbdd = bddlshift(_zbdd, s); return *this = h; }

  ZBDD& operator>>=(int s)
    { ZBDD h; h._zbdd = bddrshift(_zbdd, s); return *this = h; }

  ZBDD& operator*=(const ZBDD&);
  ZBDD& operator/=(const ZBDD&);
  ZBDD& operator%=(const ZBDD&);

  ZBDD operator<<(int s) const
    { ZBDD h; h._zbdd = bddlshift(_zbdd, s); return h; }

  ZBDD operator>>(int s) const
    { ZBDD h; h._zbdd = bddrshift(_zbdd, s); return h; }

  int Top(void) const { return bddtop(_zbdd); }

  ZBDD OffSet(int v) const
    { ZBDD h; h._zbdd = bddoffset(_zbdd, v); return h; }

  ZBDD OnSet(int v) const
    { ZBDD h; h._zbdd = bddonset(_zbdd, v); return h; }

  ZBDD OnSet0(int v) const
    { ZBDD h; h._zbdd = bddonset0(_zbdd, v); return h; }

  ZBDD Change(int v) const
    { ZBDD h; h._zbdd = bddchange(_zbdd, v); return h; }

  bddword GetID(void) const { return _zbdd; }
  bddword Size(void) const { return bddsize(_zbdd); }
  bddword Card(void) const { return bddcard(_zbdd); }
  bddword Lit(void) const { return bddlit(_zbdd); }
  bddword Len(void) const { return bddlen(_zbdd); }

  void Export(FILE *strm = stdout) const;
  void XPrint(void) const;
  void Print(void) const;
  void PrintPla(void) const;

  ZBDD Swap(int, int) const;
  ZBDD Restrict(const ZBDD&) const;
  ZBDD Permit(const ZBDD&) const;
  ZBDD PermitSym(int) const;
  ZBDD Support(void) const
    { ZBDD h; h._zbdd = bddsupport(_zbdd); return h; }
  ZBDD Always(void) const;

  int SymChk(int, int) const;
  ZBDD SymGrp(void) const;
  ZBDD SymGrpNaive(void) const;

  ZBDD SymSet(int) const;
  int ImplyChk(int, int) const;
  int CoImplyChk(int, int) const;
  ZBDD ImplySet(int) const;
  ZBDD CoImplySet(int) const;

  int IsPoly(void) const;
  ZBDD Divisor(void) const;

  ZBDD ZLev(int lev, int last = 0) const;
  void SetZSkip(void) const;
  ZBDD Intersec(const ZBDD&) const;

  friend ZBDD ZBDD_ID(bddword);

  //friend class SeqBDD;
};

extern ZBDD operator*(const ZBDD&, const ZBDD&);
extern ZBDD operator/(const ZBDD&, const ZBDD&);
extern ZBDD ZBDD_Meet(const ZBDD&, const ZBDD&);
extern ZBDD ZBDD_Random(int, int density = 50);
extern ZBDD ZBDD_Import(FILE *strm = stdin);

extern ZBDD ZBDD_LCM_A(char *, int);
extern ZBDD ZBDD_LCM_C(char *, int);
extern ZBDD ZBDD_LCM_M(char *, int);

inline ZBDD ZBDD_ID(bddword zbdd)
  { ZBDD h; h._zbdd = zbdd; return h; }

inline ZBDD BDD_CacheZBDD(char op, bddword fx, bddword gx)
  { return ZBDD_ID(bddcopy(bddrcache(op, fx, gx))); }

inline ZBDD operator&(const ZBDD& f, const ZBDD& g)
  { return ZBDD_ID(bddintersec(f.GetID(), g.GetID())); }

inline ZBDD operator+(const ZBDD& f, const ZBDD& g)
  { return ZBDD_ID(bddunion(f.GetID(), g.GetID())); }

inline ZBDD operator-(const ZBDD& f, const ZBDD& g)
  { return ZBDD_ID(bddsubtract(f.GetID(), g.GetID())); }

inline ZBDD operator%(const ZBDD& f, const ZBDD& p)
  { return f - (f/p) * p; }

inline int operator==(const ZBDD& f, const ZBDD& g)
  { return f.GetID() == g.GetID(); }

inline int operator!=(const ZBDD& f, const ZBDD& g)
  { return !(f == g); }

inline ZBDD& ZBDD::operator*=(const ZBDD& f)
  { return *this = *this * f; }

inline ZBDD& ZBDD::operator/=(const ZBDD& f)
  { return *this = *this / f; }

inline ZBDD& ZBDD::operator%=(const ZBDD& f)
  { return *this = *this % f; }


class ZBDDV
{
  ZBDD _zbdd;

public:
  ZBDDV(void) { _zbdd = 0; }
  ZBDDV(const ZBDDV& fv) { _zbdd = fv._zbdd; }
  ZBDDV(const ZBDD& f, int location = 0);
  ~ZBDDV(void) { }

  ZBDDV& operator=(const ZBDDV& fv) { _zbdd = fv._zbdd; return *this; }
  ZBDDV& operator&=(const ZBDDV& fv) { _zbdd &= fv._zbdd; return *this; }
  ZBDDV& operator+=(const ZBDDV& fv) { _zbdd += fv._zbdd; return *this; }
  ZBDDV& operator-=(const ZBDDV& fv) { _zbdd -= fv._zbdd; return *this; }
  ZBDDV& operator<<=(int);
  ZBDDV& operator>>=(int);

  ZBDDV operator<<(int) const;
  ZBDDV operator>>(int) const;

  ZBDDV OffSet(int) const;
  ZBDDV OnSet(int) const;
  ZBDDV OnSet0(int) const;
  ZBDDV Change(int) const;
  ZBDDV Swap(int, int) const;
  
  int Top(void) const;
  int Last(void) const;
  ZBDDV Mask(int start, int length = 1) const;
  ZBDD GetZBDD(int) const;

  ZBDD GetMetaZBDD(void) const { return _zbdd; }
  bddword Size(void) const;
  void Print(void) const;
  void Export(FILE *strm = stdout) const;
  int PrintPla(void) const;
  void XPrint(void) const;
	
  friend ZBDDV operator&(const ZBDDV&, const ZBDDV&);
  friend ZBDDV operator+(const ZBDDV&, const ZBDDV&);
  friend ZBDDV operator-(const ZBDDV&, const ZBDDV&);
};

extern ZBDDV ZBDDV_Import(FILE *strm = stdin);

inline ZBDDV operator&(const ZBDDV& fv, const ZBDDV& gv)
  { ZBDDV hv; hv._zbdd = fv._zbdd & gv._zbdd; return hv; }
inline ZBDDV operator+(const ZBDDV& fv, const ZBDDV& gv)
  { ZBDDV hv; hv._zbdd = fv._zbdd + gv._zbdd; return hv; }
inline ZBDDV operator-(const ZBDDV& fv, const ZBDDV& gv)
  { ZBDDV hv; hv._zbdd = fv._zbdd - gv._zbdd; return hv; }
inline int operator==(const ZBDDV& fv, const ZBDDV& gv)
  {  return fv.GetMetaZBDD() == gv.GetMetaZBDD(); }
inline int operator!=(const ZBDDV& fv, const ZBDDV& gv)
  {  return !(fv == gv); }

inline ZBDDV& ZBDDV::operator<<=(int s)
  { return *this = *this << s; }

inline ZBDDV& ZBDDV::operator>>=(int s)
  { return *this = *this >> s; }

class ZBDD_Hash;
class ZBDD_Hash
{
  struct ZBDD_Entry
  {
    ZBDD _key;
    void* _ptr;
    ZBDD_Entry(void){ _key = -1; }
  };

  bddword _amount;
  bddword _hashSize;
  ZBDD_Entry* _wheel;
  
  ZBDD_Entry* GetEntry(ZBDD);
  void Enlarge(void);
public:
  ZBDD_Hash(void);
  ~ZBDD_Hash(void);
  void Clear(void);
  void Enter(ZBDD, void *);
  void* Refer(ZBDD);
  bddword Amount(void);
};

#endif // _ZBDD_
