/*******************************************************************************
 * Copyright (c) 2015-2017 ONERA and IRT AESE (IRT Saint Exupéry).
 * Ce logiciel est la propriété de l’ONERA (the French Aerospace Lab) et de
 * l'IRT AESE (IRT Saint Exupéry).
 * Tous droits réservés.
 *
 * Ce programme et les éléments qui l'accompagnent sont mis à disposition
 * aux conditions définies par le contrat de licence logiciel CeCILL-C soumise
 * au droit français et respectant les principes de diffusion des logiciels libres.
 * Vous pouvez utiliser, modifier et/ou redistribuer ce programme
 * sous les conditions de la licence CeCILL-C (http://www.cecill.info).

 * This software is property of ONERA (the French Aerospace Lab) and of
 * the IRT AESE (IRT Saint Exupéry).
 * All rights reserved.
 *
 * This program and the accompanying materials are made available under
 * the terms of the CeCILL-C license under French law and
 * abiding by the rules of distribution of free software.
 * You can  use, modify and/ or redistribute the software under the terms of
 * the CeCILL-C license (http://www.cecill.info).
 *
 * Contributeurs/contributors:
 *     Guillaume Infantes (ONERA - Centre de Toulouse) - initial API and implementation
 *     Alexandre Albore (IRT Saint-Exupéry) - Altarica/Fiacre translation
 *
 *******************************************************************************/

#include "ExpressionEvaluator.hh"
#include "Expressions.hh"
#include "../AltaricaState.hh"
#include "../AltaricaModel.hh"
#include "../Assignment.hh"
#include "../AltaricaException.hh"
#include "../Constant.hh"
#include "../Node.hh"
#include "../Values.hh"
#include "../Variable.hh"
// #include <iostream>

using namespace epoch::altarica;

Value * ExpressionEvaluator::visit(const Expression *e) {
  e->accept(this);
  Value *v = val_;
  if (v == NULL)
    std::cerr << "NULL value computed!!!\n";  
  val_ = NULL;
  return v;
}

void ExpressionEvaluator::visitADD(const ExpressionADD *e){
  Value *lv = visit(e->getLeft());
  Value *rv = visit(e->getRight());

  if (lv->isBool() || rv->isBool() || lv->isSymbol() || rv->isSymbol()) {
    Value::deleteIfPossible(lv);
    Value::deleteIfPossible(rv);
    throw AltaricaException("PLUS/MINUS asked on bools or symbols");
  }


  ValueInt * li = NULL;
  ValueReal *lr = NULL;
  ValueInt * ri = NULL;
  ValueReal *rr = NULL;
  if (lv->isInt())
    li = (ValueInt*) lv;
  else
    lr = (ValueReal*) lv;
  if (rv->isInt())
    ri = (ValueInt*) rv;
  else
    rr = (ValueReal*) rv;

  if (lv->ro())
    if (li && ri)
      li = (ValueInt*) lv->clone();
    else if (lr)
      lr = (ValueReal*) lv->clone();
    else {
      lr = new ValueReal((double)li->toInt());
      Value::deleteIfPossible(li);
      li = NULL;
    }

  if (e->getType() == 0) {
    if (li)
      li->add(ri);
    else
      if (ri)
        lr->add(new ValueReal((double)ri->toInt()));
      else
        lr->add(rr);
  } else {
    if (li)
      li->minus(ri);
    else
      if (ri)
        lr->minus(new ValueReal((double)ri->toInt()));
      else
        lr->minus(rr);
  }
  Value::deleteIfPossible(rv);
  if (li)
    val_ = li;
  else
    val_ = lr;
}

