/* $Id: ClpConstraintQuadratic.cpp 1402 2009-07-25 08:39:55Z forrest $ */
// Copyright (C) 2007, International Business Machines
// Corporation and others.  All Rights Reserved.

#include "CoinPragma.hpp"
#include "CoinHelperFunctions.hpp"
#include "CoinIndexedVector.hpp"
#include "ClpSimplex.hpp"
#include "ClpConstraintQuadratic.hpp"
#include "CoinSort.hpp"
//#############################################################################
// Constructors / Destructor / Assignment
//#############################################################################

//-------------------------------------------------------------------
// Default Constructor 
//-------------------------------------------------------------------
ClpConstraintQuadratic::ClpConstraintQuadratic () 
: ClpConstraint()
{
  type_=0;
  start_=NULL;
  column_=NULL;
  coefficient_ = NULL;
  numberColumns_=0;
  numberCoefficients_=0;
  numberQuadraticColumns_=0;
}

//-------------------------------------------------------------------
// Useful Constructor 
//-------------------------------------------------------------------
ClpConstraintQuadratic::ClpConstraintQuadratic (int row, int numberQuadraticColumns , 
						int numberColumns, const CoinBigIndex * start,
						const int * column, const double * coefficient)
  : ClpConstraint()
{
  type_=0;
  rowNumber_=row;
  numberColumns_ = numberColumns;
  numberQuadraticColumns_ = numberQuadraticColumns;
  start_ = CoinCopyOfArray(start,numberQuadraticColumns+1);
  int numberElements = start_[numberQuadraticColumns_];
  column_ = CoinCopyOfArray(column,numberElements);
  coefficient_ = CoinCopyOfArray(coefficient,numberElements);
  char * mark = new char [numberQuadraticColumns_];
  memset(mark,0,numberQuadraticColumns_);
  int iColumn;
  for (iColumn=0;iColumn<numberQuadraticColumns_;iColumn++) {
    CoinBigIndex j;
    for (j=start_[iColumn];j<start_[iColumn+1];j++) {
      int jColumn = column_[j];
      if (jColumn>=0) {
	assert (jColumn<numberQuadraticColumns_);
	mark[jColumn]=1;
      }
      mark[iColumn]=1;
    }
  }
  numberCoefficients_ = 0;
  for (iColumn=0;iColumn<numberQuadraticColumns_;iColumn++) {
    if (mark[iColumn])
      numberCoefficients_++;
  }
  delete [] mark;
}

//-------------------------------------------------------------------
// Copy constructor 
//-------------------------------------------------------------------
ClpConstraintQuadratic::ClpConstraintQuadratic (const ClpConstraintQuadratic & rhs)
: ClpConstraint(rhs)
{  
  numberColumns_=rhs.numberColumns_;
  numberCoefficients_=rhs.numberCoefficients_;
  numberQuadraticColumns_ = rhs.numberQuadraticColumns_;
  start_ = CoinCopyOfArray(rhs.start_,numberQuadraticColumns_+1);
  int numberElements = start_[numberQuadraticColumns_];
  column_ = CoinCopyOfArray(rhs.column_,numberElements);
  coefficient_ = CoinCopyOfArray(rhs.coefficient_,numberElements);
}
  

//-------------------------------------------------------------------
// Destructor 
//-------------------------------------------------------------------
ClpConstraintQuadratic::~ClpConstraintQuadratic ()
{
  delete [] start_;
  delete [] column_;
  delete [] coefficient_;
}

//----------------------------------------------------------------
// Assignment operator 
//-------------------------------------------------------------------
ClpConstraintQuadratic &
ClpConstraintQuadratic::operator=(const ClpConstraintQuadratic& rhs)
{
  if (this != &rhs) {
    delete [] start_;
    delete [] column_;
    delete [] coefficient_;
    numberColumns_=rhs.numberColumns_;
    numberCoefficients_=rhs.numberCoefficients_;
    numberQuadraticColumns_ = rhs.numberQuadraticColumns_;
    start_ = CoinCopyOfArray(rhs.start_,numberQuadraticColumns_+1);
    int numberElements = start_[numberQuadraticColumns_];
    column_ = CoinCopyOfArray(rhs.column_,numberElements);
    coefficient_ = CoinCopyOfArray(rhs.coefficient_,numberElements);
  }
  return *this;
}
//-------------------------------------------------------------------
// Clone
//-------------------------------------------------------------------
ClpConstraint * ClpConstraintQuadratic::clone() const
{
  return new ClpConstraintQuadratic(*this);
}

