/*****************************************************************************/
/*!
 * \file translator.cpp
 * \brief Description: Code for translation between languages
 * 
 * Author: Clark Barrett
 * 
 * Created: Sat Jun 25 18:06:52 2005
 *
 * <hr>
 *
 * License to use, copy, modify, sell and/or distribute this software
 * and its documentation for any purpose is hereby granted without
 * royalty, subject to the terms and conditions defined in the \ref
 * LICENSE file provided with this distribution.
 * 
 * <hr>
 * 
 */
/*****************************************************************************/


#include "translator.h"
#include "expr.h"
#include "theory_core.h"
#include "theory_uf.h"
#include "theory_arith.h"
#include "theory_bitvector.h"
#include "theory_array.h"
#include "theory_quant.h"
#include "theory_records.h"
#include "theory_simulate.h"
#include "theory_datatype.h"
#include "theory_datatype_lazy.h"
#include "smtlib_exception.h"


using namespace std;
using namespace CVC3;


string Translator::fileToSMTLIBIdentifier(const string& filename)
{
  string tmpName;
  string::size_type pos = filename.rfind("/");
  if (pos == string::npos) {
    tmpName = filename;
  }
  else {
    tmpName = filename.substr(pos+1);
  }
  char c = tmpName[0];
  string res;
  if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z')) {
    res = "B_";
  }
  for (unsigned i = 0; i < tmpName.length(); ++i) {
    c = tmpName[i];
    if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') &&
        (c < '0' || c > '9') && (c != '.' && c != '_')) {
      c = '_';
    }
    res += c;
  }
  return res;
}


