/* $Id$ */
// Copyright (C) 2002, International Business Machines
// Corporation and others, Copyright (C) 2012, FasterCoin.  All Rights Reserved.
// This code is licensed under the terms of the Eclipse Public License (EPL).

#include "CoinPragma.hpp"
#include "AbcSimplex.hpp"
#include "AbcDualRowDantzig.hpp"
#include "AbcSimplexFactorization.hpp"
#include "CoinIndexedVector.hpp"
#include "CoinHelperFunctions.hpp"
#ifndef CLP_DUAL_COLUMN_MULTIPLIER
#define CLP_DUAL_COLUMN_MULTIPLIER 1.01
#endif

//#############################################################################
// Constructors / Destructor / Assignment
//#############################################################################

//-------------------------------------------------------------------
// Default Constructor
//-------------------------------------------------------------------
AbcDualRowDantzig::AbcDualRowDantzig()
  : AbcDualRowPivot()
  , infeasible_(NULL)
{
  type_ = 1;
}

//-------------------------------------------------------------------
// Copy constructor
//-------------------------------------------------------------------
AbcDualRowDantzig::AbcDualRowDantzig(const AbcDualRowDantzig &rhs)
  : AbcDualRowPivot(rhs)
  , infeasible_(NULL)
{
  model_ = rhs.model_;
  if ((model_ && model_->whatsChanged() & 1) != 0) {
    if (rhs.infeasible_) {
      infeasible_ = new CoinIndexedVector(rhs.infeasible_);
    } else {
      infeasible_ = NULL;
    }
  }
}

//-------------------------------------------------------------------
// Destructor
//-------------------------------------------------------------------
AbcDualRowDantzig::~AbcDualRowDantzig()
{
  delete infeasible_;
}

//----------------------------------------------------------------
// Assignment operator
//-------------------------------------------------------------------
AbcDualRowDantzig &
AbcDualRowDantzig::operator=(const AbcDualRowDantzig &rhs)
{
  if (this != &rhs) {
    AbcDualRowPivot::operator=(rhs);
    delete infeasible_;
    if (rhs.infeasible_ != NULL) {
      infeasible_ = new CoinIndexedVector(rhs.infeasible_);
    } else {
      infeasible_ = NULL;
    }
  }
  return *this;
}