// Returns gradient
int  
ClpConstraintQuadratic::gradient(const ClpSimplex * model,
			      const double * solution, 
			      double * gradient,
			      double & functionValue, 
			      double & offset,
			      bool useScaling,
			      bool refresh) const
{
  if (refresh||!lastGradient_) {
    offset_=0.0;
    functionValue_=0.0;
    if (!lastGradient_)
      lastGradient_ = new double[numberColumns_];
    CoinZeroN(lastGradient_,numberColumns_);
    bool scaling=(model&&model->rowScale()&&useScaling);
    if (!scaling) {
      int iColumn;
      for (iColumn=0;iColumn<numberQuadraticColumns_;iColumn++) {
	double valueI = solution[iColumn];
	CoinBigIndex j;
	for (j=start_[iColumn];j<start_[iColumn+1];j++) {
	  int jColumn = column_[j];
	  if (jColumn>=0) {
	    double valueJ = solution[jColumn];
	    double elementValue = coefficient_[j];
	    if (iColumn!=jColumn) {
	      offset_ -= valueI*valueJ*elementValue;
	      double gradientI = valueJ*elementValue;
	      double gradientJ = valueI*elementValue;
	      lastGradient_[iColumn] += gradientI;
	      lastGradient_[jColumn] += gradientJ;
	    } else {
	      offset_ -= 0.5*valueI*valueI*elementValue;
	      double gradientI = valueI*elementValue;
	      lastGradient_[iColumn] += gradientI;
	    }
	  } else {
	    // linear part
	    lastGradient_[iColumn] += coefficient_[j];
	    functionValue_ += valueI*coefficient_[j];
	  }
	}
      }
      functionValue_ -= offset_;
    } else {
      abort();
      // do scaling
      const double * columnScale = model->columnScale();
      for (int i=0;i<numberCoefficients_;i++) {
	int iColumn = column_[i];
	double value = solution[iColumn]; // already scaled
	double coefficient = coefficient_[i]*columnScale[iColumn];
	functionValue_ += value*coefficient;
	lastGradient_[iColumn]=coefficient;
      }
    }
  }
  functionValue = functionValue_;
  offset = offset_;
  CoinMemcpyN(lastGradient_,numberColumns_,gradient);
  return 0;
}
// Resize constraint
void 
ClpConstraintQuadratic::resize(int newNumberColumns)
{
  if (numberColumns_!=newNumberColumns) {
    abort();
#ifndef NDEBUG
    int lastColumn = column_[numberCoefficients_-1];
#endif
    assert (newNumberColumns>lastColumn);
    delete [] lastGradient_;
    lastGradient_ = NULL;
    numberColumns_ = newNumberColumns;
  } 
}
// Delete columns in  constraint
void 
ClpConstraintQuadratic::deleteSome(int numberToDelete, const int * which) 
{
  if (numberToDelete) {
    abort();
    int i ;
    char * deleted = new char[numberColumns_];
    memset(deleted,0,numberColumns_*sizeof(char));
    for (i=0;i<numberToDelete;i++) {
      int j = which[i];
      if (j>=0&&j<numberColumns_&&!deleted[j]) {
	deleted[j]=1;
      }
    }
    int n=0;
    for (i=0;i<numberCoefficients_;i++) {
      int iColumn = column_[i];
      if (!deleted[iColumn]) {
	column_[n]=iColumn;
	coefficient_[n++]=coefficient_[i];
      }
    }
    numberCoefficients_ = n;
  }
}
// Scale constraint 
void 
ClpConstraintQuadratic::reallyScale(const double * ) 
{
  abort();
}
/* Given a zeroed array sets nonquadratic columns to 1.
   Returns number of nonlinear columns
*/
int 
ClpConstraintQuadratic::markNonlinear(char * which) const
{
  int iColumn;
  for (iColumn=0;iColumn<numberQuadraticColumns_;iColumn++) {
    CoinBigIndex j;
    for (j=start_[iColumn];j<start_[iColumn+1];j++) {
      int jColumn = column_[j];
      if (jColumn>=0) {
	assert (jColumn<numberQuadraticColumns_);
	which[jColumn]=1;
	which[iColumn]=1;
      }
    }
  }
  int numberCoefficients = 0;
  for (iColumn=0;iColumn<numberQuadraticColumns_;iColumn++) {
    if (which[iColumn])
      numberCoefficients++;
  }
  return numberCoefficients;
}
/* Given a zeroed array sets possible nonzero coefficients to 1.
   Returns number of nonzeros
*/
int 
ClpConstraintQuadratic::markNonzero(char * which) const
{
  int iColumn;
  for (iColumn=0;iColumn<numberQuadraticColumns_;iColumn++) {
    CoinBigIndex j;
    for (j=start_[iColumn];j<start_[iColumn+1];j++) {
      int jColumn = column_[j];
      if (jColumn>=0) {
	assert (jColumn<numberQuadraticColumns_);
	which[jColumn]=1;
      }
      which[iColumn]=1;
    }
  }
  int numberCoefficients = 0;
  for (iColumn=0;iColumn<numberQuadraticColumns_;iColumn++) {
    if (which[iColumn])
      numberCoefficients++;
  }
  return numberCoefficients;
}
// Number of coefficients
int 
ClpConstraintQuadratic::numberCoefficients() const
{
  return numberCoefficients_;
}