Expr Translator::preprocessRec(const Expr& e, ExprMap<Expr>& cache)
{
  ExprMap<Expr>::iterator i(cache.find(e));
  if(i != cache.end()) {
    return (*i).second;
  }

  if (e.isClosure()) {
    Expr newBody = preprocessRec(e.getBody(), cache);
    Expr e2(e);
    if (newBody != e.getBody()) {
      e2 = d_em->newClosureExpr(e.getKind(), e.getVars(), newBody);
    }
    d_theoryCore->theoryOf(e2)->setUsed();
    cache[e] = e2;
    return e2;
  }

  Expr e2(e);
  vector<Expr> children;
  bool diff = false;

  for(int k = 0; k < e.arity(); ++k) {
    // Recursively preprocess the kids
    Expr child = preprocessRec(e[k], cache);
    if (child != e[k]) diff = true;
    children.push_back(child);
  }
  if (diff) {
    e2 = Expr(e.getOp(), children);
  }

  Rational r;
  switch (e2.getKind()) {
    case APPLY:
      // Expand lambda applications
      if (e2.getOpKind() == LAMBDA) {
        Expr lambda(e2.getOpExpr());
        Expr body(lambda.getBody());
        const vector<Expr>& vars = lambda.getVars();
        FatalAssert(vars.size() == (size_t)e2.arity(), 
                    "wrong number of arguments applied to lambda\n");
        body = body.substExpr(vars, e2.getKids());
        e2 = preprocessRec(body, cache);
      }
      break;
    case EQ:
      if (d_theoryArith->getBaseType(e2[0]) != d_theoryArith->realType())
        break;
      goto arith_rewrites;

    case UMINUS:
      if (d_theoryArith->isSyntacticRational(e2, r)) {
        e2 = preprocessRec(d_em->newRatExpr(r), cache);
        break;
      }
      goto arith_rewrites;

    case DIVIDE:
      if (d_theoryArith->isSyntacticRational(e2, r)) {
        e2 = preprocessRec(d_em->newRatExpr(r), cache);
        break;
      }
      else if (d_convertArith && e2[1].isRational()) {
        r = e2[1].getRational();
        if (r != 0) {
          e2 = d_em->newRatExpr(1/r) * e2[0];
          e2 = preprocessRec(e2, cache);
          break;
        }
      }
      goto arith_rewrites;

    case PLUS:
      if (d_convertArith) {
        // Flatten and combine constants
        vector<Expr> terms;
        bool changed = false;
        r = 0;
        Expr::iterator i = e2.begin(), iend = e2.end();
        for(; i!=iend; ++i) {
          if ((*i).getKind() == PLUS) {
            changed = true;
            Expr::iterator i2 = (*i).begin(), i2end = (*i).end();
            for (; i2 != i2end; ++i2) {
              if ((*i2).isRational()) r += (*i2).getRational();
              else terms.push_back(*i2);
            }
          }
          else {
            if ((*i).isRational()) {
              if (r != 0) changed = true;
              r += (*i).getRational();
            }
            else terms.push_back(*i);
          }
        }
        if (terms.size() == 0) {
          e2 = preprocessRec(d_em->newRatExpr(r), cache);
          break;
        }
        else if (terms.size() == 1) {
          if (r == 0) {
            e2 = terms[0];
            break;
          }
          else if (r < 0) {
            e2 = preprocessRec(Expr(MINUS, terms[0], d_em->newRatExpr(-r)), cache);
            break;
          }
        }
        if (changed) {
          if (r != 0) terms.push_back(d_em->newRatExpr(r));
          e2 = preprocessRec(Expr(PLUS, terms), cache);
          break;
        }
      }
      goto arith_rewrites;

    case POW:
      if (d_convertArith && e2[0].isRational()) {
        r = e2[0].getRational();
        if (r.isInteger() && r.getNumerator() == 2) {
          e2 = preprocessRec(e2[1] * e2[1], cache);
          break;
        }
      }
      // fall through

    case LT:
    case GT:
    case LE:
    case GE:
    case MINUS:
    case MULT:
    case INTDIV:
    case MOD:

  arith_rewrites:
      if (d_iteLiftArith) {
        diff = false;
        children.clear();
        vector<Expr> children2;
        Expr cond;
        for (int k = 0; k < e2.arity(); ++k) {
          if (e2[k].getKind() == ITE && !diff) {
            diff = true;
            cond = e2[k][0];
            children.push_back(e2[k][1]);
            children2.push_back(e2[k][2]);
          }
          else {
            children.push_back(e2[k]);
            children2.push_back(e2[k]);
          }
        }
        if (diff) {
          Expr thenpart = Expr(e2.getOp(), children);
          Expr elsepart = Expr(e2.getOp(), children2);
          e2 = cond.iteExpr(thenpart, elsepart);
          e2 = preprocessRec(e2, cache);
          break;
        }
      }

      if (d_convertToDiff != "" && d_theoryArith->isAtomicArithFormula(e2)) {
        Expr e3 = d_theoryArith->rewriteToDiff(e2);
        if (e2 != e3) {
          DebugAssert(e3 == d_theoryArith->rewriteToDiff(e3), "Expected idempotent rewrite");
          e2 = preprocessRec(e3, cache);
        }
        break;
      }

      break;
    default:
      break;
  }

  // Figure out language
  switch (e2.getKind()) {
    case RATIONAL_EXPR:
      r = e2.getRational();
      if (r.isInteger()) {
        d_intConstUsed = true;
      }
      else {
        d_realUsed = true;
      }
      if (d_langUsed == NOT_USED) d_langUsed = TERMS_ONLY;
      break;
    case IS_INTEGER:
      d_unknown = true;
      break;
    case PLUS: {
      if (e2.arity() == 2) {
        int nonconst = 0, isconst = 0;
        Expr::iterator i = e2.begin(), iend = e2.end();
        for(; i!=iend; ++i) {
          if ((*i).isRational()) {
            if ((*i).getRational() <= 0) {
              d_UFIDL_ok = false;
              break;
            }
            else ++isconst;
          }
          else ++nonconst;
        }
        if (nonconst > 1 || isconst > 1)
          d_UFIDL_ok = false;
      }
      else d_UFIDL_ok = false;
      if (d_langUsed == NOT_USED) d_langUsed = TERMS_ONLY;
      break;
    }
    case MINUS:
      if (!e2[1].isRational() || e2[1].getRational() <= 0) {
        d_UFIDL_ok = false;
      }
      if (d_langUsed == NOT_USED) d_langUsed = TERMS_ONLY;
      break;
    case UMINUS:
      d_UFIDL_ok = false;
      if (d_langUsed == NOT_USED) d_langUsed = TERMS_ONLY;
      break;
    case MULT: {
      d_UFIDL_ok = false;
      if (d_langUsed == NONLINEAR) break;
      d_langUsed = LINEAR;
      bool nonconst = false;
      for (int i=0; i!=e2.arity(); ++i) {
        if (e2[i].isRational()) continue;
        if (nonconst) {
          d_langUsed = NONLINEAR;
          break;
        }
        nonconst = true;
      }
      break;
    }
    case POW:
    case DIVIDE:
      d_unknown = true;
      break;

    case EQ:
      if (d_theoryArith->getBaseType(e2[0]) != d_theoryArith->realType())
        break;
    case LT:
    case GT:
    case LE:
    case GE:
      if (d_langUsed >= LINEAR) break;
      if (!d_theoryArith->isAtomicArithFormula(e2)) {
        d_langUsed = LINEAR;
        break;
      }
      if (e2[0].getKind() == MINUS &&
          d_theoryArith->isLeaf(e2[0][0]) &&
          d_theoryArith->isLeaf(e2[0][1]) &&
          e2[1].isRational()) {
        d_langUsed = DIFF_ONLY;
        break;
      }
      if (d_theoryArith->isLeaf(e2[0]) &&
          d_theoryArith->isLeaf(e2[1])) {
        d_langUsed = DIFF_ONLY;
        break;
      }
      d_langUsed = LINEAR;
      break;
    default:
      break;
  }

  d_theoryCore->theoryOf(e2)->setUsed();
  
  cache[e] = e2;
  return e2;
}


