
#include "YapInterface.h"

#include "util.h"
#include "cudd.h"

static YAP_Functor FunctorDollarVar, FunctorAnd, FunctorOr, FunctorXor, FunctorNot, FunctorNand, FunctorNor;

void init_cudd(void);

static DdNode *
cudd_and(DdManager *manager, DdNode *bdd1, DdNode *bdd2) {
  DdNode *tmp;
  fprintf(stderr,"bdd %p %p\n", bdd1, bdd2);
  tmp = Cudd_bddAnd(manager, bdd1, bdd2);
  Cudd_Ref(tmp);
  return tmp;
}

static DdNode *
cudd_nand(DdManager *manager, DdNode *bdd1, DdNode *bdd2) {
  DdNode *tmp;
  tmp = Cudd_bddNand(manager, bdd1, bdd2);
  Cudd_Ref(tmp);
  return tmp;
}

static DdNode *
cudd_or(DdManager *manager, DdNode *bdd1, DdNode *bdd2) {
  DdNode *tmp;
  tmp = Cudd_bddOr(manager, bdd1, bdd2);
  Cudd_Ref(tmp);
  return tmp;
}

static DdNode *
cudd_nor(DdManager *manager, DdNode *bdd1, DdNode *bdd2) {
  DdNode *tmp;
  tmp = Cudd_bddNor(manager, bdd1, bdd2);
  Cudd_Ref(tmp);
  return tmp;
}

static DdNode *
cudd_xor(DdManager *manager, DdNode *bdd1, DdNode *bdd2) {
  DdNode *tmp;
  tmp = Cudd_bddXor(manager, bdd1, bdd2);
  Cudd_Ref(tmp);
  return tmp;
}

static DdNode *
cudd_plus(DdManager *manager,  DdNode *l, DdNode *r) {
  DdNode *f;

  f = Cudd_addApply(manager,Cudd_addPlus,l,r);
  Cudd_Ref(f);
  return f;
}

static DdNode *
cudd_times(DdManager *manager,  DdNode *l, DdNode *r) {
  DdNode *f;

  f = Cudd_addApply(manager,Cudd_addTimes,l,r);
  Cudd_Ref(f);
  return f;
}

static DdNode *
cudd_plus_const(DdManager *manager,  DdNode *x, double c) {
  DdNode *f;

  f = Cudd_addConst(manager,c);
  Cudd_Ref(f);
  f = Cudd_addApply(manager,Cudd_addTimes,x,f);
  return f;
}

static DdNode *
cudd_times_const(DdManager *manager,  DdNode *x, double c) {
  DdNode *f;

  f = Cudd_addConst(manager,c);
  Cudd_Ref(f);
  f = Cudd_addApply(manager,Cudd_addTimes,x,f);
  return f;
}

static DdNode *
cudd_factor(DdManager *manager, int n, int c,  DdNode *mps[]) {
  DdNode *f;
  int i;

  f = Cudd_addConst(manager,c);
  Cudd_Ref(f);
  for (i = 0; i < n; i++) {
    DdNode *tmp, *var = mps[i-1];
    tmp = Cudd_addApply(manager,Cudd_addTimes,var,f);
    Cudd_Ref(tmp);
    f = tmp;
  }
  return f;
}

static DdNode *
term_to_cudd(DdManager *manager, YAP_Term t)
{
  if (YAP_IsApplTerm(t)) {
    YAP_Functor f = YAP_FunctorOfTerm(t);
    if (f == FunctorDollarVar) {
      DdNode *var = Cudd_addIthVar(manager,YAP_IntOfTerm(YAP_ArgOfTerm(1,t)));

      return var;
    } else if (f == FunctorAnd) {
      DdNode *x1 = term_to_cudd(manager, YAP_ArgOfTerm(1, t));
      DdNode *x2 = term_to_cudd(manager, YAP_ArgOfTerm(2, t));
      DdNode *tmp = cudd_and(manager, x1, x2);
      Cudd_RecursiveDeref(manager,x1);
      Cudd_RecursiveDeref(manager,x2);
      return tmp;
    } else if (f == FunctorOr) {
      DdNode *x1 = term_to_cudd(manager, YAP_ArgOfTerm(1, t));
      DdNode *x2 = term_to_cudd(manager, YAP_ArgOfTerm(2, t));
      DdNode *tmp = cudd_or(manager, x1, x2);
      Cudd_RecursiveDeref(manager,x1);
      Cudd_RecursiveDeref(manager,x2);
      return tmp;
    } else if (f == FunctorXor) {
      DdNode *x1 = term_to_cudd(manager, YAP_ArgOfTerm(1, t));
      DdNode *x2 = term_to_cudd(manager, YAP_ArgOfTerm(2, t));
      DdNode *tmp = cudd_xor(manager, x1, x2);
      Cudd_RecursiveDeref(manager,x1);
      Cudd_RecursiveDeref(manager,x2);
      return tmp;
    } else if (f == FunctorNot) {
      DdNode *x1 = term_to_cudd(manager, YAP_ArgOfTerm(1, t));
      return Cudd_Not(x1);
    }
  } else if (YAP_IsIntTerm(t)) {
    YAP_Int i = YAP_IntOfTerm(t);
    if (i == 0)
      return Cudd_ReadLogicZero(manager);
    else if (i==1)
      return Cudd_ReadOne(manager);
  }
  return NULL;
}

static int
p_term_to_cudd(void)
{
  DdManager *manager = Cudd_Init(0,0,CUDD_UNIQUE_SLOTS,CUDD_CACHE_SLOTS,0);
  DdNode *t = term_to_cudd(manager, YAP_ARG1);
  return YAP_Unify(YAP_ARG2, YAP_MkIntTerm((YAP_Int)t));    
}

void
init_cudd(void)
{
  
  FunctorDollarVar = YAP_MkFunctor(YAP_LookupAtom("$VAR"), 1);
  FunctorAnd = YAP_MkFunctor(YAP_LookupAtom("/\\"), 2);
  FunctorOr = YAP_MkFunctor(YAP_LookupAtom("\\/"), 2);
  FunctorXor = YAP_MkFunctor(YAP_LookupAtom("xor"), 2);
  FunctorNor = YAP_MkFunctor(YAP_LookupAtom("nor"), 2);
  FunctorNand = YAP_MkFunctor(YAP_LookupAtom("nandr"), 2);
  FunctorNot = YAP_MkFunctor(YAP_LookupAtom("~"), 1);
  YAP_UserCPredicate("term_to_cudd", p_term_to_cudd, 2);
}

