/* $Id: CouenneExprBMul.hpp 591 2011-05-31 17:03:03Z pbelotti $
 *
 * Name:    exprBMul.hpp
 * Author:  Pietro Belotti
 * Purpose: definition of operators to compute lower/upper bounds of multiplications
 *
 * (C) Carnegie-Mellon University, 2006. 
 * This file is licensed under the Eclipse Public License (EPL)
 */

#ifndef COUENNE_EXPRBMUL_H
#define COUENNE_EXPRBMUL_H

#include "CouenneExprOp.hpp"
#include "CouenneConfig.h"
#include "CoinHelperFunctions.hpp"
#include "CoinFinite.hpp"

namespace Couenne {

#define MUL_ZERO 1e-20
#define MUL_INF  sqrt (COIN_DBL_MAX)

/// product that avoids NaN's 
inline CouNumber safeProd (register CouNumber a, register CouNumber b) {

  if (a >  MUL_INF) return (b < -MUL_ZERO) ? -COIN_DBL_MAX : (b > MUL_ZERO) ?  COIN_DBL_MAX : 0.;
  if (a < -MUL_INF) return (b < -MUL_ZERO) ?  COIN_DBL_MAX : (b > MUL_ZERO) ? -COIN_DBL_MAX : 0.;

  if (b >  MUL_INF) return (a < -MUL_ZERO) ? -COIN_DBL_MAX : (a > MUL_ZERO) ?  COIN_DBL_MAX : 0.;
  if (b < -MUL_INF) return (a < -MUL_ZERO) ?  COIN_DBL_MAX : (a > MUL_ZERO) ? -COIN_DBL_MAX : 0.;

  return a*b;
}


/// class to compute lower bound of a product based on the bounds of
/// both factors

class exprLBMul: public exprOp {

 public:

  /// Constructors, destructor
  exprLBMul  (expression **al, int n): 
    exprOp (al, n) {} //< non-leaf expression, with argument list

  /// cloning method
  expression *clone (Domain *d = NULL) const
    {return new exprLBMul (clonearglist (d), nargs_);}

  /// function for the evaluation of the expression
  CouNumber operator () ();

  /// print position (PRE, INSIDE, POST)
  enum pos printPos () const
    {return PRE;}

  /// print operator
  std::string printOp () const
    {return "LB_Mul";}
};


/// compute sum

inline CouNumber exprLBMul::operator () () {

  register CouNumber n = (*(arglist_ [0])) ();
  register CouNumber N = (*(arglist_ [1])) ();
  register CouNumber d = (*(arglist_ [2])) ();
  register CouNumber D = (*(arglist_ [3])) ();

  if (d>=0)
    if   (n>=0) return safeProd (n,d);
    else        return safeProd (n,D);
  else // d <= 0
    if (N>0) {
      CouNumber Nd = safeProd (N,d), nD;
      if (n<0 && D>0 && 
	  (Nd > (nD = safeProd (n,D)))) return nD;
      else                              return Nd;
    }
    else 
      if (D>0) return safeProd (n,D);
      else     return safeProd (N,D);
}


/// class to compute upper bound of a product based on the bounds of
/// both factors

class exprUBMul: public exprOp {

 public:

  /// Constructors, destructor
  exprUBMul  (expression **al, int n): 
    exprOp (al, n) {} //< non-leaf expression, with argument list

  /// cloning method
  expression *clone (Domain *d = NULL) const
    {return new exprUBMul (clonearglist (d), nargs_);}

  /// function for the evaluation of the expression
  CouNumber operator () ();

  /// print position (PRE, INSIDE, POST)
  enum pos printPos () const
    {return PRE;}

  /// print operator
  std::string printOp () const
    {return "UB_Mul";}
};


/// compute sum

inline CouNumber exprUBMul::operator () () {

  //  exprOp:: operator () ();

  register CouNumber n = (*(arglist_ [0])) ();
  register CouNumber N = (*(arglist_ [1])) ();
  register CouNumber d = (*(arglist_ [2])) ();
  register CouNumber D = (*(arglist_ [3])) ();

  if (d>0)
    if (N<0) return safeProd (N,d);
    else     return safeProd (N,D);
  else // d <= 0
    if (n<0) {
      CouNumber nd = safeProd (n,d), ND;
      if (N>0 && D>0 && 
	  ((ND = safeProd (N,D)) > nd)) return ND;
      else                              return nd;
    }
    else 
      if (D>0) return safeProd (N,D);
      else     return safeProd (n,D);
}

}

#endif