Expr Translator::preprocess(const Expr& e, ExprMap<Expr>& cache)
{
  Expr result;
  try {
    result = preprocessRec(e, cache);
  } catch (SmtlibException& ex) {
    cerr << "Error while processing the formula:\n"
         << e.toString() << endl;
    throw ex;
  }
  return result;
}


Expr Translator::preprocess2Rec(const Expr& e, ExprMap<Expr>& cache, Type desiredType)
{
  ExprMap<Expr>::iterator i(cache.find(e));
  if(i != cache.end()) {
    if ((desiredType.isNull() &&
         (*i).second.getType() != d_theoryArith->realType()) ||
        (*i).second.getType() == desiredType) {
      return (*i).second;
    }
  }

  if (e.isClosure()) {
    Expr newBody = preprocess2Rec(e.getBody(), cache, Type());
    Expr e2(e);
    if (newBody != e.getBody()) {
      e2 = d_em->newClosureExpr(e.getKind(), e.getVars(), newBody);
    }
    cache[e] = e2;
    return e2;
  }

  bool forceReal = false;
  if (desiredType == d_theoryArith->intType() &&
      e.getType() != d_theoryArith->intType()) {
    d_unknown = true;
//     throw SmtlibException("Unable to separate int and real "+
//                           e.getType().getExpr().toString()+
//                           "\n\nwhile processing the term:\n"+
//                           e.toString(PRESENTATION_LANG));
  }

  // Try to force type-compliance
  switch (e.getKind()) {
    case EQ:
    case LT:
    case GT:
    case LE:
    case GE:
      if (e[0].getType() != e[1].getType()) {
        if (e[0].getType() != d_theoryArith->intType() &&
            e[1].getType() != d_theoryArith->intType()) {
          d_unknown = true;
          throw SmtlibException("Expected each side to be REAL or INT, but we got lhs: "+
                                e[0].getType().getExpr().toString()+" and rhs: "+
                                e[1].getType().getExpr().toString()+
                                "\n\nwhile processing the term:\n"+
                                e.toString());
        }
        forceReal = true;
        break;
      case ITE:
      case UMINUS:
      case PLUS:
      case MINUS:
      case MULT:
        if (desiredType == d_theoryArith->realType())
          forceReal = true;
        break;
      case DIVIDE:
        forceReal = true;
        break;
      default:
        break;
    }
  }

  Expr e2(e);
  vector<Expr> children;
  bool diff = false;

  Type funType;
  if (e.isApply()) {
    funType = e.getOpExpr().getType();
    if (funType.getExpr().getKind() == ANY_TYPE) funType = Type();
  }

  for(int k = 0; k < e.arity(); ++k) {
    Type t;
    if (forceReal && e[k].getType() == d_theoryArith->intType())
      t = d_theoryArith->realType();
    else if (!funType.isNull()) t = funType[k];
    // Recursively preprocess the kids
    Expr child = preprocess2Rec(e[k], cache, t);
    if (child != e[k]) diff = true;
    children.push_back(child);
  }
  if (diff) {
    e2 = Expr(e.getOp(), children);
  }
  else if (e2.getKind() == RATIONAL_EXPR) {
    if (e2.getType() == d_theoryArith->realType() ||
        (e2.getType() == d_theoryArith->intType() &&
         desiredType == d_theoryArith->realType()))
      e2 = Expr(REAL_CONST, e2);
  }
  if (!desiredType.isNull() && e2.getType() != desiredType) {
    d_unknown = true;
    throw SmtlibException("Type error: expected "+
                          desiredType.getExpr().toString()+
                          " but instead got "+
                          e2.getType().getExpr().toString()+
                          "\n\nwhile processing term:\n"+
                          e.toString());
  }

  cache[e] = e2;
  return e2;
}


