/* $Id: elementBreak.cpp 490 2011-01-14 16:07:12Z pbelotti $ */
/*
 * Name:    elementBreak.cpp
 * Author:  Pietro Belotti
 * Purpose: decompose element of sum if it is of the form cx or x
 *
 * (C) Carnegie-Mellon University, 2007. 
 * This file is licensed under the Eclipse Public License (EPL)
 */

#include "CouenneExpression.hpp"

using namespace Couenne;

/// given an element of a sum, check if it is a variable (possibly
/// with a coefficient) and return its index (and the coefficient) if
/// it has not been spotted as an auxiliary (check wentAux)

void elementBreak (expression *arg, int &index, CouNumber &coeff) {

  CouNumber oppMulCoe = 1.;
  bool isMul = false;
  index = -1;

  if (arg -> Linearity () <= LINEAR) { 

    // check if it's w, c*w, or w/c. If more complicated (sum or
    // subtraction) nevermind

    switch (arg -> code ()) { // check element of sum

    case COU_EXPRCONST: // it is a constant, nevermind
      break; 

    case COU_EXPRVAR: // it is a simple variable, w
      index = arg -> Index ();
      coeff = 1.;
      break;

    case COU_EXPROPP: { // it is the opposite of a simple variable or of
                        // another linear term

      index = arg -> Argument () -> Index (); 
      coeff = -1.;

      oppMulCoe = -1; // to be used below
      arg = arg -> Argument (); // transfer analysis to argument of exprOpp

      int argcode = arg -> code ();

      if      (argcode == COU_EXPRMUL) isMul = true;
      else if (argcode != COU_EXPRDIV) break;
    } 
      // no break. Bail out of switch only if this was a -w rather
      // than a -c*w or a -w/c

    case COU_EXPRMUL: 
      if (isMul) { 

      // it is c*w or w*c. Forget the case with more than two
      // non-constant arguments.

      expression **factors = arg -> ArgList ();

      int pos = 0;

      index = (*factors) -> Index ();

      if (index < 0) 
	index = factors [pos = 1] -> Index ();

      if ((index >= 0) && 
	  (fabs (coeff = oppMulCoe * factors [1 - pos] -> Value ()) < COUENNE_EPS))
	index = -1;

      break;
    } 
      // no break outside, there could be some residue from case
      // COU_EXPROPP

    case COU_EXPRDIV: { // if linear, it must be of the form w/c

      expression **factors = arg -> ArgList ();

      coeff = factors [1] -> Value ();
      index = (*factors) -> Index ();

      if (fabs (coeff) < COUENNE_EPS)
	index = -1;
      else coeff = 1. / coeff;

    } break;

    default: break;
    }
  }
}
