/* $Id: CouenneThreeWayBranchObj.cpp 694 2011-06-18 20:13:17Z stefan $
 *
 * Name:    CouenneThreeWayBranchObj.cpp
 * Authors: Pietro Belotti, Carnegie Mellon University
 * Purpose: Branching object for auxiliary variables
 *
 * (C) Carnegie-Mellon University, 2006-10.
 * This file is licensed under the Eclipse Public License (EPL)
 */

#include "CoinHelperFunctions.hpp"
#include "CouenneObject.hpp"
#include "CouenneBranchingObject.hpp"
#include "CouenneThreeWayBranchObj.hpp"

using namespace Ipopt;
using namespace Couenne;

/// Constructor. Get a variable as an argument and set value_ through
/// a call to operator () of that exprAux.
CouenneThreeWayBranchObj::CouenneThreeWayBranchObj (JnlstPtr jnlst,
						    expression *brVar, 
						    CouNumber lcrop, 
						    CouNumber rcrop,
						    int way
						    //bool isint
						    ): 
  brVar_   (brVar),
  lcrop_   (lcrop),
  rcrop_   (rcrop),
  jnlst_   (jnlst) {

  numberBranches_ = 3;

  // if none of these three, set to 0 and do code below
  firstBranch_ = (way == THREE_LEFT)   ? 0 : 
                 (way == THREE_CENTER) ? 1 : 
                 (way == THREE_RIGHT)  ? 2 : 0; 
  
  if (way == THREE_RAND) { // pick first branch randomly
    CouNumber rnd = 3. * CoinDrand48 ();
    firstBranch_ = (rnd < 1.) ? 0 : (rnd < 2.) ? 1: 2;
  }
}


/** \brief Execute the actions required to branch, as specified by the
	   current state of the branching object, and advance the
	   object's state.
	   Returns change in guessed objective on next branch
*/

double CouenneThreeWayBranchObj::branch (OsiSolverInterface * solver) {

  //       -1 if "<= a"  node
  // way =  0 if "[a,b]" node
  //        1 if ">= b"  node

  int way = 0;

  switch (branchIndex_) {
    // if first offspring, let firstBranch_ decide who's first
  case 0: way = firstBranch_;                break;
  case 1: way = (firstBranch_ == 0) ? 1 : 0; break;
  case 2: way = (firstBranch_ == 2) ? 1 : 2; break;
  default: jnlst_->Printf(J_WARNING, J_BRANCHING, 
			  "Warning, branchIndex_ has a strange value (%d)\n", branchIndex_);
  }

  // set lower or upper bound (round if this variable is integer)

  int  index   = brVar_ -> Index ();
  bool integer = brVar_ -> isInteger ();

  CouNumber
    l = solver -> getColLower () [index],
    u = solver -> getColUpper () [index];

  if (lcrop_ < l) lcrop_ = l;
  if (rcrop_ > u) rcrop_ = u;

  switch (--way) { // from {0,1,2} to {-1,0,1}

  case -1: solver -> setColUpper (index, integer ? floor (lcrop_) : lcrop_); break; // left
  case  0: solver -> setColLower (index, integer ? ceil  (lcrop_) : lcrop_);
           solver -> setColUpper (index, integer ? floor (rcrop_) : rcrop_); break; // central
  case  1: solver -> setColLower (index, integer ? ceil  (rcrop_) : rcrop_); break; // right
  default: jnlst_->Printf(J_WARNING, J_BRANCHING, "Couenne: branching on nonsense way %d\n", way);
  }

  // TODO: apply bound tightening 

  if (jnlst_->ProduceOutput(J_DETAILED, J_BRANCHING)) {  
    switch (way) {
    case -1: jnlst_->Printf(J_DETAILED, J_BRANCHING, 
			    "#3# Branch: x%d <= %g\n",               index, lcrop_); break; // left
    case  0: jnlst_->Printf(J_DETAILED, J_BRANCHING, 
			    "#3# Branch: %g <= x%d <= %g\n", lcrop_, index, rcrop_); break; // center
    case  1: jnlst_->Printf(J_DETAILED, J_BRANCHING, 
			    "#3# Branch: x%d >= %g\n",               index, rcrop_); break; // right
    default: jnlst_->Printf(J_DETAILED, J_BRANCHING, "Couenne: branching on nonsense way %d\n", way);
    }
  }

  branchIndex_++;

  return 0.; // estimated change in objective function
}