Expr Translator::preprocess2(const Expr& e, ExprMap<Expr>& cache)
{
  Expr result;
  try {
    result = preprocess2Rec(e, cache, Type());
  } catch (SmtlibException& ex) {
    cerr << "Error while processing the formula:\n"
         << e.toString() << endl;
    throw ex;
  }
  return result;
}




bool Translator::containsArray(const Expr& e)
{
  if (e.getKind() == ARRAY) return true;
  Expr::iterator i = e.begin(), iend=e.end();
  for(; i!=iend; ++i) if (containsArray(*i)) return true;
  return false;
}


Translator::Translator(ExprManager* em,
                       const bool& translate,
                       const bool& real2int,
                       const bool& convertArith,
                       const string& convertToDiff,
                       bool iteLiftArith,
                       const string& expResult,
                       const string& category,
                       bool convertArray,
                       bool combineAssump)
  : d_em(em), d_translate(translate),
    d_real2int(real2int),
    d_convertArith(convertArith),
    d_convertToDiff(convertToDiff),
    d_iteLiftArith(iteLiftArith),
    d_expResult(expResult),
    d_category(category),
    d_convertArray(convertArray),
    d_combineAssump(combineAssump),
    d_dump(false), d_dumpFileOpen(false),
    d_intIntArray(false), d_intRealArray(false), d_intIntRealArray(false),
    d_ax(false), d_unknown(false),
    d_realUsed(false), d_intUsed(false), d_intConstUsed(false),
    d_langUsed(NOT_USED), d_UFIDL_ok(true), d_arithUsed(false),
    d_zeroVar(NULL)
{}


