/* $Id: nl2e.cpp 603 2011-06-03 10:48:19Z pbelotti $
 *
 * Name:    nl2e.cpp
 * Author:  Pietro Belotti
 * Purpose: converts a nl expression into a Couenne expression
 *
 * (C) Carnegie-Mellon University, 2006-09.
 * This file is licensed under the Eclipse Public License (EPL)
 */

#include "CouenneTypes.hpp"

#include "CouenneExprVar.hpp"
#include "CouenneExprAbs.hpp"
#include "CouenneExprSum.hpp"
#include "CouenneExprSub.hpp"
#include "CouenneExprMul.hpp"
#include "CouenneExprDiv.hpp"
#include "CouenneExprInv.hpp"
#include "CouenneExprSin.hpp"
#include "CouenneExprPow.hpp"
#include "CouenneExprClone.hpp"
#include "CouenneExprLog.hpp"
#include "CouenneExprOpp.hpp"
#include "CouenneExprCos.hpp"
#include "CouenneExprExp.hpp"

#include "asl.h"
#include "nlp.h"
#include "opcode.hd"

using namespace Couenne;

// get ASL op. code relative to function pointer passed as parameter 
size_t getOperator (efunc *);


// warning for non-implemented functions -- return 0 constant expression
//expression *notimpl (const std::string &fname) {
void notimpl (const std::string &fname) {
  std::cerr << "*** Error: " << fname << " not implemented" << std::endl;
  exit (-1);
}


// converts an AMPL expression (sub)tree into an expression* (sub)tree
expression *CouenneProblem::nl2e (expr *e, const ASL *asl) {

  switch (getOperator (e -> op)) {

  case OPPLUS:  return new exprSum (nl2e (e -> L.e, asl), nl2e (e -> R.e, asl));
  case OPMINUS: return new exprSub (nl2e (e -> L.e, asl), nl2e (e -> R.e, asl));
  case OPMULT:  return new exprMul (nl2e (e -> L.e, asl), nl2e (e -> R.e, asl));
  case OPDIV:   return new exprDiv (nl2e (e -> L.e, asl), nl2e (e -> R.e, asl));
    //case OPREM:   notimpl ("remainder");
  case OPPOW:   return new exprPow (nl2e (e -> L.e, asl), nl2e (e -> R.e, asl));
    //case OPLESS:  notimpl ("less");
    //case MINLIST: notimpl ("min");
    //case MAXLIST: notimpl ("max");
    //case FLOOR:   notimpl ("floor");
    //case CEIL:    notimpl ("ceil");
  case ABS:     return new exprAbs (nl2e (e -> L.e, asl));
  case OPUMINUS:return new exprOpp (nl2e (e -> L.e, asl));
    //          return new exprOpp (nl2e (e -> L.e -> L.e, asl));
    //case OPIFnl:  { notimpl ("ifnl");

    // see ASL/solvers/rops.c, IfNL
    //}

  case OP_tanh: return new exprDiv 
      (new exprSub (new exprExp (nl2e (e -> L.e, asl)),
		    new exprExp (new exprOpp (nl2e (e->L.e, asl)))),
       new exprSum (new exprExp (nl2e (e -> L.e, asl)),
		    new exprExp (new exprOpp (nl2e (e->L.e, asl)))));

  case OP_tan: {
    expression *arg;
    arg = nl2e (e -> L.e, asl);
    return new exprDiv (new exprSin (arg), new exprCos (new exprClone (arg)));
  }
  case OP_sqrt:    return new exprPow (nl2e (e -> L.e, asl), new exprConst (0.5));
  case OP_sinh:    return new exprMul (new exprConst (0.5),
				       new exprSub (new exprExp (nl2e (e -> L.e, asl)),
						    new exprExp (new exprOpp (nl2e (e->L.e, asl)))));
  case OP_sin:     return new exprSin (nl2e (e -> L.e, asl));
  case OP_log10:   return new exprMul (new exprConst (1.0 / log (10.0)), 
				       new exprLog (nl2e (e -> L.e, asl)));
  case OP_log:     return new exprLog (nl2e (e -> L.e, asl));
  case OP_exp:     return new exprExp (nl2e (e -> L.e, asl));
  case OP_cosh:    return new exprMul (new exprConst (0.5),
				       new exprSum (new exprExp (nl2e (e -> L.e, asl)),
						    new exprExp (new exprOpp (nl2e (e->L.e, asl)))));

  case OP_cos:   return new exprCos (nl2e (e -> L.e, asl));
    //case OP_atanh: notimpl ("atanh");
    //case OP_atan2: notimpl ("atan2");
    //case OP_atan:  notimpl ("atan");
    //case OP_asinh: notimpl ("asinh");
    //case OP_asin:  notimpl ("asin");
    //case OP_acosh: notimpl ("acosh");
    //case OP_acos:  notimpl ("acos");

  case OPSUMLIST: {
    int i=0;
    expression **al = new expression * [(e->R.ep - e->L.ep)];
    for (expr **ep = e->L.ep; ep < e->R.ep; ep++)
      al [i++] = nl2e (*ep, asl);
    return new exprSum (al, i);
  }
    //case OPintDIV: notimpl ("intdiv");
    //case OPprecision: notimpl ("precision");
    //case OPround:  notimpl ("round");
    //case OPtrunc:  notimpl ("trunc");

  case OP1POW: return new exprPow (nl2e (e -> L.e, asl), 
				   new exprConst (((expr_n *)e->R.e)->v));
  case OP2POW: return new exprPow (nl2e (e -> L.e, asl), 
				   new exprConst (2.));
  case OPCPOW: return new exprPow (new exprConst (((expr_n *)e->L.e)->v),
				   nl2e (e -> R.e, asl));
    //case OPFUNCALL: notimpl ("function call");
  case OPNUM:     return new exprConst (((expr_n *)e)->v);
    //case OPPLTERM:  notimpl ("plterm");
    //case OPIFSYM:   notimpl ("ifsym");
    //case OPHOL:     notimpl ("hol");
  case OPVARVAL:  {

    int j = ((expr_v *) e) -> a;

    if (j >= nOrigVars_) // common expression
      // use base pointer otherwise the .a field returns an awkward, out-of-bound index
      // TODO: fix! In itointqor.nl should return v51=y44 but returns v52=y44
      //                                          v??=y39 but returns v79=y39
      j = ((expr_v *) e) - ((const ASL_fg *) asl) -> I.var_e_; 

    if (j >= nOrigVars_ + ndefined_) {
      printf ("error: unknown variable x_%d\n", j);
      //return new exprClone (variables_ [0]);
      exit (-1);
    }

    return new exprClone (variables_ [j]);
  }

  default: 
    printf ("ERROR: unknown operator (address %p), aborting.\n", Intcast (e -> op));
    exit (-1);
    //return new exprConst (0);
  }

  return new exprConst (0.);
}