void ExpressionEvaluator::visitAND(const ExpressionAND *e){
  Value * lv = visit(e->getLeft());


  // if (lv->isBool() != rv->isBool()) {
  //   std::cerr<< state_->toString(true,true,true,true);
  //   throw AltaricaException(
  //     std::string() + "AND left and right argument are not of same type -> " +
  //     "do not know if i have to do a unification or a logical comparison in " +
  //     e->toString());
  //   Value::deleteIfPossible(lv);
  //   Value::deleteIfPossible(rv);
  // }

  if (lv->isBool()) {
    ValueBool * lb = (ValueBool*) lv;
    if (lb->ro())
      lb = (ValueBool*) lb->clone();

    if (lb->allowedTop()) {      
      ValueBool * rb = (ValueBool*) visit(e->getRight());      
      lb->land(rb);      
    }
    val_ = lb;
    
    
  } else {
    if (lv->ro())
      lv =  lv->clone();
    Value * rv = visit(e->getRight());    
    lv->intersectValue(rv);
    val_ =  lv;
    Value::deleteIfPossible(rv);
  }

}

void ExpressionEvaluator::doAffectIfNecessary(Value *v, const Expression * le) {
  if (!v->constrained()) {

    const ExpressionMEMBER * em = dynamic_cast<const ExpressionMEMBER*>(le);
    if (em != NULL)  // we have to do the affectation
      state_->setValue(em->getVariable(node_, amodel_),v);
  }
}

void ExpressionEvaluator::visitARITH(const ExpressionARITH *e)  {
  Value * lv = visit(e->getLeft());
  Value * rv = visit(e->getRight());

  if (lv->isBool() || rv->isBool() || lv->isSymbol() || rv->isSymbol()) {
    throw AltaricaException(
      "ARITHMETIC OPERATION ON NOT INTs nor REALs.." + lv->toString() +
      "  " + rv->toString() + " in " + e->toString());
  }

  ValueInt * li = NULL;
  ValueReal *lr = NULL;
  if (lv->isInt())
    li = (ValueInt*) lv;
  else
    lr = (ValueReal*) lv;
  ValueInt * ri = NULL;
  ValueReal * rr = NULL;
  if (rv->isInt())
    ri = (ValueInt*) rv;
  else
    rr = (ValueReal*) rv;



  if (assert_) {

    if (li && ri) {
      if (li->ro())
        li = (ValueInt*) li->clone();
      switch(e->getType()) {
      case 0:     li->setToInfThan(ri);    break;
      case 1:     li->setToSupThan(ri);    break;
      case 2:     li->setToInfEqThan(ri);  break;
      case 3:     li->setToSupEqThan(ri);  break;
      }
      doAffectIfNecessary(li, e->getLeft());
    } else if (lr && rr) {
      if (lr->ro())
        lr = (ValueReal*) lr->clone();
      switch(e->getType()) {
      case 0:     lr->setToInfThan(rr);    break;
      case 1:     lr->setToSupThan(rr);    break;
      case 2:     lr->setToInfEqThan(rr);  break;
      case 3:     lr->setToSupEqThan(rr);  break;
      }
      doAffectIfNecessary(lr, e->getLeft());
    } else if (li && rr) {
      ValueReal * lr = new ValueReal(li);
      switch(e->getType()) {
      case 0:     lr->setToInfThan(rr);    break;
      case 1:     lr->setToSupThan(rr);    break;
      case 2:     lr->setToInfEqThan(rr);  break;
      case 3:     lr->setToSupEqThan(rr);  break;
      }
      doAffectIfNecessary(lr, e->getLeft());
    } else { // lr && ri
      if (lr->ro())
        lr = (ValueReal*) lr->clone();
      ValueReal * rr  = new ValueReal(ri);
      switch(e->getType()) {
      case 0:     lr->setToInfThan(rr);    break;
      case 1:     lr->setToSupThan(rr);    break;
      case 2:     lr->setToInfEqThan(rr);  break;
      case 3:     lr->setToSupEqThan(rr);  break;
      }
      delete rr;
      doAffectIfNecessary(lr, e->getLeft());
    }

    // if (!lv->constrained()) {
    //     const ExpressionMEMBER * em = dynamic_cast<const ExpressionMEMBER*>(e->getLeft());
    //     if (em != NULL)  // we have to do the affectation
    //    state_->setValue(em->getVariable(node_),lv);
    // }
    val_ = new ValueBool(true,false);
  } else {
    if (li && ri) {
      switch(e->getType()) {
      case 0:    val_ = new ValueBool(li->getMin()<ri->getMax(),li->getMax()>=ri->getMin());    break;
      case 1:    val_ = new ValueBool(li->getMin()>ri->getMax(),li->getMax()<=ri->getMin());    break;
      case 2:    val_ = new ValueBool(li->getMin()<=ri->getMax(),li->getMax()>ri->getMin());    break;
      case 3:    val_ = new ValueBool(li->getMin()>=ri->getMax(),li->getMax()<ri->getMin());    break;
      }
    } else if (lr && ri) {
      switch(e->getType()) {
      case 0:    val_ = new ValueBool(lr->getMin()<ri->getMax(),lr->getMax()>=ri->getMin());    break;
      case 1:    val_ = new ValueBool(lr->getMin()>ri->getMax(),lr->getMax()<=ri->getMin());    break;
      case 2:    val_ = new ValueBool(lr->getMin()<=ri->getMax(),lr->getMax()>ri->getMin());    break;
      case 3:    val_ = new ValueBool(lr->getMin()>=ri->getMax(),lr->getMax()<ri->getMin());    break;
      }
    } else if (li && rr) {
      switch(e->getType()) {
      case 0:    val_ = new ValueBool(li->getMin()<rr->getMax(),li->getMax()>=rr->getMin());    break;
      case 1:    val_ = new ValueBool(li->getMin()>rr->getMax(),li->getMax()<=rr->getMin());    break;
      case 2:    val_ = new ValueBool(li->getMin()<=rr->getMax(),li->getMax()>rr->getMin());    break;
      case 3:    val_ = new ValueBool(li->getMin()>=rr->getMax(),li->getMax()<rr->getMin());    break;
      }
    } else { //lr && rr
      switch(e->getType()) {
      case 0:    val_ = new ValueBool(lr->getMin()<rr->getMax(),lr->getMax()>=rr->getMin());    break;
      case 1:    val_ = new ValueBool(lr->getMin()>rr->getMax(),lr->getMax()<=rr->getMin());    break;
      case 2:    val_ = new ValueBool(lr->getMin()<=rr->getMax(),lr->getMax()>rr->getMin());    break;
      case 3:    val_ = new ValueBool(lr->getMin()>=rr->getMax(),lr->getMax()<rr->getMin());    break;
      }
    }
    Value::deleteIfPossible(lv);
  }
  Value::deleteIfPossible(rv);

}