bool Translator::start(const string& dumpFile)
{
  if (d_translate && d_em->getOutputLang() == SMTLIB_LANG) {
    d_dump = true;
    if (dumpFile == "") {
      d_osdump = &cout;
    }
    else {
      d_osdumpFile.open(dumpFile.c_str());
      if(!d_osdumpFile)
        throw Exception("cannot open a log file: "+dumpFile);
      else {
        d_dumpFileOpen = true;
        d_osdump = &d_osdumpFile;
      }
    }
    *d_osdump <<
      "(benchmark " << fileToSMTLIBIdentifier(dumpFile) << endl <<
      "  :source {" << endl;
    string tmpName;
    string::size_type pos = dumpFile.rfind("/");
    if (pos == string::npos) {
      tmpName = "README";
    }
    else {
      tmpName = dumpFile.substr(0,pos+1) + "README";
    }
    d_tmpFile.open(tmpName.c_str());
    char c;
    if (d_tmpFile.is_open()) {
      d_tmpFile.get(c);
      while (!d_tmpFile.eof()) {
        if (c == '{' || c == '}') *d_osdump << '\\';
        *d_osdump << c;
        d_tmpFile.get(c);
      }
      d_tmpFile.close();
    }
    else {
      *d_osdump << "Source unknown";
    }
    *d_osdump << endl;// <<
    //        "This benchmark was automatically translated into SMT-LIB format from" << endl <<
    //        "CVC format using CVC3" << endl;
    *d_osdump << "}" << endl;

  }
  else {
    if (dumpFile == "") {
      if (d_translate) {
        d_osdump = &cout;
        d_dump = true;
      }
    }
    else {
      d_osdumpFile.open(dumpFile.c_str());
      if(!d_osdumpFile)
        throw Exception("cannot open a log file: "+dumpFile);
      else {
        d_dump = true;
        d_dumpFileOpen = true;
        d_osdump = &d_osdumpFile;
      }
    }
  }
  return d_dump;
}


void Translator::dump(const Expr& e, bool dumpOnly)
{
  DebugAssert(d_dump, "dump called unexpectedly");
  if (!dumpOnly || !d_translate) {
    if (d_convertArray && e.getKind() == CONST &&
        e.arity() == 2) {
      if (e[1].getKind() == ARRAY) {
        if (containsArray(e[1][0]) ||
            containsArray(e[1][1])) {
          throw Exception("convertArray failed because of nested arrays in"+
                          e[1].toString());
        }
        Expr funType = Expr(ARROW, e[1][0], e[1][1]);
        Expr outExpr = Expr(CONST, e[0], funType);
        if (d_translate && d_em->getOutputLang() == SMTLIB_LANG) {
          d_dumpExprs.push_back(outExpr);
        }
        else {
          *d_osdump << outExpr << endl;
        }
      }
      else if (containsArray(e[1])) {
        throw Exception("convertArray failed becauase of use of arrays in"+
                        e[1].toString());
      }
      else if (d_translate && d_em->getOutputLang() == SMTLIB_LANG) {
        d_dumpExprs.push_back(e);
      }
      else *d_osdump << e << endl;
    }
    else if (d_translate && d_em->getOutputLang() == SMTLIB_LANG) {
      d_dumpExprs.push_back(e);
    }
    else *d_osdump << e << endl;
  }
}


bool Translator::dumpAssertion(const Expr& e)
{
  Expr outExpr = Expr(ASSERT, e);
  if (d_translate && d_em->getOutputLang() == SMTLIB_LANG) {
    d_dumpExprs.push_back(outExpr);
  }
  else {
    *d_osdump << outExpr << endl;
  }
  return d_translate;
}


bool Translator::dumpQuery(const Expr& e)
{
  Expr outExpr = Expr(QUERY, e);
  if (d_translate && d_em->getOutputLang() == SMTLIB_LANG) {
    d_dumpExprs.push_back(outExpr);
  }
  else {
    *d_osdump << outExpr << endl;
  }
  return d_translate;
}


void Translator::dumpQueryResult(QueryResult qres)
{
  if(d_translate && d_em->getOutputLang() == SMTLIB_LANG) {
    *d_osdump << "  :status ";
    switch (qres) {
      case UNSATISFIABLE:
        *d_osdump << "unsat" << endl;
        break;
      case SATISFIABLE:
        *d_osdump << "sat" << endl;
        break;
      default:
        *d_osdump << "unknown" << endl;
        break;
    }
  }
}


Expr Translator::processType(const Expr& e)
{
  switch (e.getKind()) {
    case INT:
      d_intUsed = true;
      break;
    case REAL:
      if (d_real2int) {
        d_intUsed = true;
        return d_theoryArith->intType().getExpr();
      }
      else {
        d_realUsed = true;
      }
      break;
    case SUBRANGE:
      d_unknown = true;
      break;
    default:
      break;
  }
  d_theoryCore->theoryOf(e)->setUsed();
  if (e.arity() == 0) return e;
  bool changed = false;
  vector<Expr> vec;
  for (int i = 0; i < e.arity(); ++i) {
    vec.push_back(processType(e[i]));
    if (vec.back() != e[i]) changed = true;
  }
  if (changed)
    return Expr(e.getOp(), vec);
  return e;
}