/* 
   1) before factorization
   2) after factorization
   3) just redo infeasibilities
   4) restore weights
*/
void AbcDualRowDantzig::saveWeights(AbcSimplex *model, int mode)
{
  model_ = model;
  int numberRows = model_->numberRows();
  if (mode == 1) {
    // Check if size has changed
    if (infeasible_ && infeasible_->capacity() != numberRows) {
      // size has changed - clear everything
      delete infeasible_;
      infeasible_ = NULL;
    }
  } else if (mode != 3 && !infeasible_) {
    infeasible_ = new CoinIndexedVector();
    infeasible_->reserve(numberRows);
  }
  if (mode >= 2) {
    recomputeInfeasibilities();
  }
}
// Recompute infeasibilities
void AbcDualRowDantzig::recomputeInfeasibilities()
{
  int numberRows = model_->numberRows();
  infeasible_->clear();
  double tolerance = model_->currentPrimalTolerance();
  const double *COIN_RESTRICT solutionBasic = model_->solutionBasic();
  const double *COIN_RESTRICT lowerBasic = model_->lowerBasic();
  const double *COIN_RESTRICT upperBasic = model_->upperBasic();
  for (int iRow = 0; iRow < numberRows; iRow++) {
    double value = solutionBasic[iRow];
    double lower = lowerBasic[iRow];
    double upper = upperBasic[iRow];
    if (value < lower - tolerance) {
      value -= lower;
#ifdef CLP_DUAL_FIXED_COLUMN_MULTIPLIER
      if (lower == upper)
        value *= CLP_DUAL_FIXED_COLUMN_MULTIPLIER; // bias towards taking out fixed variables
#endif
      // store in list
      infeasible_->quickAdd(iRow, fabs(value));
    } else if (value > upper + tolerance) {
      value -= upper;
#ifdef CLP_DUAL_FIXED_COLUMN_MULTIPLIER
      if (lower == upper)
        value *= CLP_DUAL_FIXED_COLUMN_MULTIPLIER; // bias towards taking out fixed variables
#endif
      // store in list
      infeasible_->quickAdd(iRow, fabs(value));
    }
  }
}
#if ABC_PARALLEL == 2
static void choose(CoinIndexedVector *infeasible,
  int &chosenRowSave, double &largestSave, int first, int last,
  double tolerance)
{
  if (last - first > 256) {
    int mid = (last + first) >> 1;
    int chosenRow2 = chosenRowSave;
    double largest2 = largestSave;
    cilk_spawn choose(infeasible, chosenRow2, largest2, first, mid,
      tolerance);
    choose(infeasible, chosenRowSave, largestSave, mid, last,
      tolerance);
    cilk_sync;
    if (largest2 > largestSave) {
      largestSave = largest2;
      chosenRowSave = chosenRow2;
    }
  } else {
    const int *index = infeasible->getIndices();
    const double *infeas = infeasible->denseVector();
    double largest = largestSave;
    int chosenRow = chosenRowSave;
    for (int i = first; i < last; i++) {
      int iRow = index[i];
      double value = infeas[iRow];
      if (value > largest) {
        largest = value;
        chosenRow = iRow;
      }
    }
    chosenRowSave = chosenRow;
    largestSave = largest;
  }
}
#endif
// Returns pivot row, -1 if none
int AbcDualRowDantzig::pivotRow()
{
  assert(model_);
  double *COIN_RESTRICT infeas = infeasible_->denseVector();
  int *COIN_RESTRICT index = infeasible_->getIndices();
  int number = infeasible_->getNumElements();
  double tolerance = model_->currentPrimalTolerance();
  // we can't really trust infeasibilities if there is primal error
  if (model_->largestPrimalError() > 1.0e-8)
    tolerance *= model_->largestPrimalError() / 1.0e-8;
  int numberRows = model_->numberRows();
  const double *COIN_RESTRICT solutionBasic = model_->solutionBasic();
  const double *COIN_RESTRICT lowerBasic = model_->lowerBasic();
  const double *COIN_RESTRICT upperBasic = model_->upperBasic();
  const int *pivotVariable = model_->pivotVariable();
  // code so has list of infeasibilities (like steepest)
  int numberWanted = CoinMax(2000, numberRows >> 4);
  numberWanted = CoinMax(numberWanted, number >> 2);
  if (model_->largestPrimalError() > 1.0e-3)
    numberWanted = number + 1; // be safe
  // Setup two passes
  int start[4];
  start[1] = number;
  start[2] = 0;
  double dstart = static_cast< double >(number) * model_->randomNumberGenerator()->randomDouble();
  start[0] = static_cast< int >(dstart);
  start[3] = start[0];
  double largest = tolerance;
  int chosenRow = -1;
  int saveNumberWanted = numberWanted;
#ifdef DO_REDUCE
  bool doReduce = true;
  int lastChosen = -1;
  double lastLargest = 0.0;
#endif
  for (int iPass = 0; iPass < 2; iPass++) {
    int endThis = start[2 * iPass + 1];
    int startThis = start[2 * iPass];
    while (startThis < endThis) {
      int end = CoinMin(endThis, startThis + numberWanted);
#ifdef DO_REDUCE
      if (doReduce) {
        choose(infeasible, chosenRow, largest, startThis, end, tolerance);
        if (chosenRow != lastChosen) {
          assert(chosenRow >= 0);
          if (model_->flagged(pivotVariable[chosenRow]) || (solutionBasic[chosenRow] <= upperBasic[chosenRow] + tolerance && solutionBasic[chosenRow] >= lowerBasic[chosenRow] - tolerance)) {
            doReduce = false;
            chosenRow = lastChosen;
            largest = lastLargest;
          } else {
            lastChosen = chosenRow;
            lastLargest = largest;
          }
        }
      }
      if (!doReduce) {
#endif
        for (int i = startThis; i < end; i++) {
          int iRow = index[i];
          double value = infeas[iRow];
          if (value > largest) {
            if (!model_->flagged(pivotVariable[iRow])) {
              if (solutionBasic[iRow] > upperBasic[iRow] + tolerance || solutionBasic[iRow] < lowerBasic[iRow] - tolerance) {
                chosenRow = iRow;
                largest = value;
              }
            }
          }
        }
#ifdef DO_REDUCE
      }
#endif
      numberWanted -= (end - startThis);
      if (!numberWanted) {
        if (chosenRow >= 0)
          break;
        else
          numberWanted = (saveNumberWanted + 1) >> 1;
      }
      startThis = end;
    }
    if (!numberWanted) {
      if (chosenRow >= 0)
        break;
      else
        numberWanted = (saveNumberWanted + 1) >> 1;
    }
  }
  return chosenRow;
}
// FT update and returns pivot alpha
double
AbcDualRowDantzig::updateWeights(CoinIndexedVector &input, CoinIndexedVector &updatedColumn)
{
  // Do FT update
  model_->factorization()->updateColumnFT(updatedColumn);
  // pivot element
  double alpha = 0.0;
  // look at updated column
  double *work = updatedColumn.denseVector();
  int pivotRow = model_->lastPivotRow();
  assert(pivotRow == model_->pivotRow());

  assert(!updatedColumn.packedMode());
  alpha = work[pivotRow];
  return alpha;
}
double
AbcDualRowDantzig::updateWeights1(CoinIndexedVector &input, CoinIndexedVector &updateColumn)
{
  return updateWeights(input, updateColumn);
}
#if ABC_PARALLEL == 2
static void update(int first, int last,
  const int *COIN_RESTRICT which, double *COIN_RESTRICT work,
  const double *COIN_RESTRICT lowerBasic, double *COIN_RESTRICT solutionBasic,
  const double *COIN_RESTRICT upperBasic, double theta, double tolerance)
{
  if (last - first > 256) {
    int mid = (last + first) >> 1;
    cilk_spawn update(first, mid, which, work, lowerBasic, solutionBasic,
      upperBasic, theta, tolerance);
    update(mid, last, which, work, lowerBasic, solutionBasic,
      upperBasic, theta, tolerance);
    cilk_sync;
  } else {
    for (int i = first; i < last; i++) {
      int iRow = which[i];
      double updateValue = work[iRow];
      double value = solutionBasic[iRow];
      double change = theta * updateValue;
      value -= change;
      double lower = lowerBasic[iRow];
      double upper = upperBasic[iRow];
      solutionBasic[iRow] = value;
      if (value < lower - tolerance) {
        value -= lower;
#ifdef CLP_DUAL_FIXED_COLUMN_MULTIPLIER
        if (lower == upper)
          value *= CLP_DUAL_FIXED_COLUMN_MULTIPLIER; // bias towards taking out fixed variables
#endif
      } else if (value > upper + tolerance) {
        value -= upper;
#ifdef CLP_DUAL_FIXED_COLUMN_MULTIPLIER
        if (lower == upper)
          value *= CLP_DUAL_FIXED_COLUMN_MULTIPLIER; // bias towards taking out fixed variables
#endif
      } else {
        // feasible
        value = 0.0;
      }
      // store
      work[iRow] = fabs(value);
    }
  }
}
#endif
/* Updates primal solution (and maybe list of candidates)
   Uses input vector which it deletes
   Computes change in objective function
*/
void AbcDualRowDantzig::updatePrimalSolution(CoinIndexedVector &primalUpdate,
  double theta)
{
  double *COIN_RESTRICT work = primalUpdate.denseVector();
  int numberNonZero = primalUpdate.getNumElements();
  int *which = primalUpdate.getIndices();
  double tolerance = model_->currentPrimalTolerance();
  double *COIN_RESTRICT infeas = infeasible_->denseVector();
  double *COIN_RESTRICT solutionBasic = model_->solutionBasic();
  const double *COIN_RESTRICT lowerBasic = model_->lowerBasic();
  const double *COIN_RESTRICT upperBasic = model_->upperBasic();
  assert(!primalUpdate.packedMode());
#if 0 //ABC_PARALLEL==2
  update(0,numberNonZero,which,work,
	 lowerBasic,solutionBasic,upperBasic,
	 theta,tolerance);
  for (int i = 0; i < numberNonZero; i++) {
    int iRow = which[i];
    double infeasibility=work[iRow];
    work[iRow]=0.0;
    if (infeasibility) {
      if (infeas[iRow])
	infeas[iRow] = infeasibility; // already there
      else
	infeasible_->quickAdd(iRow, infeasibility);
    } else {
      // feasible - was it infeasible - if so set tiny
      if (infeas[iRow])
	infeas[iRow] = COIN_INDEXED_REALLY_TINY_ELEMENT;
    }
  }
#else
  for (int i = 0; i < numberNonZero; i++) {
    int iRow = which[i];
    double updateValue = work[iRow];
    work[iRow] = 0.0;
    double value = solutionBasic[iRow];
    double change = theta * updateValue;
    value -= change;
    double lower = lowerBasic[iRow];
    double upper = upperBasic[iRow];
    solutionBasic[iRow] = value;
    if (value < lower - tolerance) {
      value -= lower;
#ifdef CLP_DUAL_FIXED_COLUMN_MULTIPLIER
      if (lower == upper)
        value *= CLP_DUAL_FIXED_COLUMN_MULTIPLIER; // bias towards taking out fixed variables
#endif
      // store in list
      if (infeas[iRow])
        infeas[iRow] = fabs(value); // already there
      else
        infeasible_->quickAdd(iRow, fabs(value));
    } else if (value > upper + tolerance) {
      value -= upper;
#ifdef CLP_DUAL_FIXED_COLUMN_MULTIPLIER
      if (lower == upper)
        value *= CLP_DUAL_FIXED_COLUMN_MULTIPLIER; // bias towards taking out fixed variables
#endif
      // store in list
      if (infeas[iRow])
        infeas[iRow] = fabs(value); // already there
      else
        infeasible_->quickAdd(iRow, fabs(value));
    } else {
      // feasible - was it infeasible - if so set tiny
      if (infeas[iRow])
        infeas[iRow] = COIN_INDEXED_REALLY_TINY_ELEMENT;
    }
  }
#endif
  primalUpdate.setNumElements(0);
}
//-------------------------------------------------------------------
// Clone
//-------------------------------------------------------------------
AbcDualRowPivot *AbcDualRowDantzig::clone(bool CopyData) const
{
  if (CopyData) {
    return new AbcDualRowDantzig(*this);
  } else {
    return new AbcDualRowDantzig();
  }
}

/* vi: softtabstop=2 shiftwidth=2 expandtab tabstop=2
*/