// gives the highest element
void ExpressionEvaluator::visitINTERVAL(const ExpressionINTERVAL *e)  {
	Value * lv = visit(e->getLeft());
	Value * rv = visit(e->getRight());

	ValueReal * v;
	v = (ValueReal*)rv;
	val_ = new ValueReal(v->toDouble());

	Value::deleteIfPossible(rv);
	Value::deleteIfPossible(lv);
}


void ExpressionEvaluator::visitBOOLCONSTANT(const ExpressionBOOLCONSTANT *e)  {
  val_ =  e->get();
}

void ExpressionEvaluator::visitCARD(const ExpressionCARD *e)  {
  int i = 0;
  for (std::vector<Expression*>::const_iterator ei = e->getExprs()->begin(); ei != e->getExprs()->end(); ++ei) {
    Value *v = visit(*ei);//val_;
    i+= v->card();
    Value::deleteIfPossible(v);
  }
  val_ = new ValueInt(i);
}
void ExpressionEvaluator::visitCONSTARRAY(const ExpressionCONSTARRAY *e)  {
  throw AltaricaException("Const Array eval not implemented!!!");
}

void ExpressionEvaluator::visitCONSTSTRUCT(const ExpressionCONSTSTRUCT *e)  {
  throw AltaricaException("Const Struct eval not implemented!!!");
}