void Translator::finish()
{
  if (d_translate && d_em->getOutputLang() == SMTLIB_LANG) {

    // Print the rest of the preamble for smt-lib benchmarks

    // Get status from expResult flag
    *d_osdump << "  :status ";
    if (d_expResult == "invalid" ||
        d_expResult == "satisfiable")
      *d_osdump << "sat";
    else if (d_expResult == "valid" ||
             d_expResult == "unsatisfiable")
      *d_osdump << "unsat";
    else *d_osdump << "unknown";
    *d_osdump << endl;

    // difficulty
    *d_osdump << "  :difficulty { unknown }" << endl;

    // category
    *d_osdump << "  :category { ";
    *d_osdump << d_category << " }" << endl;

    // Compute logic for smt-lib
    bool qf_uf = false;
    {
      {
        ExprMap<Expr> cache;
        // Step 1: preprocess asserts, queries, and types
        vector<Expr>::iterator i = d_dumpExprs.begin(), iend = d_dumpExprs.end();
        for (; i != iend; ++i) {
          Expr e = *i;
          switch (e.getKind()) {
            case ASSERT: {
              Expr e2 = preprocess(e[0], cache);
              if (e[0] == e2) break;
              e2.getType();
              *i = Expr(ASSERT, e2);
              break;
            }
            case QUERY: {
              Expr e2 = preprocess(e[0].negate(), cache);
              if (e[0].negate() == e2) break;
              e2.getType();
              *i = Expr(QUERY, e2.negate());
              break;
            }
            case CONST: {
              DebugAssert(e.arity() == 2, "Expected CONST with arity 2");
              Expr e2 = processType(e[1]);
              if (e[1] == e2) break;
              *i = Expr(CONST, e[0], e2);
              break;
            }
            default:
              break;
          }
        }
      }

      if (d_zeroVar) {
        d_dumpExprs.insert(d_dumpExprs.begin(),
                           Expr(CONST, Expr(ID, d_em->newStringExpr("cvc3Zero")),
                                processType(d_zeroVar->getType().getExpr())));
      }

      // Step 2: If both int and real are used, try to separate them
      if (!d_unknown && d_intUsed && d_realUsed) {
        ExprMap<Expr> cache;
        vector<Expr>::iterator i = d_dumpExprs.begin(), iend = d_dumpExprs.end();
        for (; i != iend; ++i) {
          Expr e = *i;
          switch (e.getKind()) {
            case ASSERT: {
              Expr e2 = preprocess2(e[0], cache);
              e2.getType();
              *i = Expr(ASSERT, e2);
              break;
            }
            case QUERY: {
              Expr e2 = preprocess2(e[0].negate(), cache);
              e2.getType();
              *i = Expr(QUERY, e2.negate());
              break;
            }
            default:
              break;
          }
        }
      }
      d_arithUsed = d_realUsed || d_intUsed || d_intConstUsed || (d_langUsed != NOT_USED);

      // Step 3: Go through the cases and figure out the right logic
      *d_osdump << "  :logic ";
      if (d_unknown ||
          d_theoryRecords->theoryUsed() ||
          d_theorySimulate->theoryUsed() ||
          d_theoryDatatype->theoryUsed()) {
        *d_osdump << "unknown";
      }
      else {
        if ((d_ax && (d_arithUsed ||
                      d_theoryBitvector->theoryUsed() ||
                      d_theoryUF->theoryUsed())) ||
            (d_intIntArray && d_realUsed) ||
            (d_arithUsed && d_theoryBitvector->theoryUsed())) {
          *d_osdump << "unknown";
        }
        else {
          bool QF = false;
          bool A = false;
          bool UF = false;

          if (!d_theoryQuant->theoryUsed()) {
            QF = true;
            *d_osdump << "QF_";
          }

          if (d_theoryArray->theoryUsed() && !d_convertArray) {
            A = true;
            *d_osdump << "A";
          }

          // Promote undefined subset logics
          else if (!QF && !d_theoryBitvector->theoryUsed()) {
            A = true;
            *d_osdump << "A";
          }

          if (d_theoryUF->theoryUsed() ||
              (d_theoryArray->theoryUsed() && d_convertArray)) {
            UF = true;
            *d_osdump << "UF";
          }

          // Promote undefined subset logics
          else if (!QF &&
                   !d_theoryBitvector->theoryUsed()) {
            UF = true;
            *d_osdump << "UF";
          }

          if (d_arithUsed) {
            if (d_intUsed && d_realUsed) {
              if (d_langUsed < NONLINEAR) {
                *d_osdump << "LIRA";
              }
              else *d_osdump << "NIRA";
            }
            else if (d_realUsed) {
              if (d_langUsed <= DIFF_ONLY) {

                // Promote undefined subset logics
                if (!QF) {
                  *d_osdump << "LIRA";
                } else

                  *d_osdump << "RDL";
              }
              else if (d_langUsed <= LINEAR) {

                // Promote undefined subset logics
                if (!QF) {
                  *d_osdump << "LIRA";
                } else

                  *d_osdump << "LRA";
              }
              else {

                // Promote undefined subset logics
                if (!QF) {
                  *d_osdump << "NIRA";
                } else

                  *d_osdump << "NRA";
              }
            }
            else {
              if (QF && !A && UF && d_langUsed <= LINEAR) {
                if (!d_realUsed && d_langUsed <= LINEAR && d_UFIDL_ok) {
                  *d_osdump << "IDL";
                }
                else {
                  *d_osdump << "LIA";
                }
              }
              else if (d_langUsed <= DIFF_ONLY) {

                // Promote undefined subset logics
                if (!QF) {
                  *d_osdump << "LIA";
                }
                else if (A) {
                  if (!UF) {
                    UF = true;
                    *d_osdump << "UF";
                  }
                  *d_osdump << "LIA";                  
                } else

                  *d_osdump << "IDL";
              }
              else if (d_langUsed <= LINEAR) {

                // Promote undefined subset logics
                if (QF && A && !UF) {
                  UF = true;
                  *d_osdump << "UF";
                }

                if (QF && !A && (!d_realUsed && d_langUsed <= LINEAR && d_UFIDL_ok)) {
                  *d_osdump << "UFIDL";
                }
                else {
                  *d_osdump << "LIA";
                }
              }
              else {

                // Promote undefined subset logics
                if (!QF) {
                  *d_osdump << "NIRA";
                } else
                
                  *d_osdump << "NIA";
              }
            }
          }
          else if (d_theoryBitvector->theoryUsed()) {

            // Promote undefined subset logics
            if (A && QF && !UF) {
              UF = true;
              *d_osdump << "UF";
            }

            *d_osdump << "BV";//[" << d_theoryBitvector->getMaxSize() << "]";
          }
          else {

            if (d_ax) {
              *d_osdump << "X";
            }

            // Promote undefined subset logics
            else if (!QF || (A && UF)) {
              *d_osdump << "LIA";
            } else {
            
              // Default logic
              if (!A && !UF) {
                UF = true;
                *d_osdump << "UF";
              }
              qf_uf = QF && UF && (!A);
            }
          }
        }
      }
    }
    *d_osdump << endl;

    // Dump the actual expressions

    vector<Expr>::const_iterator i = d_dumpExprs.begin(), iend = d_dumpExprs.end();
    vector<Expr> assumps;
    for (; i != iend; ++i) {
      Expr e = *i;
      switch (e.getKind()) {
        case ASSERT: {
          if (d_combineAssump) {
            assumps.push_back(e[0]);
          }
          else {
            *d_osdump << "  :assumption" << endl;
            *d_osdump << e[0] << endl;
          }
          break;
        }
        case QUERY: {
          *d_osdump << "  :formula" << endl;
          if (!assumps.empty()) {
            assumps.push_back(e[0].negate());
            e = Expr(AND, assumps);
            *d_osdump << e << endl;
          }
          else {
            *d_osdump << e[0].negate() << endl;
          }
          break;
        }
        default:
          if (qf_uf && e.getKind() == TYPE && e[0].getKind() == RAW_LIST &&
              e[0][0].getKind() == ID && e[0][0][0].getKind() == STRING_EXPR &&
              e[0][0][0].getString() == "U")
            break;
          *d_osdump << e << endl;
          break;
      }
    }
    *d_osdump << ")" << endl;
  }

  if (d_dumpFileOpen) d_osdumpFile.close();
  if (d_zeroVar) delete d_zeroVar;
}


