/* $Id: linStandardize.cpp 268 2009-10-22 03:41:03Z pbelotti $
 *
 * Name:    linStandardize.cpp
 * Author:  Pietro Belotti
 * Purpose: standardize sum expressions (expr{Sum,Sub,Quad,Group,Opp})
 *
 * (C) Carnegie-Mellon University, 2007. 
 * This file is licensed under the Common Public License (CPL)
 */

#include "CouenneProblem.hpp"
#include "exprSum.hpp"
#include "exprSub.hpp"
#include "exprOpp.hpp"
#include "exprMul.hpp"
#include "exprPow.hpp"
#include "exprGroup.hpp"
#include "exprQuad.hpp"
#include "lqelems.hpp"

//#define DEBUG

/// standardization of linear exprOp's
exprAux *CouenneProblem::linStandardize (bool addAux, 
					 CouNumber c0, 
					 LinMap  &lmap,
					 QuadMap &qmap) {

  // check if quadratic forms are dense enough ///////////////////////////////////////////

  analyzeSparsity (c0, lmap, qmap);

  ////////////////////////////////////////////////////////////////////////////////////////

  int  nq = qmap.Map().size (),     /// data for exprQuad
      *qi = new int [nq+1], 
      *qj = new int [nq+1];

  CouNumber *qc = new CouNumber [nq];

  int  
     nl = lmap.Map().size(),      /// data for exprGroup
    *li = new int [nl+1];

  CouNumber *lc = new CouNumber [nl];

  // terminate arrays with negative index
  qi [nq] = li [nl] = -1; 

  std::map <int, CouNumber>::iterator lit = lmap.Map().begin (); 

  // fill in arrays for linear part
  for (int i=0; i<nl; i++, lit++) {

    li [i] = lit -> first;
    lc [i] = lit -> second;
  }

  std::map <std::pair <int, int>, CouNumber>::iterator qit = qmap.Map().begin (); 

  // fill in arrays for quadratic part
  for (int i=0; i < nq; i++, qit++) {
    qi [i] = qit -> first. first;
    qj [i] = qit -> first. second;
    qc [i] = qit -> second;
  }

  nl = lmap.Map().size ();
  nq = qmap.Map().size ();

  // particular cases ///////////////////////////////////////////////////////////

  expression *ret;

  // a constant
  if ((nq==0) && (nl==0)) 

    ret = new exprConst (c0); // a constant auxiliary? 

  else if ((nq==0) && (fabs (c0) < COUENNE_EPS) && (nl==1)) { // a linear monomial, cx

    if      (fabs (*lc - 1.) < COUENNE_EPS) ret = new exprClone (Var (*li));
    else if (fabs (*lc + 1.) < COUENNE_EPS) ret = new exprOpp (new exprClone (Var (*li)));
    else                                    ret = new exprMul (new exprConst (*lc), 
				 			       new exprClone (Var (*li)));

  } else if ((nl==0) && (fabs (c0) < COUENNE_EPS) && (nq==1)) { 

    // a bilinear/quadratic monomial, cx^2 or cxy

    expression *quad;

    if (*qi == *qj) quad = new exprPow (new exprClone (Var (*qi)), new exprConst (2.));
    else            quad = new exprMul (new exprClone (Var (*qi)), 
					new exprClone (Var (*qj)));

    if (fabs (*qc - 1) < COUENNE_EPS) 
      ret    = quad;
    else {
      quad = addAuxiliary (quad);
      ret  = new exprMul (new exprConst (*qc), new exprClone (quad));
    }

  } else {

    // general case ///////////////////////////////////////////////////////////////

    std::vector <std::pair <exprVar *, CouNumber> > lcoeff;
    indcoe2vector (li, lc, lcoeff);

    std::vector <quadElem> qcoeff;
    indcoe2vector (qi, qj, qc, qcoeff);

    if (!nq) ret = new exprGroup (c0, lcoeff); // create exprGroup
    else     ret = new exprQuad  (c0, lcoeff, qcoeff); // create exprQuad
  }

  delete [] li;
  delete [] lc;
  delete [] qi;
  delete [] qj;
  delete [] qc;

#ifdef DEBUG
  printf ("\nlinstand ==> "); 
  ret -> print (); printf ("\n"); 
  //  ret -> Image () -> print (); printf ("\n");
#endif

  //if (ret -> Type () == AUX)
  //return ret;

  return (addAux ? addAuxiliary (ret) : new exprAux (ret, &domain_));
}