void ExpressionEvaluator::visitCASE(const ExpressionCASE *e)  {
  bool doElse = false;
  bool doCase = false;
  Value * ret = NULL;
  const std::vector<Expression*> * conds = e->getConds();
  const std::vector<Expression*> * thens = e->getThens();
  for (unsigned int i = 0; i< conds->size(); ++i) {
    //  std::cerr << (*conds)[i]->toString() << std::endl;
    Value *c = visit((*conds)[i]);
    if (!c->isBool())
      throw AltaricaException("condition of case is not a bool!");


    ValueBool *c2 = (ValueBool*)c;
    if (c2->allowedTop()) {
      doCase = true;
      Value *v = visit((*thens)[i]);
      if (ret == NULL) {        
        ret = v;
        
      }
      else {
        ret->merge(v);
        Value::deleteIfPossible(v);
      }
    }
    if (c2->allowedBottom())
      doElse = true;
    Value::deleteIfPossible(c2);


  }

  if (e->getElse() != NULL && (doElse && !doCase)) {
    Value *v = visit(e->getElse());
    if (ret == NULL)
      ret = v;
    else {
      ret->merge(v);
      Value::deleteIfPossible(v);
    }
    // if (ret == NULL)
    //   std::cerr << "NULL value in case\n";
  }
  val_ = ret;
  // if (val_ == NULL)
  //   std::cerr << "NULL value in case\n";  
}

void ExpressionEvaluator::visitFUNCTIONCALL(const ExpressionFUNCTIONCALL *e)  {
  throw AltaricaException("function eval  not implemented");
}

void ExpressionEvaluator::visitIfThenElse(const ExpressionIfThenElse *e)  {
  Value *c = visit(e->getIf());
  if (!c->isBool()) {
    Value::deleteIfPossible(c);
    throw AltaricaException("conditon of ifthenelse is not a bool in " +
                            e->toString());
  }

  ValueBool * c2 = (ValueBool*) c;

  if (c2->allowedTop())
    val_ = visit(e->getThen());
  if (c2->allowedBottom()) {
    Value * v = visit(e->getElse());
    if (val_ == NULL)
      val_ = v;
    else {
      val_->merge(v);
      Value::deleteIfPossible(v);
    }
  }
  Value::deleteIfPossible(c);
}


void ExpressionEvaluator::visitMEMBER(const ExpressionMEMBER *e)  {
  Variable * v = NULL;
  Value * vv;
  Constant *c = NULL;

  //cached version (dig expressions.old or svn for uncached versions)
  switch (e->getIsSymbol()) {
  case -1:
    if (node_ != NULL && node_->isSymbol(e->getSymbol())) {
      e->setIsSymbol(0);
      e->setSymbol();
      val_ = e->getSymbolValue();
    } else {
      if (node_ != NULL) {
        v = node_->findStateVariableByName(e->getMA());
        e->resizeCache(amodel_->getNNodes());
      }
      if (v != NULL) {
        e->setIsSymbol(1);
        e->setCache(node_->getIndex(), v);
        vv = state_->getValue(v);
        if (vv == NULL)
          vv = v->getInit()->evaluate(amodel_, state_,node_);        
        vv->setDomain(v->getDomain());
        val_ = vv;
      } else {
        if (node_ != NULL)
          v = node_->findFlowVariableByName(e->getMA());
        if (v != NULL) {
          e->setIsSymbol(2);
          e->setCache(node_->getIndex(), v);
          v->evaluate(amodel_, state_);
          val_ = state_->getValue(v);
        } else {
          c = amodel_->findConstantByName(e->getMA()->toString());
          if (c != NULL) {
            e->setIsSymbol(3);
            val_ = c->getValue(amodel_, state_, node_);
          } else {
            // in this case, symbol that is not in n->symbolCache ( -> symbol of subnode....)
            e->setIsSymbol(0);
            e->setSymbol();
            val_ = e->getSymbolValue();
          }
        }
      }
    }
    break;
  case 0:
    val_ = e->getSymbolValue();
    break;
  case 1:
    v = e->getVarFromCache(node_->getIndex());
    if (v == NULL) {
      v = node_->findStateVariableByName(e->getMA());
      e->setCache(node_->getIndex(),v);
    }
    vv = state_->getValue(v);
    vv->setDomain(v->getDomain());
    val_ = vv;
    break;
  case 2:
    v = e->getVarFromCache(node_->getIndex());
    if (v == NULL) {
      v = node_->findFlowVariableByName(e->getMA());
      e->setCache(node_->getIndex(),v);
    }
    v->evaluate(amodel_, state_);    
    val_ = state_->getValue(v);
    break;
  case 3:
    c = amodel_->findConstantByName(e->getMA()->toString());
    val_ = c->getValue(amodel_, state_, node_);
    break;
  default:
    ValueSymbol *os = new ValueSymbol(e->getSymbol());
    val_ = os;
  }
}