const string Translator::fixConstName(const string& s)
{
  if (d_em->getOutputLang() == SMTLIB_LANG) {
    if (s[0] == '_') {
      return "smt"+s;
    }
  }
  return s;
}


bool Translator::printArrayExpr(ExprStream& os, const Expr& e)
{
  if (e.getKind() == ARRAY) {
    if (d_convertArray) {
      os << Expr(ARROW, e[0], e[1]);
      return true;
    }
    if (d_em->getOutputLang() != SMTLIB_LANG) return false;
    if (e[0].getKind() == TYPEDECL) {
      DebugAssert(e[0].arity() == 1, "expected arity 1");
      if (e[0][0].getString() == "Index") {
        if (e[1].getKind() == TYPEDECL) {
          DebugAssert(e[1].arity() == 1, "expected arity 1");
          if (e[1][0].getString() == "Element") {
            d_ax = true;
            os << "Array";
            return true;
          }
        }
      }
    }
    else if (isInt(Type(e[0]))) {
      if (isInt(Type(e[1]))) {
        d_intIntArray = true;
        os << "Array";
        return true;
      }
      else if (isReal(Type(e[1]))) {
        d_intRealArray = true;
        os << "Array1";
        return true;
      }
      else if (isArray(Type(e[1])) &&
               isInt(Type(e[1][0])) &&
               isReal(Type(e[1][1]))) {
        d_intIntRealArray = true;
        os << "Array2";
        return true;
      }
    }
    else if (e[0].getKind() == BITVECTOR &&
             e[1].getKind() == BITVECTOR) {
      os << "Array[";
      os << d_theoryBitvector->getBitvectorTypeParam(Type(e[0]));
      os << ":";
      os << d_theoryBitvector->getBitvectorTypeParam(Type(e[1]));
      os << "]";
      return true;
    }
    os << "Array";
    d_unknown = true;
    return true;
  }

  switch (e.getKind()) {
    case READ:
      if (d_convertArray) {
        if (e[0].getKind() == UCONST) {
          os << Expr(d_em->newSymbolExpr(e[0].getName(), UFUNC).mkOp(), e[1]);
          return true;
        }
        else if (e[0].getKind() == WRITE) {
          throw Exception("READ of WRITE not implemented yet for convertArray");
        }
        else {
          throw Exception("Unexpected case for convertArray");
        }
      }
      break;
    case WRITE:
      if (d_convertArray) {
        throw Exception("WRITE expression encountered while attempting "
                        "to convert arrays to uninterpreted functions");
      }
      break;
    case ARRAY_LITERAL:
      if (d_convertArray) {
        throw Exception("ARRAY_LITERAL expression encountered while attempting"
                        " to convert arrays to uninterpreted functions");
      }
      break;
    default:
      throw Exception("Unexpected case in printArrayExpr");
      break;
  }
  return false;
}


Expr Translator::zeroVar()
{
  if (!d_zeroVar) {
    d_zeroVar = new Expr();
    if (d_convertToDiff == "int") {
      *d_zeroVar = d_theoryCore->newVar("cvc3Zero", d_theoryArith->intType().getExpr());
    }
    else if (d_convertToDiff == "real") {
      *d_zeroVar = d_theoryCore->newVar("cvc3Zero", d_theoryArith->realType().getExpr());
    }
  }
  return *d_zeroVar;
}


syntax highlighted by Code2HTML, v. 0.9.1