void ExpressionEvaluator::visitMINMAX(const ExpressionMINMAX *e)  {
  const std::vector<Expression*> * exprs = e->getExprs();

  Value *optV = visit(*(exprs->begin()));
  if (!optV->isInt() && !optV->isReal()) {
    delete(optV);
    throw AltaricaException("MIN/MAX on not int nor real in " +
                            e->toString());
  }

  ValueInt * optI;
  ValueReal * optR;
  if (optV->isInt())
    optI = (ValueInt*)(optV);
  else
    optR = (ValueReal*)(optV);

  if (e->max()) {
    int maxI = std::numeric_limits<int>::min();
    double maxR =  std::numeric_limits<double>::min();
    if (optI) {
      maxI = optI->max();
      Value::deleteIfPossible(optI);
    } else {
      maxR = optR->max();
      Value::deleteIfPossible(optR);
    }


    for (std::vector<Expression*>::const_iterator eIT = exprs->begin(); eIT != exprs->end(); ++eIT) {
      Value *v = visit((*eIT));
      if (!v->isInt() && !v->isReal()) {
        Value::deleteIfPossible(v);
        throw AltaricaException("MIN/MAX on not an int nor a real in " +
                                e->toString());
      } else {
        if (v->isInt()) {
          int cmax = ((ValueInt*)v)->max();
          if (cmax > maxI)
            maxI = cmax;
        } else {
          double cmax = ((ValueReal*)v)->max();
          if (cmax > maxI)
            maxR = cmax;
        }
        Value::deleteIfPossible(v);
      }
    }
    if (maxR > maxI)
      val_ = new ValueReal(maxR);
    else if (maxR != std::numeric_limits<double>::min())
      val_ = new ValueReal(maxI);
    else
      val_ = new ValueInt(maxI);
  } else {

    int minI =  std::numeric_limits<int>::max();
    double minR = std::numeric_limits<double>::max();
    if (optI)
      minI = optI->min();
    else
      minR = optR->min();

    for (std::vector<Expression*>::const_iterator eIT = exprs->begin();  eIT != exprs->end(); ++eIT) {
      Value *v = visit(*eIT);
      if (!v->isInt() && !v->isReal()) {
        Value::deleteIfPossible(v);
        throw AltaricaException("MIN/MAX on not an int nor a real :" +
                                e->toString());
      } else {
        if (v->isInt()) {
          int cmin = ((ValueInt*)v)->min();
          if (cmin < minI)
            minI = cmin;
        } else {
          int cmin = ((ValueReal*)v)->min();
          if (cmin < minR)
            minR = cmin;
        }
        Value::deleteIfPossible(v);
      }
    }
    if (minR < minI)
      val_ =  new ValueReal(minR);
    else if (minR != std::numeric_limits<double>::max())
      val_ = new ValueReal(minI);
    else
      val_ = new ValueInt(minI);
  }
}
void ExpressionEvaluator::visitMUL(const ExpressionMUL *e)  {
  Value *lv = visit(e->getLeft());
  Value *rv = visit(e->getRight());

  if ( (!lv->isInt()&&!lv->isReal()) || (!rv->isInt()&&!rv->isReal())) {
    Value::deleteIfPossible(lv);
    Value::deleteIfPossible(rv);
    throw AltaricaException("TIMES/DIV asked on bools in " + e->toString());
  }

  ValueInt * li = NULL;
  ValueReal * lr = NULL;
  ValueInt * ri = NULL;
  ValueReal * rr = NULL;
  if (lv->isInt())
    li =     (ValueInt*) lv;
  else
    lr = (ValueReal*) lv;
  if (rv->isInt())
    ri = (ValueInt*) rv;
  else
    rr = (ValueReal*) rv;

  if (li && ri) {
    if (li->ro())
      li = (ValueInt*) li->clone();
    switch (e->getType()) {
    case 0:   li->times(ri);      break;
    case 1:     li->div(ri);        break;
    default:    li->mod(ri);        break;
    }
    val_ = li;
    Value::deleteIfPossible(rv);
    return;
  }

  if (li) {
    lr = new ValueReal(li);
    Value::deleteIfPossible(li);
  } else
    if (lr->ro())
      lr = (ValueReal*) lr->clone();

  if (ri) {
    rr =  new ValueReal(ri);
    Value::deleteIfPossible(ri);
  }

  switch (e->getType()) {
  case 0:    lr->times(rr);       break;
  case 1:     lr->div(rr);        break;
  default:    lr->mod(rr);        break;
  }

  Value::deleteIfPossible(rr);

  val_ = lr;
}

void ExpressionEvaluator::visitPAREN(const ExpressionPAREN *e)  {
  val_ = visit(e->getExpr());
}

void ExpressionEvaluator::visitUNSIGNEDINT(const ExpressionUNSIGNEDINT *e)  {
  val_ = e->getValue();
}

void ExpressionEvaluator::visitREAL(const ExpressionREAL *e)  {
  val_ = e->getValue();
}




void ExpressionEvaluator::visitUNARY(const ExpressionUNARY *e)  {
  Value *c = visit(e->getBelow());
  switch(e->getType()) {
  case 0:  // MINUS
    if (!c->isInt() && !c->isReal()) {
      Value::deleteIfPossible(c);
      throw AltaricaException("unary MINUS on not an int nor a real in " +
                              e->toString());
    }
    if (c->ro())
      c = (Value*) c->clone();
    c->inverse();
    val_ = c;
    break;
  case 1: //NOT
    if (!c->isBool()) {
      Value::deleteIfPossible(c);
      throw AltaricaException("NOT on a not bool in " +e->toString());
    }
    if (c->ro())
      c =  c->clone();
    c->inverse();
    val_ = c;
    break;
  case 2: //COMPL
    if (c->ro())
      c =  c->clone();
    c->inverse();
    val_ = c;
    break;
  }
}



void ExpressionEvaluator::visitOR(const ExpressionOR *e)  {
  Value *lv = visit(e->getLeft());
  Value *rv = visit(e->getRight());

  if (lv->ro())
    lv = lv->clone();

  if (lv->isBool()) {
    if (!rv->isBool()) {
      throw AltaricaException(
        std::string() + "OR left is bool and right is not -> do not know if " +
        "i have to do a unification or a logical comparison" + e->toString());
    }
    ((ValueBool*)lv)->lor((ValueBool*)rv);
    Value::deleteIfPossible(rv);
    val_ = lv;
  } else if (lv->isSymbol()) {
    if (!rv->isSymbol()) {
      throw AltaricaException("OR left is symbol and right is not" +
                              e->toString());
    }
    ((ValueSymbol*)lv)->merge((ValueSymbol*)rv);
    Value::deleteIfPossible(rv);
    val_ = lv;
  } else if (lv->isInt() && rv->isInt()) {
    ((ValueInt*)lv)->intUnion((ValueInt*)rv);
    Value::deleteIfPossible(rv);
    val_ = lv;
  } else {
    if (lv->isReal() && rv->isInt()) {
      ValueReal* rr = new ValueReal((ValueInt*)rv);
      ((ValueReal*)lv)->realUnion(rr);
      delete rr;
      Value::deleteIfPossible(rv);
      val_ = lv;
    } else if (lv->isReal() && rv->isReal()) {
      ((ValueReal*)lv)->realUnion((ValueReal*)rv);
      Value::deleteIfPossible(rv);
      val_ = lv;
    } else { //lv->isInt() && rv->isReal()
      ValueReal * lr = new ValueReal((ValueInt*)lv);
      lr->realUnion((ValueReal*)rv);
      Value::deleteIfPossible(rv);
      Value::deleteIfPossible(lv);
      val_ = lr;
    }
  }





}

void ExpressionEvaluator::visitQUANTIFIED(const ExpressionQUANTIFIED *e)  {
  std::vector<Variable*> vars;
  const std::vector<std::string> * qVars = e->getQVars();
  const std::vector<Domain*> * dVars = e->getDVars();

  for (unsigned int i= 0; i<qVars->size(); ++i) {
    Variable * v = new Variable((*qVars)[i],(*dVars)[i],true);
    v->setNode(node_);
    vars.push_back(v);
  }

  std::vector<std::vector<Value*>*> * combinations = buildCombinations(&vars);
  val_ = new ValueBool();
  bool top;
  bool bottom;

  if (e->forAll()) {
    top = true;
    bottom = false;
    for (std::vector<std::vector<Value*>*>::iterator combi = combinations->begin();
         combi != combinations->end(); ++combi) {
      AltaricaState *ss = state_->clone();
      for (unsigned int i = 0; i<qVars->size(); ++i) {
        ss->setValue(vars[i], (**combi)[i]);
      }
      Value *v = visit(e->getExpr());
      if (v->isBool()) {
        ValueBool * vb = (ValueBool*)v;
        if (!vb->allowedTop()) {
          top = false;
          bottom = true;
        }
        if (vb->allowedBottom())
          bottom = true;
      }
    }
    val_ =  new ValueBool(top,bottom);
  } else {
    top = false;
    bottom = true;
    for (std::vector<std::vector<Value*>*>::iterator combi = combinations->begin();
         combi != combinations->end() ; ++combi) {
      AltaricaState *ss = state_->clone();
      for (unsigned int i = 0; i<qVars->size(); ++i) {
        ss->setValue(vars[i], (**combi)[i]);
      }
      Value *v = visit(e->getExpr());
      if (v->isBool()) {
        ValueBool * vb = (ValueBool*) v;
        if (vb->allowedTop()) {
          top = true;
        }
        if (!vb->allowedBottom())
          bottom = false;
      }
    }
    val_ = new ValueBool(top,bottom);
  }
  //clean combinations
  combinations->clear();
  delete(combinations);
}


void ExpressionEvaluator::visitLOG(const ExpressionLOG *e)  {
  
  bool oldAssert = assert_;
  assert_ = false;
  Value *rv = visit(e->getRight());

  Value *lv;

  assert_ = oldAssert;

  if (!assert_) {
    lv = visit(e->getLeft());
    switch (e->getType()) {
    case 0:
      val_ = new ValueBool(lv->hasIntersection(rv),lv->pDifferent(rv));
      Value::deleteIfPossible(lv);
      Value::deleteIfPossible(rv);
      break;
    case 1:
      val_ = new ValueBool(lv->pDifferent(rv), lv->hasIntersection(rv));
      Value::deleteIfPossible(lv);
      Value::deleteIfPossible(rv);
      break;
    case 2:
      if (lv->isBool()) {
        ValueBool * vb = (ValueBool*) lv;
        if (vb->allowedTop() && !vb->allowedBottom()) {
          val_ =  rv;
        } else {
          val_ = new ValueBool(true,false);
          Value::deleteIfPossible(rv);
        }
        Value::deleteIfPossible(lv);
      } else {
        throw AltaricaException("non boolean at left of implies "
                                + e->toString());
      }
      break;
    }

  } else {
    bool doAffect = false;
    const ExpressionMEMBER * em;
    std::vector<Variable*> * v;
//    lv = visit(e->getLeft());
    // if (e->getType() != 2 && (rv->isBool() != lv->isBool() ||
    //                           rv->isInt() != lv->isInt() ||
    //                           rv->isReal() != lv->isReal() ||
    //                           rv->isSymbol() != lv->isSymbol())) {
    //   throw AltaricaException("LOGICAL op on different types " +
    //                           e->toString());
    //   return;
    // }


    switch (e->getType()) {
    case 0:
      // if (lv->constrained()) {
      //   val_ = new ValueBool(lv->hasIntersection(rv),lv->pDifferent(rv));
      //   Value::deleteIfPossible(rv);
      //   Value::deleteIfPossible(lv);
      //   doAffect = false;
      // } else {
        // if (lv->ro()) {
        //   val_ = lv->clone();
        // }
        // else
        //   val_ = lv;
        // val_->intersectValue(rv);

        // doAffect = true;
        // Value::deleteIfPossible(rv);
      
      if (rv == NULL)
        rv =  visit(e->getRight());
      val_ = rv->clone();
      Value::deleteIfPossible(rv);
      
      // }
      // if (doAffect) {

        em = dynamic_cast<const ExpressionMEMBER*>(e->getLeft());
        if (em != NULL) { // we have to do the affectation
          state_->setValue(em->getVariable(node_, amodel_),val_);
        }
      // }
      break;
    case 1:
      if (lv->constrained()) {
        val_ = new ValueBool(lv->pDifferent(rv), lv->hasIntersection(rv));
        Value::deleteIfPossible(rv);
        Value::deleteIfPossible(lv);
        doAffect = false;
      } else {
        val_ = rv->getComplement();
        Value::deleteIfPossible(rv);
        Value::deleteIfPossible(lv);
        doAffect = true;
      }

      if (doAffect) {
        em = dynamic_cast <const ExpressionMEMBER*> (e->getLeft());
        if (em != NULL)
          state_->setValue(em->getVariable(node_, amodel_),val_);
      }
      break;
    case 2:
      if (lv->isBool()) {
        ValueBool * lb = (ValueBool*) lv;
        if (lb->allowedTop() && !lb->allowedBottom()) {
          Value::deleteIfPossible(lb);
          val_ = rv;
        } else {
          Value::deleteIfPossible(lb);
          Value::deleteIfPossible(rv);
          val_=  new ValueBool(true,false);
        }
      } else {
        throw AltaricaException("LOGICAL => with non-bool left argument");
      }
      break;
    default:
      throw AltaricaException("BAD COMPARISON TYPE IN LOG COMP " +
                              e->toString());
    }
  }


}


std::vector<std::vector<Value*>*> * ExpressionEvaluator::buildCombinations(std::vector<Variable*> * vars) {
  if (vars->size() == 0)
    return new std::vector<std::vector<Value*>*>();
  Variable * v = vars->front();
  vars->erase(vars->begin());
  std::vector<std::vector<Value*>*> * comb = buildCombinations(vars);
  unsigned int s = comb->size();
  DiscreteDomain * dd = (DiscreteDomain*) v->getDomain();
  for (int j = dd->getMin(); j<dd->getMax(); ++j)
    for (unsigned int i = 0; i<s; ++i)
      comb->push_back(new std::vector<Value*>((*comb)[i]->begin(),(*comb)[i]->end()));
  int j = dd->getMin();
  for (unsigned int i =0; i<comb->size(); ++i) {
    (*comb)[i]->insert((*comb)[i]->begin(), dd->getValue(j++));
  }
  return comb;
}
