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

/*----------------------------------------------------------------------------*/
/*      Ordering code - courtesy of Anshul Gupta                              */
/*	(C) Copyright IBM Corporation 1997, 2009.  All Rights Reserved.       */
/*----------------------------------------------------------------------------*/

/*  A compact no-frills Approximate Minimum Local Fill ordering code.

    References:

[1] Ordering Sparse Matrices Using Approximate Minimum Local Fill.
    Edward Rothberg, SGI Manuscript, April 1996.
[2] An Approximate Minimum Degree Ordering Algorithm.
    T. Davis, P. Amestoy, and I. Duff, TR-94-039, CIS Department,
    University of Florida, December 1994.
*/
/*----------------------------------------------------------------------------*/

#include "CoinPragma.hpp"

#include <iostream>

#include "ClpCholeskyBase.hpp"
#include "ClpInterior.hpp"
#include "ClpHelperFunctions.hpp"
#include "CoinHelperFunctions.hpp"
#include "CoinSort.hpp"
#include "ClpCholeskyDense.hpp"
#include "ClpMessage.hpp"
#include "ClpQuadraticObjective.hpp"

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

//-------------------------------------------------------------------
// Default Constructor
//-------------------------------------------------------------------
ClpCholeskyBase::ClpCholeskyBase(int denseThreshold)
  : type_(0)
  , doKKT_(false)
  , goDense_(0.7)
  , choleskyCondition_(0.0)
  , model_(NULL)
  , numberTrials_()
  , numberRows_(0)
  , status_(0)
  , rowsDropped_(NULL)
  , permuteInverse_(NULL)
  , permute_(NULL)
  , numberRowsDropped_(0)
  , sparseFactor_(NULL)
  , choleskyStart_(NULL)
  , choleskyRow_(NULL)
  , indexStart_(NULL)
  , diagonal_(NULL)
  , workDouble_(NULL)
  , link_(NULL)
  , workInteger_(NULL)
  , clique_(NULL)
  , sizeFactor_(0)
  , sizeIndex_(0)
  , firstDense_(0)
  , rowCopy_(NULL)
  , whichDense_(NULL)
  , denseColumn_(NULL)
  , dense_(NULL)
  , denseThreshold_(denseThreshold)
{
  memset(integerParameters_, 0, 64 * sizeof(int));
  memset(doubleParameters_, 0, 64 * sizeof(double));
}

//-------------------------------------------------------------------
// Copy constructor
//-------------------------------------------------------------------
ClpCholeskyBase::ClpCholeskyBase(const ClpCholeskyBase &rhs)
  : type_(rhs.type_)
  , doKKT_(rhs.doKKT_)
  , goDense_(rhs.goDense_)
  , choleskyCondition_(rhs.choleskyCondition_)
  , model_(rhs.model_)
  , numberTrials_(rhs.numberTrials_)
  , numberRows_(rhs.numberRows_)
  , status_(rhs.status_)
  , numberRowsDropped_(rhs.numberRowsDropped_)
{
  rowsDropped_ = ClpCopyOfArray(rhs.rowsDropped_, numberRows_);
  permuteInverse_ = ClpCopyOfArray(rhs.permuteInverse_, numberRows_);
  permute_ = ClpCopyOfArray(rhs.permute_, numberRows_);
  sizeFactor_ = rhs.sizeFactor_;
  sizeIndex_ = rhs.sizeIndex_;
  firstDense_ = rhs.firstDense_;
  sparseFactor_ = ClpCopyOfArray(rhs.sparseFactor_, rhs.sizeFactor_);
  choleskyStart_ = ClpCopyOfArray(rhs.choleskyStart_, numberRows_ + 1);
  indexStart_ = ClpCopyOfArray(rhs.indexStart_, numberRows_);
  choleskyRow_ = ClpCopyOfArray(rhs.choleskyRow_, sizeIndex_);
  diagonal_ = ClpCopyOfArray(rhs.diagonal_, numberRows_);
#if CLP_LONG_CHOLESKY != 1
  workDouble_ = ClpCopyOfArray(rhs.workDouble_, numberRows_);
#else
  // actually long double
  workDouble_ = reinterpret_cast< double * >(ClpCopyOfArray(reinterpret_cast< CoinWorkDouble * >(rhs.workDouble_), numberRows_));
#endif
  link_ = ClpCopyOfArray(rhs.link_, numberRows_);
  workInteger_ = ClpCopyOfArray(rhs.workInteger_, numberRows_);
  clique_ = ClpCopyOfArray(rhs.clique_, numberRows_);
  CoinMemcpyN(rhs.integerParameters_, 64, integerParameters_);
  CoinMemcpyN(rhs.doubleParameters_, 64, doubleParameters_);
  rowCopy_ = rhs.rowCopy_->clone();
  whichDense_ = NULL;
  denseColumn_ = NULL;
  dense_ = NULL;
  denseThreshold_ = rhs.denseThreshold_;
}

//-------------------------------------------------------------------
// Destructor
//-------------------------------------------------------------------
ClpCholeskyBase::~ClpCholeskyBase()
{
  delete[] rowsDropped_;
  delete[] permuteInverse_;
  delete[] permute_;
  delete[] sparseFactor_;
  delete[] choleskyStart_;
  delete[] choleskyRow_;
  delete[] indexStart_;
  delete[] diagonal_;
  delete[] workDouble_;
  delete[] link_;
  delete[] workInteger_;
  delete[] clique_;
  delete rowCopy_;
  delete[] whichDense_;
  delete[] denseColumn_;
  delete dense_;
}

//----------------------------------------------------------------
// Assignment operator
//-------------------------------------------------------------------
ClpCholeskyBase &
ClpCholeskyBase::operator=(const ClpCholeskyBase &rhs)
{
  if (this != &rhs) {
    type_ = rhs.type_;
    doKKT_ = rhs.doKKT_;
    goDense_ = rhs.goDense_;
    choleskyCondition_ = rhs.choleskyCondition_;
    model_ = rhs.model_;
    numberTrials_ = rhs.numberTrials_;
    numberRows_ = rhs.numberRows_;
    status_ = rhs.status_;
    numberRowsDropped_ = rhs.numberRowsDropped_;
    delete[] rowsDropped_;
    delete[] permuteInverse_;
    delete[] permute_;
    delete[] sparseFactor_;
    delete[] choleskyStart_;
    delete[] choleskyRow_;
    delete[] indexStart_;
    delete[] diagonal_;
    delete[] workDouble_;
    delete[] link_;
    delete[] workInteger_;
    delete[] clique_;
    delete rowCopy_;
    delete[] whichDense_;
    delete[] denseColumn_;
    delete dense_;
    rowsDropped_ = ClpCopyOfArray(rhs.rowsDropped_, numberRows_);
    permuteInverse_ = ClpCopyOfArray(rhs.permuteInverse_, numberRows_);
    permute_ = ClpCopyOfArray(rhs.permute_, numberRows_);
    sizeFactor_ = rhs.sizeFactor_;
    sizeIndex_ = rhs.sizeIndex_;
    firstDense_ = rhs.firstDense_;
    sparseFactor_ = ClpCopyOfArray(rhs.sparseFactor_, rhs.sizeFactor_);
    choleskyStart_ = ClpCopyOfArray(rhs.choleskyStart_, numberRows_ + 1);
    choleskyRow_ = ClpCopyOfArray(rhs.choleskyRow_, rhs.sizeFactor_);
    indexStart_ = ClpCopyOfArray(rhs.indexStart_, numberRows_);
    choleskyRow_ = ClpCopyOfArray(rhs.choleskyRow_, sizeIndex_);
    diagonal_ = ClpCopyOfArray(rhs.diagonal_, numberRows_);
#if CLP_LONG_CHOLESKY != 1
    workDouble_ = ClpCopyOfArray(rhs.workDouble_, numberRows_);
#else
    // actually long double
    workDouble_ = reinterpret_cast< double * >(ClpCopyOfArray(reinterpret_cast< CoinWorkDouble * >(rhs.workDouble_), numberRows_));
#endif
    link_ = ClpCopyOfArray(rhs.link_, numberRows_);
    workInteger_ = ClpCopyOfArray(rhs.workInteger_, numberRows_);
    clique_ = ClpCopyOfArray(rhs.clique_, numberRows_);
    rowCopy_ = rhs.rowCopy_->clone();
    whichDense_ = NULL;
    denseColumn_ = NULL;
    dense_ = NULL;
    denseThreshold_ = rhs.denseThreshold_;
  }
  return *this;
}
// reset numberRowsDropped and rowsDropped.
void ClpCholeskyBase::resetRowsDropped()
{
  numberRowsDropped_ = 0;
  memset(rowsDropped_, 0, numberRows_);
}
/* Uses factorization to solve. - given as if KKT.
   region1 is rows+columns, region2 is rows */
void ClpCholeskyBase::solveKKT(CoinWorkDouble *region1, CoinWorkDouble *region2, const CoinWorkDouble *diagonal,
  CoinWorkDouble diagonalScaleFactor)
{
  if (!doKKT_) {
    int iColumn;
    int numberColumns = model_->numberColumns();
    int numberTotal = numberRows_ + numberColumns;
    CoinWorkDouble *region1Save = new CoinWorkDouble[numberTotal];
    for (iColumn = 0; iColumn < numberTotal; iColumn++) {
      region1[iColumn] *= diagonal[iColumn];
      region1Save[iColumn] = region1[iColumn];
    }
    multiplyAdd(region1 + numberColumns, numberRows_, -1.0, region2, 1.0);
    model_->clpMatrix()->times(1.0, region1, region2);
    CoinWorkDouble maximumRHS = maximumAbsElement(region2, numberRows_);
    CoinWorkDouble scale = 1.0;
    CoinWorkDouble unscale = 1.0;
    if (maximumRHS > 1.0e-30) {
      if (maximumRHS <= 0.5) {
        CoinWorkDouble factor = 2.0;
        while (maximumRHS <= 0.5) {
          maximumRHS *= factor;
          scale *= factor;
        } /* endwhile */
      } else if (maximumRHS >= 2.0 && maximumRHS <= COIN_DBL_MAX) {
        CoinWorkDouble factor = 0.5;
        while (maximumRHS >= 2.0) {
          maximumRHS *= factor;
          scale *= factor;
        } /* endwhile */
      }
      unscale = diagonalScaleFactor / scale;
    } else {
      //effectively zero
      scale = 0.0;
      unscale = 0.0;
    }
    multiplyAdd(NULL, numberRows_, 0.0, region2, scale);
    solve(region2);
    multiplyAdd(NULL, numberRows_, 0.0, region2, unscale);
    multiplyAdd(region2, numberRows_, -1.0, region1 + numberColumns, 0.0);
    CoinZeroN(region1, numberColumns);
    model_->clpMatrix()->transposeTimes(1.0, region2, region1);
    for (iColumn = 0; iColumn < numberTotal; iColumn++)
      region1[iColumn] = region1[iColumn] * diagonal[iColumn] - region1Save[iColumn];
    delete[] region1Save;
  } else {
    // KKT
    int numberRowsModel = model_->numberRows();
    int numberColumns = model_->numberColumns();
    int numberTotal = numberColumns + numberRowsModel;
    CoinWorkDouble *array = new CoinWorkDouble[numberRows_];
    CoinMemcpyN(region1, numberTotal, array);
    CoinMemcpyN(region2, numberRowsModel, array + numberTotal);
    assert(numberRows_ >= numberRowsModel + numberTotal);
    solve(array);
    int iRow;
    for (iRow = 0; iRow < numberTotal; iRow++) {
      if (rowsDropped_[iRow] && CoinAbs(array[iRow]) > 1.0e-8) {
        COIN_DETAIL_PRINT(printf("row region1 %d dropped %g\n", iRow, array[iRow]));
      }
    }
    for (; iRow < numberRows_; iRow++) {
      if (rowsDropped_[iRow] && CoinAbs(array[iRow]) > 1.0e-8) {
        COIN_DETAIL_PRINT(printf("row region2 %d dropped %g\n", iRow, array[iRow]));
      }
    }
    CoinMemcpyN(array + numberTotal, numberRowsModel, region2);
    CoinMemcpyN(array, numberTotal, region1);
    delete[] array;
  }
}
//-------------------------------------------------------------------
// Clone
//-------------------------------------------------------------------
ClpCholeskyBase *ClpCholeskyBase::clone() const
{
  return new ClpCholeskyBase(*this);
}
// Forms ADAT - returns nonzero if not enough memory
int ClpCholeskyBase::preOrder(bool lowerTriangular, bool includeDiagonal, bool doKKT)
{
  delete rowCopy_;
  rowCopy_ = model_->clpMatrix()->reverseOrderedCopy();
  if (!doKKT) {
    numberRows_ = model_->numberRows();
    rowsDropped_ = new char[numberRows_];
    memset(rowsDropped_, 0, numberRows_);
    numberRowsDropped_ = 0;
    // Space for starts
    choleskyStart_ = new int[numberRows_ + 1];
    const CoinBigIndex *columnStart = model_->clpMatrix()->getVectorStarts();
    const int *columnLength = model_->clpMatrix()->getVectorLengths();
    const int *row = model_->clpMatrix()->getIndices();
    const CoinBigIndex *rowStart = rowCopy_->getVectorStarts();
    const int *rowLength = rowCopy_->getVectorLengths();
    const int *column = rowCopy_->getIndices();
    // We need two arrays for counts
    int *which = new int[numberRows_];
    int *used = new int[numberRows_ + 1];
    CoinZeroN(used, numberRows_);
    int iRow;
    sizeFactor_ = 0;
    int numberColumns = model_->numberColumns();
    int numberDense = 0;
    //denseThreshold_=3;
    if (denseThreshold_ > 0) {
      delete[] whichDense_;
      delete[] denseColumn_;
      delete dense_;
      whichDense_ = new char[numberColumns];
      int iColumn;
      used[numberRows_] = 0;
      for (iColumn = 0; iColumn < numberColumns; iColumn++) {
        int length = columnLength[iColumn];
        used[length] += 1;
      }
      int nLong = 0;
      int stop = CoinMax(denseThreshold_ / 2, 100);
      for (iRow = numberRows_; iRow >= stop; iRow--) {
        if (used[iRow])
          COIN_DETAIL_PRINT(printf("%d columns are of length %d\n", used[iRow], iRow));
        nLong += used[iRow];
        if (nLong > 50 || nLong > (numberColumns >> 2))
          break;
      }
      CoinZeroN(used, numberRows_);
      for (iColumn = 0; iColumn < numberColumns; iColumn++) {
        if (columnLength[iColumn] < denseThreshold_) {
          whichDense_[iColumn] = 0;
        } else {
          whichDense_[iColumn] = 1;
          numberDense++;
        }
      }
      if (!numberDense || numberDense > 100) {
        // free
        delete[] whichDense_;
        whichDense_ = NULL;
        denseColumn_ = NULL;
        dense_ = NULL;
      } else {
        // space for dense columns
        denseColumn_ = new longDouble[numberDense * numberRows_];
        // dense cholesky
        dense_ = new ClpCholeskyDense();
        dense_->reserveSpace(NULL, numberDense);
        COIN_DETAIL_PRINT(printf("Taking %d columns as dense\n", numberDense));
      }
    }
    int offset = includeDiagonal ? 0 : 1;
    if (lowerTriangular)
      offset = -offset;
    for (iRow = 0; iRow < numberRows_; iRow++) {
      int number = 0;
      // make sure diagonal exists if includeDiagonal
      if (!offset) {
        which[0] = iRow;
        used[iRow] = 1;
        number = 1;
      }
      CoinBigIndex startRow = rowStart[iRow];
      CoinBigIndex endRow = rowStart[iRow] + rowLength[iRow];
      if (lowerTriangular) {
        for (CoinBigIndex k = startRow; k < endRow; k++) {
          int iColumn = column[k];
          if (!whichDense_ || !whichDense_[iColumn]) {
            CoinBigIndex start = columnStart[iColumn];
            CoinBigIndex end = columnStart[iColumn] + columnLength[iColumn];
            for (CoinBigIndex j = start; j < end; j++) {
              int jRow = row[j];
              if (jRow <= iRow + offset) {
                if (!used[jRow]) {
                  used[jRow] = 1;
                  which[number++] = jRow;
                }
              }
            }
          }
        }
      } else {
        for (CoinBigIndex k = startRow; k < endRow; k++) {
          int iColumn = column[k];
          if (!whichDense_ || !whichDense_[iColumn]) {
            CoinBigIndex start = columnStart[iColumn];
            CoinBigIndex end = columnStart[iColumn] + columnLength[iColumn];
            for (CoinBigIndex j = start; j < end; j++) {
              int jRow = row[j];
              if (jRow >= iRow + offset) {
                if (!used[jRow]) {
                  used[jRow] = 1;
                  which[number++] = jRow;
                }
              }
            }
          }
        }
      }
      sizeFactor_ += number;
      int j;
      for (j = 0; j < number; j++)
        used[which[j]] = 0;
    }
    delete[] which;
    // Now we have size - create arrays and fill in
    try {
      choleskyRow_ = new int[sizeFactor_];
    } catch (...) {
      // no memory
      delete[] choleskyStart_;
      choleskyStart_ = NULL;
      return -1;
    }
    sizeFactor_ = 0;
    which = choleskyRow_;
    for (iRow = 0; iRow < numberRows_; iRow++) {
      int number = 0;
      // make sure diagonal exists if includeDiagonal
      if (!offset) {
        which[0] = iRow;
        used[iRow] = 1;
        number = 1;
      }
      choleskyStart_[iRow] = sizeFactor_;
      CoinBigIndex startRow = rowStart[iRow];
      CoinBigIndex endRow = rowStart[iRow] + rowLength[iRow];
      if (lowerTriangular) {
        for (CoinBigIndex k = startRow; k < endRow; k++) {
          int iColumn = column[k];
          if (!whichDense_ || !whichDense_[iColumn]) {
            CoinBigIndex start = columnStart[iColumn];
            CoinBigIndex end = columnStart[iColumn] + columnLength[iColumn];
            for (CoinBigIndex j = start; j < end; j++) {
              int jRow = row[j];
              if (jRow <= iRow + offset) {
                if (!used[jRow]) {
                  used[jRow] = 1;
                  which[number++] = jRow;
                }
              }
            }
          }
        }
      } else {
        for (CoinBigIndex k = startRow; k < endRow; k++) {
          int iColumn = column[k];
          if (!whichDense_ || !whichDense_[iColumn]) {
            CoinBigIndex start = columnStart[iColumn];
            CoinBigIndex end = columnStart[iColumn] + columnLength[iColumn];
            for (CoinBigIndex j = start; j < end; j++) {
              int jRow = row[j];
              if (jRow >= iRow + offset) {
                if (!used[jRow]) {
                  used[jRow] = 1;
                  which[number++] = jRow;
                }
              }
            }
          }
        }
      }
      sizeFactor_ += number;
      int j;
      for (j = 0; j < number; j++)
        used[which[j]] = 0;
      // Sort
      std::sort(which, which + number);
      // move which on
      which += number;
    }
    choleskyStart_[numberRows_] = sizeFactor_;
    delete[] used;
    return 0;
  } else {
    int numberRowsModel = model_->numberRows();
    int numberColumns = model_->numberColumns();
    int numberTotal = numberColumns + numberRowsModel;
    numberRows_ = 2 * numberRowsModel + numberColumns;
    rowsDropped_ = new char[numberRows_];
    memset(rowsDropped_, 0, numberRows_);
    numberRowsDropped_ = 0;
    CoinPackedMatrix *quadratic = NULL;
    ClpQuadraticObjective *quadraticObj = (dynamic_cast< ClpQuadraticObjective * >(model_->objectiveAsObject()));
    if (quadraticObj)
      quadratic = quadraticObj->quadraticObjective();
    CoinBigIndex numberElements = model_->clpMatrix()->getNumElements();
    numberElements = numberElements + 2 * numberRowsModel + numberTotal;
    if (quadratic)
      numberElements += quadratic->getNumElements();
    // Space for starts
    choleskyStart_ = new int[numberRows_ + 1];
    const CoinBigIndex *columnStart = model_->clpMatrix()->getVectorStarts();
    const int *columnLength = model_->clpMatrix()->getVectorLengths();
    const int *row = model_->clpMatrix()->getIndices();
    //const double * element = model_->clpMatrix()->getElements();
    // Now we have size - create arrays and fill in
    try {
      choleskyRow_ = new int[numberElements];
    } catch (...) {
      // no memory
      delete[] choleskyStart_;
      choleskyStart_ = NULL;
      return -1;
    }
    int iRow, iColumn;

    sizeFactor_ = 0;
    // matrix
    if (lowerTriangular) {
      if (!quadratic) {
        for (iColumn = 0; iColumn < numberColumns; iColumn++) {
          choleskyStart_[iColumn] = sizeFactor_;
          choleskyRow_[sizeFactor_++] = iColumn;
          CoinBigIndex start = columnStart[iColumn];
          CoinBigIndex end = columnStart[iColumn] + columnLength[iColumn];
          if (!includeDiagonal)
            start++;
          for (CoinBigIndex j = start; j < end; j++) {
            choleskyRow_[sizeFactor_++] = row[j] + numberTotal;
          }
        }
      } else {
        // Quadratic
        const int *columnQuadratic = quadratic->getIndices();
        const CoinBigIndex *columnQuadraticStart = quadratic->getVectorStarts();
        const int *columnQuadraticLength = quadratic->getVectorLengths();
        //const double * quadraticElement = quadratic->getElements();
        for (iColumn = 0; iColumn < numberColumns; iColumn++) {
          choleskyStart_[iColumn] = sizeFactor_;
          if (includeDiagonal)
            choleskyRow_[sizeFactor_++] = iColumn;
          CoinBigIndex j;
          for (j = columnQuadraticStart[iColumn];
               j < columnQuadraticStart[iColumn] + columnQuadraticLength[iColumn]; j++) {
            int jColumn = columnQuadratic[j];
            if (jColumn > iColumn)
              choleskyRow_[sizeFactor_++] = jColumn;
          }
          CoinBigIndex start = columnStart[iColumn];
          CoinBigIndex end = columnStart[iColumn] + columnLength[iColumn];
          for (j = start; j < end; j++) {
            choleskyRow_[sizeFactor_++] = row[j] + numberTotal;
          }
        }
      }
      // slacks
      for (; iColumn < numberTotal; iColumn++) {
        choleskyStart_[iColumn] = sizeFactor_;
        if (includeDiagonal)
          choleskyRow_[sizeFactor_++] = iColumn;
        choleskyRow_[sizeFactor_++] = iColumn - numberColumns + numberTotal;
      }
      // Transpose - nonzero diagonal (may regularize)
      for (iRow = 0; iRow < numberRowsModel; iRow++) {
        choleskyStart_[iRow + numberTotal] = sizeFactor_;
        // diagonal
        if (includeDiagonal)
          choleskyRow_[sizeFactor_++] = iRow + numberTotal;
      }
      choleskyStart_[numberRows_] = sizeFactor_;
    } else {
      // transpose
      ClpMatrixBase *rowCopy = model_->clpMatrix()->reverseOrderedCopy();
      const CoinBigIndex *rowStart = rowCopy->getVectorStarts();
      const int *rowLength = rowCopy->getVectorLengths();
      const int *column = rowCopy->getIndices();
      if (!quadratic) {
        for (iColumn = 0; iColumn < numberColumns; iColumn++) {
          choleskyStart_[iColumn] = sizeFactor_;
          if (includeDiagonal)
            choleskyRow_[sizeFactor_++] = iColumn;
        }
      } else {
        // Quadratic
        // transpose
        CoinPackedMatrix quadraticT;
        quadraticT.reverseOrderedCopyOf(*quadratic);
        const int *columnQuadratic = quadraticT.getIndices();
        const CoinBigIndex *columnQuadraticStart = quadraticT.getVectorStarts();
        const int *columnQuadraticLength = quadraticT.getVectorLengths();
        //const double * quadraticElement = quadraticT.getElements();
        for (iColumn = 0; iColumn < numberColumns; iColumn++) {
          choleskyStart_[iColumn] = sizeFactor_;
          for (CoinBigIndex j = columnQuadraticStart[iColumn];
               j < columnQuadraticStart[iColumn] + columnQuadraticLength[iColumn]; j++) {
            int jColumn = columnQuadratic[j];
            if (jColumn < iColumn)
              choleskyRow_[sizeFactor_++] = jColumn;
          }
          if (includeDiagonal)
            choleskyRow_[sizeFactor_++] = iColumn;
        }
      }
      int iRow;
      // slacks
      for (iRow = 0; iRow < numberRowsModel; iRow++) {
        choleskyStart_[iRow + numberColumns] = sizeFactor_;
        if (includeDiagonal)
          choleskyRow_[sizeFactor_++] = iRow + numberColumns;
      }
      for (iRow = 0; iRow < numberRowsModel; iRow++) {
        choleskyStart_[iRow + numberTotal] = sizeFactor_;
        CoinBigIndex start = rowStart[iRow];
        CoinBigIndex end = rowStart[iRow] + rowLength[iRow];
        for (CoinBigIndex j = start; j < end; j++) {
          choleskyRow_[sizeFactor_++] = column[j];
        }
        // slack
        choleskyRow_[sizeFactor_++] = numberColumns + iRow;
        if (includeDiagonal)
          choleskyRow_[sizeFactor_++] = iRow + numberTotal;
      }
      choleskyStart_[numberRows_] = sizeFactor_;
    }
  }
  return 0;
}
/* Orders rows and saves pointer to matrix.and model */
int ClpCholeskyBase::order(ClpInterior *model)
{
  model_ = model;
#define BASE_ORDER 2
#if BASE_ORDER > 0
  if (model_->numberRows() > 6) {
    if (preOrder(doKKT_, true, doKKT_))
      return -1;
    //rowsDropped_ = new char [numberRows_];
    numberRowsDropped_ = 0;
    memset(rowsDropped_, 0, numberRows_);
    //rowCopy_ = model->clpMatrix()->reverseOrderedCopy();
    // approximate minimum degree
    return orderAMD();
  }
#endif
  int numberRowsModel = model_->numberRows();
  int numberColumns = model_->numberColumns();
  int numberTotal = numberColumns + numberRowsModel;
  CoinPackedMatrix *quadratic = NULL;
  ClpQuadraticObjective *quadraticObj = (dynamic_cast< ClpQuadraticObjective * >(model_->objectiveAsObject()));
  if (quadraticObj)
    quadratic = quadraticObj->quadraticObjective();
  if (!doKKT_) {
    numberRows_ = model->numberRows();
  } else {
    numberRows_ = 2 * numberRowsModel + numberColumns;
  }
  rowsDropped_ = new char[numberRows_];
  numberRowsDropped_ = 0;
  memset(rowsDropped_, 0, numberRows_);
  rowCopy_ = model->clpMatrix()->reverseOrderedCopy();
  const CoinBigIndex *columnStart = model_->clpMatrix()->getVectorStarts();
  const int *columnLength = model_->clpMatrix()->getVectorLengths();
  const int *row = model_->clpMatrix()->getIndices();
  const CoinBigIndex *rowStart = rowCopy_->getVectorStarts();
  const int *rowLength = rowCopy_->getVectorLengths();
  const int *column = rowCopy_->getIndices();
  // We need arrays for counts
  int *which = new int[numberRows_];
  int *used = new int[numberRows_ + 1];
  int *count = new int[numberRows_];
  CoinZeroN(count, numberRows_);
  CoinZeroN(used, numberRows_);
  int iRow;
  sizeFactor_ = 0;
  permute_ = new int[numberRows_];
  for (iRow = 0; iRow < numberRows_; iRow++)
    permute_[iRow] = iRow;
  if (!doKKT_) {
    int numberDense = 0;
    if (denseThreshold_ > 0) {
      delete[] whichDense_;
      delete[] denseColumn_;
      delete dense_;
      whichDense_ = new char[numberColumns];
      int iColumn;
      used[numberRows_] = 0;
      for (iColumn = 0; iColumn < numberColumns; iColumn++) {
        int length = columnLength[iColumn];
        used[length] += 1;
      }
      int nLong = 0;
      int stop = CoinMax(denseThreshold_ / 2, 100);
      for (iRow = numberRows_; iRow >= stop; iRow--) {
        if (used[iRow])
          COIN_DETAIL_PRINT(printf("%d columns are of length %d\n", used[iRow], iRow));
        nLong += used[iRow];
        if (nLong > 50 || nLong > (numberColumns >> 2))
          break;
      }
      CoinZeroN(used, numberRows_);
      for (iColumn = 0; iColumn < numberColumns; iColumn++) {
        if (columnLength[iColumn] < denseThreshold_) {
          whichDense_[iColumn] = 0;
        } else {
          whichDense_[iColumn] = 1;
          numberDense++;
        }
      }
      if (!numberDense || numberDense > 100) {
        // free
        delete[] whichDense_;
        whichDense_ = NULL;
        denseColumn_ = NULL;
        dense_ = NULL;
      } else {
        // space for dense columns
        denseColumn_ = new longDouble[numberDense * numberRows_];
        // dense cholesky
        dense_ = new ClpCholeskyDense();
        dense_->reserveSpace(NULL, numberDense);
        COIN_DETAIL_PRINT(printf("Taking %d columns as dense\n", numberDense));
      }
    }
    /*
             Get row counts and size
          */
    for (iRow = 0; iRow < numberRows_; iRow++) {
      int number = 1;
      // make sure diagonal exists
      which[0] = iRow;
      used[iRow] = 1;
      CoinBigIndex startRow = rowStart[iRow];
      CoinBigIndex endRow = rowStart[iRow] + rowLength[iRow];
      for (CoinBigIndex k = startRow; k < endRow; k++) {
        int iColumn = column[k];
        if (!whichDense_ || !whichDense_[iColumn]) {
          CoinBigIndex start = columnStart[iColumn];
          CoinBigIndex end = columnStart[iColumn] + columnLength[iColumn];
          for (CoinBigIndex j = start; j < end; j++) {
            int jRow = row[j];
            if (jRow < iRow) {
              if (!used[jRow]) {
                used[jRow] = 1;
                which[number++] = jRow;
                count[jRow]++;
              }
            }
          }
        }
      }
      sizeFactor_ += number;
      count[iRow] += number;
      int j;
      for (j = 0; j < number; j++)
        used[which[j]] = 0;
    }
    CoinSort_2(count, count + numberRows_, permute_);
  } else {
    // KKT
    CoinBigIndex numberElements = model_->clpMatrix()->getNumElements();
    numberElements = numberElements + 2 * numberRowsModel + numberTotal;
    if (quadratic)
      numberElements += quadratic->getNumElements();
    // off diagonal
    numberElements -= numberRows_;
    sizeFactor_ = static_cast< int >(numberElements);
    // If we sort we need to redo symbolic etc
  }
  delete[] which;
  delete[] used;
  delete[] count;
  permuteInverse_ = new int[numberRows_];
  for (iRow = 0; iRow < numberRows_; iRow++) {
    //permute_[iRow]=iRow; // force no permute
    //permute_[iRow]=numberRows_-1-iRow; // force odd permute
    //permute_[iRow]=(iRow+1)%numberRows_; // force odd permute
    permuteInverse_[permute_[iRow]] = iRow;
  }
  return 0;
}
#if BASE_ORDER == 1
/* Orders rows and saves pointer to matrix.and model */
int ClpCholeskyBase::orderAMD()
{
  permuteInverse_ = new int[numberRows_];
  permute_ = new int[numberRows_];
  // Do ordering
  int returnCode = 0;
  // get more space and full matrix
  int space = 6 * sizeFactor_ + 100000;
  int *temp = new int[space];
  int *which = new int[2 * numberRows_];
  CoinBigIndex *tempStart = new CoinBigIndex[numberRows_ + 1];
  memset(which, 0, numberRows_ * sizeof(int));
  for (int iRow = 0; iRow < numberRows_; iRow++) {
    which[iRow] += choleskyStart_[iRow + 1] - choleskyStart_[iRow] - 1;
    for (CoinBigIndex j = choleskyStart_[iRow] + 1; j < choleskyStart_[iRow + 1]; j++) {
      int jRow = choleskyRow_[j];
      which[jRow]++;
    }
  }
  CoinBigIndex sizeFactor = 0;
  for (int iRow = 0; iRow < numberRows_; iRow++) {
    int length = which[iRow];
    permute_[iRow] = length;
    tempStart[iRow] = sizeFactor;
    which[iRow] = sizeFactor;
    sizeFactor += length;
  }
  for (int iRow = 0; iRow < numberRows_; iRow++) {
    assert(choleskyRow_[choleskyStart_[iRow]] == iRow);
    for (CoinBigIndex j = choleskyStart_[iRow] + 1; j < choleskyStart_[iRow + 1]; j++) {
      int jRow = choleskyRow_[j];
      int put = which[iRow];
      temp[put] = jRow;
      which[iRow]++;
      put = which[jRow];
      temp[put] = iRow;
      which[jRow]++;
    }
  }
  for (int iRow = 1; iRow < numberRows_; iRow++)
    assert(which[iRow - 1] == tempStart[iRow]);
  CoinBigIndex lastSpace = sizeFactor;
  delete[] choleskyRow_;
  choleskyRow_ = temp;
  delete[] choleskyStart_;
  choleskyStart_ = tempStart;
  // linked lists of sizes and lengths
  int *first = new int[numberRows_];
  int *next = new int[numberRows_];
  int *previous = new int[numberRows_];
  char *mark = new char[numberRows_];
  memset(mark, 0, numberRows_);
  CoinBigIndex *sort = new CoinBigIndex[numberRows_];
  int *order = new int[numberRows_];
  for (int iRow = 0; iRow < numberRows_; iRow++) {
    first[iRow] = -1;
    next[iRow] = -1;
    previous[iRow] = -1;
    permuteInverse_[iRow] = -1;
  }
  int large = 1000 + 2 * numberRows_;
  for (int iRow = 0; iRow < numberRows_; iRow++) {
    int n = permute_[iRow];
    if (first[n] < 0) {
      first[n] = iRow;
      previous[iRow] = n + large;
      next[iRow] = n + 2 * large;
    } else {
      int k = first[n];
      assert(k < numberRows_);
      first[n] = iRow;
      previous[iRow] = n + large;
      assert(previous[k] == n + large);
      next[iRow] = k;
      previous[k] = iRow;
    }
  }
  int smallest = 0;
  int done = 0;
  int numberCompressions = 0;
  int numberExpansions = 0;
  while (smallest < numberRows_) {
    if (first[smallest] < 0 || first[smallest] > numberRows_) {
      smallest++;
      continue;
    }
    int iRow = first[smallest];
    if (false) {
      int kRow = -1;
      int ss = 999999;
      for (int jRow = numberRows_ - 1; jRow >= 0; jRow--) {
        if (permuteInverse_[jRow] < 0) {
          int length = permute_[jRow];
          assert(length > 0);
          for (CoinBigIndex j = choleskyStart_[jRow];
               j < choleskyStart_[jRow] + length; j++) {
            int jjRow = choleskyRow_[j];
            assert(permuteInverse_[jjRow] < 0);
          }
          if (length < ss) {
            ss = length;
            kRow = jRow;
          }
        }
      }
      assert(smallest == ss);
      printf("list chose %d - full chose %d - length %d\n",
        iRow, kRow, ss);
    }
    int kNext = next[iRow];
    first[smallest] = kNext;
    if (kNext < numberRows_)
      previous[kNext] = previous[iRow];
    previous[iRow] = -1;
    next[iRow] = -1;
    permuteInverse_[iRow] = done;
    done++;
    // Now add edges
    CoinBigIndex start = choleskyStart_[iRow];
    CoinBigIndex end = choleskyStart_[iRow] + permute_[iRow];
    int nSave = 0;
    for (CoinBigIndex k = start; k < end; k++) {
      int kRow = choleskyRow_[k];
      assert(permuteInverse_[kRow] < 0);
      //if (permuteInverse_[kRow]<0)
      which[nSave++] = kRow;
    }
    for (int i = 0; i < nSave; i++) {
      int kRow = which[i];
      int length = permute_[kRow];
      CoinBigIndex start = choleskyStart_[kRow];
      CoinBigIndex end = choleskyStart_[kRow] + length;
      for (CoinBigIndex j = start; j < end; j++) {
        int jRow = choleskyRow_[j];
        mark[jRow] = 1;
      }
      mark[kRow] = 1;
      int n = nSave;
      for (int j = 0; j < nSave; j++) {
        int jRow = which[j];
        if (!mark[jRow]) {
          which[n++] = jRow;
        }
      }
      for (CoinBigIndex j = start; j < end; j++) {
        int jRow = choleskyRow_[j];
        mark[jRow] = 0;
      }
      mark[kRow] = 0;
      if (n > nSave) {
        bool inPlace = (n - nSave == 1);
        if (!inPlace) {
          // extend
          int length = n - nSave + end - start;
          if (length + lastSpace > space) {
            // need to compress
            numberCompressions++;
            int newN = 0;
            for (int iRow = 0; iRow < numberRows_; iRow++) {
              CoinBigIndex start = choleskyStart_[iRow];
              if (permuteInverse_[iRow] < 0) {
                sort[newN] = start;
                order[newN++] = iRow;
              } else {
                choleskyStart_[iRow] = -1;
                permute_[iRow] = 0;
              }
            }
            CoinSort_2(sort, sort + newN, order);
            sizeFactor = 0;
            for (int k = 0; k < newN; k++) {
              int iRow = order[k];
              int length = permute_[iRow];
              CoinBigIndex start = choleskyStart_[iRow];
              choleskyStart_[iRow] = sizeFactor;
              for (int j = 0; j < length; j++)
                choleskyRow_[sizeFactor + j] = choleskyRow_[start + j];
              sizeFactor += length;
            }
            lastSpace = sizeFactor;
            if ((sizeFactor + numberRows_ + 1000) * 4 > 3 * space) {
              // need to expand
              numberExpansions++;
              space = (3 * space) / 2;
              int *temp = new int[space];
              memcpy(temp, choleskyRow_, sizeFactor * sizeof(int));
              delete[] choleskyRow_;
              choleskyRow_ = temp;
            }
          }
        }
        // Now add
        start = choleskyStart_[kRow];
        end = choleskyStart_[kRow] + permute_[kRow];
        if (!inPlace)
          choleskyStart_[kRow] = lastSpace;
        CoinBigIndex put = choleskyStart_[kRow];
        for (CoinBigIndex j = start; j < end; j++) {
          int jRow = choleskyRow_[j];
          if (permuteInverse_[jRow] < 0)
            choleskyRow_[put++] = jRow;
        }
        for (int j = nSave; j < n; j++) {
          int jRow = which[j];
          choleskyRow_[put++] = jRow;
        }
        if (!inPlace) {
          permute_[kRow] = put - lastSpace;
          lastSpace = put;
        } else {
          permute_[kRow] = put - choleskyStart_[kRow];
        }
      } else {
        // pack down for new counts
        CoinBigIndex put = start;
        for (CoinBigIndex j = start; j < end; j++) {
          int jRow = choleskyRow_[j];
          if (permuteInverse_[jRow] < 0)
            choleskyRow_[put++] = jRow;
        }
        permute_[kRow] = put - start;
      }
      // take out
      int iNext = next[kRow];
      int iPrevious = previous[kRow];
      if (iPrevious < numberRows_) {
        next[iPrevious] = iNext;
      } else {
        assert(iPrevious == length + large);
        first[length] = iNext;
      }
      if (iNext < numberRows_) {
        previous[iNext] = iPrevious;
      } else {
        assert(iNext == length + 2 * large);
      }
      // put in
      length = permute_[kRow];
      smallest = CoinMin(smallest, length);
      if (first[length] < 0 || first[length] > numberRows_) {
        first[length] = kRow;
        previous[kRow] = length + large;
        next[kRow] = length + 2 * large;
      } else {
        int k = first[length];
        assert(k < numberRows_);
        first[length] = kRow;
        previous[kRow] = length + large;
        assert(previous[k] == length + large);
        next[kRow] = k;
        previous[k] = kRow;
      }
    }
  }
  // do rest
  for (int iRow = 0; iRow < numberRows_; iRow++) {
    if (permuteInverse_[iRow] < 0)
      permuteInverse_[iRow] = done++;
  }
  COIN_DETAIL_PRINT(printf("%d compressions, %d expansions\n",
    numberCompressions, numberExpansions));
  assert(done == numberRows_);
  delete[] sort;
  delete[] order;
  delete[] which;
  delete[] mark;
  delete[] first;
  delete[] next;
  delete[] previous;
  delete[] choleskyRow_;
  choleskyRow_ = NULL;
  delete[] choleskyStart_;
  choleskyStart_ = NULL;
#ifndef NDEBUG
  for (int iRow = 0; iRow < numberRows_; iRow++) {
    permute_[iRow] = -1;
  }
#endif
  for (int iRow = 0; iRow < numberRows_; iRow++) {
    permute_[permuteInverse_[iRow]] = iRow;
  }
#ifndef NDEBUG
  for (int iRow = 0; iRow < numberRows_; iRow++) {
    assert(permute_[iRow] >= 0 && permute_[iRow] < numberRows_);
  }
#endif
  return returnCode;
}
#elif BASE_ORDER == 2
/*----------------------------------------------------------------------------*/
/*	(C) Copyright IBM Corporation 1997, 2009.  All Rights Reserved.       */
/*----------------------------------------------------------------------------*/

/*  A compact no-frills Approximate Minimum Local Fill ordering code.

    References:

[1] Ordering Sparse Matrices Using Approximate Minimum Local Fill.
    Edward Rothberg, SGI Manuscript, April 1996.
[2] An Approximate Minimum Degree Ordering Algorithm.
    T. Davis, P. Amestoy, and I. Duff, TR-94-039, CIS Department,
    University of Florida, December 1994.
*/

#include <math.h>
#include <stdlib.h>

typedef int WSI;

#define NORDTHRESH 7
#define MAXIW 2147000000

//#define WSSMP_DBG
#ifdef WSSMP_DBG
static void chk(WSI ind, WSI i, WSI lim)
{

  if (i <= 0 || i > lim) {
    printf("%d: chk: myamlf: WAR**: i, lim = %d, %d\n", ind, i, lim);
  }
}
#endif

static void
myamlf(WSI n, WSI xadj[], WSI adjncy[], WSI dgree[], WSI varbl[],
  WSI snxt[], WSI perm[], WSI invp[], WSI head[], WSI lsize[],
  WSI flag[], WSI erscore[], WSI locaux, WSI adjln, WSI speed)
{
  WSI l, i, j, k;
  double x, y;
  WSI maxmum, fltag, nodeg, scln, nm1, deg, tn,
    /*locatns,*/ ipp, jpp, nnode, numpiv, node,
    nodeln, currloc, counter, numii, mindeg,
    i0, i1, i2, i4, i5, i6, i7, i9,
    j0, j1, j2, j3, j4, j5, j6, j7, j8, j9;
  /*                                             n cannot be less than NORDTHRESH
      if (n < 3) {
         if (n > 0) perm[0] = invp[0] = 1;
         if (n > 1) perm[1] = invp[1] = 2;
         return;
      }
     */
#ifdef WSSMP_DBG
  printf("Myamlf: n, locaux, adjln, speed = %d,%d,%d,%d\n", n, locaux, adjln, speed);
  for (i = 0; i < n; i++)
    flag[i] = 0;
  k = xadj[0];
  for (i = 1; i <= n; i++) {
    j = k + dgree[i - 1];
    if (j < k || k < 1)
      printf("ERR**: myamlf: i, j, k = %d,%d,%d\n", i, j, k);
    for (l = k; l < j; l++) {
      if (adjncy[l - 1] < 1 || adjncy[l - 1] > n || adjncy[l - 1] == i)
        printf("ERR**: myamlf: i, l, adjj[l] = %d,%d,%d\n", i, l, adjncy[l - 1]);
      if (flag[adjncy[l - 1] - 1] == i)
        printf("ERR**: myamlf: duplicate entry: %d - %d\n", i, adjncy[l - 1]);
      flag[adjncy[l - 1] - 1] = i;
      nm1 = adjncy[l - 1] - 1;
      for (fltag = xadj[nm1]; fltag < xadj[nm1] + dgree[nm1]; fltag++) {
        if (adjncy[fltag - 1] == i)
          goto L100;
      }
      printf("ERR**: Unsymmetric %d %d\n", i, fltag);
    L100:;
    }
    k = j;
    if (k > locaux)
      printf("ERR**: i, j, k, locaux = %d, %d, %d, %d\n",
        i, j, k, locaux);
  }
  if (n * (n - 1) < locaux - 1)
    printf("ERR**: myamlf: too many nnz, n, ne = %d, %d\n",
      n, adjln);
  for (i = 0; i < n; i++)
    flag[i] = 1;
  if (n > 10000)
    printf("Finished error checking in AMF\n");
  for (i = locaux; i <= adjln; i++)
    adjncy[i - 1] = -22;
#endif

  maxmum = 0;
  mindeg = 1;
  fltag = 2;
  //locatns = locaux - 1;
  nm1 = n - 1;
  counter = 1;
  for (l = 0; l < n; l++) {
    j = erscore[l];
#ifdef WSSMP_DBG
    chk(1, j, n);
#endif
    if (j > 0) {
      nnode = head[j - 1];
      if (nnode)
        perm[nnode - 1] = l + 1;
      snxt[l] = nnode;
      head[j - 1] = l + 1;
    } else {
      invp[l] = -(counter++);
      flag[l] = xadj[l] = 0;
    }
  }
  while (counter <= n) {
    for (deg = mindeg; head[deg - 1] < 1; deg++) {
    };
    nodeg = 0;
#ifdef WSSMP_DBG
    chk(2, deg, n - 1);
#endif
    node = head[deg - 1];
#ifdef WSSMP_DBG
    chk(3, node, n);
#endif
    mindeg = deg;
    nnode = snxt[node - 1];
    if (nnode)
      perm[nnode - 1] = 0;
    head[deg - 1] = nnode;
    nodeln = invp[node - 1];
    numpiv = varbl[node - 1];
    invp[node - 1] = -counter;
    counter += numpiv;
    varbl[node - 1] = -numpiv;
    if (nodeln) {
      j4 = locaux;
      i5 = lsize[node - 1] - nodeln;
      i2 = nodeln + 1;
      l = xadj[node - 1];
      for (i6 = 1; i6 <= i2; ++i6) {
        if (i6 == i2) {
          tn = node, i0 = l, scln = i5;
        } else {
#ifdef WSSMP_DBG
          chk(4, l, adjln);
#endif
          tn = adjncy[l - 1];
          l++;
#ifdef WSSMP_DBG
          chk(5, tn, n);
#endif
          i0 = xadj[tn - 1];
          scln = lsize[tn - 1];
        }
        for (i7 = 1; i7 <= scln; ++i7) {
#ifdef WSSMP_DBG
          chk(6, i0, adjln);
#endif
          i = adjncy[i0 - 1];
          i0++;
#ifdef WSSMP_DBG
          chk(7, i, n);
#endif
          numii = varbl[i - 1];
          if (numii > 0) {
            if (locaux > adjln) {
              lsize[node - 1] -= i6;
              xadj[node - 1] = l;
              if (!lsize[node - 1])
                xadj[node - 1] = 0;
              xadj[tn - 1] = i0;
              lsize[tn - 1] = scln - i7;
              if (!lsize[tn - 1])
                xadj[tn - 1] = 0;
              for (j = 1; j <= n; j++) {
                i4 = xadj[j - 1];
                if (i4 > 0) {
                  xadj[j - 1] = adjncy[i4 - 1];
#ifdef WSSMP_DBG
                  chk(8, i4, adjln);
#endif
                  adjncy[i4 - 1] = -j;
                }
              }
              i9 = j4 - 1;
              j6 = j7 = 1;
              while (j6 <= i9) {
#ifdef WSSMP_DBG
                chk(9, j6, adjln);
#endif
                j = -adjncy[j6 - 1];
                j6++;
                if (j > 0) {
#ifdef WSSMP_DBG
                  chk(10, j7, adjln);
                  chk(11, j, n);
#endif
                  adjncy[j7 - 1] = xadj[j - 1];
                  xadj[j - 1] = j7++;
                  j8 = lsize[j - 1] - 1 + j7;
                  for (; j7 < j8; j7++, j6++)
                    adjncy[j7 - 1] = adjncy[j6 - 1];
                }
              }
              for (j0 = j7; j4 < locaux; j4++, j7++) {
#ifdef WSSMP_DBG
                chk(12, j4, adjln);
#endif
                adjncy[j7 - 1] = adjncy[j4 - 1];
              }
              j4 = j0;
              locaux = j7;
              i0 = xadj[tn - 1];
              l = xadj[node - 1];
            }
            adjncy[locaux - 1] = i;
            locaux++;
            varbl[i - 1] = -numii;
            nodeg += numii;
            ipp = perm[i - 1];
            nnode = snxt[i - 1];
#ifdef WSSMP_DBG
            if (ipp)
              chk(13, ipp, n);
            if (nnode)
              chk(14, nnode, n);
#endif
            if (ipp)
              snxt[ipp - 1] = nnode;
            else
              head[erscore[i - 1] - 1] = nnode;
            if (nnode)
              perm[nnode - 1] = ipp;
          }
        }
        if (tn != node) {
          flag[tn - 1] = 0, xadj[tn - 1] = -node;
        }
      }
      currloc = (j5 = locaux) - j4;
      //locatns += currloc;
    } else {
      i1 = (j4 = xadj[node - 1]) + lsize[node - 1];
      for (j = j5 = j4; j < i1; j++) {
#ifdef WSSMP_DBG
        chk(15, j, adjln);
#endif
        i = adjncy[j - 1];
#ifdef WSSMP_DBG
        chk(16, i, n);
#endif
        numii = varbl[i - 1];
        if (numii > 0) {
          nodeg += numii;
          varbl[i - 1] = -numii;
#ifdef WSSMP_DBG
          chk(17, j5, adjln);
#endif
          adjncy[j5 - 1] = i;
          ipp = perm[i - 1];
          nnode = snxt[i - 1];
          j5++;
#ifdef WSSMP_DBG
          if (ipp)
            chk(18, ipp, n);
          if (nnode)
            chk(19, nnode, n);
#endif
          if (ipp)
            snxt[ipp - 1] = nnode;
          else
            head[erscore[i - 1] - 1] = nnode;
          if (nnode)
            perm[nnode - 1] = ipp;
        }
      }
      currloc = 0;
    }
#ifdef WSSMP_DBG
    chk(20, node, n);
#endif
    lsize[node - 1] = j5 - (xadj[node - 1] = j4);
    dgree[node - 1] = nodeg;
    if (fltag + n < 0 || fltag + n > MAXIW) {
      for (i = 1; i <= n; i++)
        if (flag[i - 1])
          flag[i - 1] = 1;
      fltag = 2;
    }
    for (j3 = j4; j3 < j5; j3++) {
#ifdef WSSMP_DBG
      chk(21, j3, adjln);
#endif
      i = adjncy[j3 - 1];
#ifdef WSSMP_DBG
      chk(22, i, n);
#endif
      j = invp[i - 1];
      if (j > 0) {
        i4 = fltag - (numii = -varbl[i - 1]);
        i2 = xadj[i - 1] + j;
        for (l = xadj[i - 1]; l < i2; l++) {
#ifdef WSSMP_DBG
          chk(23, l, adjln);
#endif
          tn = adjncy[l - 1];
#ifdef WSSMP_DBG
          chk(24, tn, n);
#endif
          j9 = flag[tn - 1];
          if (j9 >= fltag)
            j9 -= numii;
          else if (j9)
            j9 = dgree[tn - 1] + i4;
          flag[tn - 1] = j9;
        }
      }
    }
    for (j3 = j4; j3 < j5; j3++) {
#ifdef WSSMP_DBG
      chk(25, j3, adjln);
#endif
      i = adjncy[j3 - 1];
      i5 = deg = 0;
#ifdef WSSMP_DBG
      chk(26, i, n);
#endif
      j1 = (i4 = xadj[i - 1]) + invp[i - 1];
      for (l = j0 = i4; l < j1; l++) {
#ifdef WSSMP_DBG
        chk(27, l, adjln);
#endif
        tn = adjncy[l - 1];
#ifdef WSSMP_DBG
        chk(70, tn, n);
#endif
        j8 = flag[tn - 1];
        if (j8) {
          deg += (j8 - fltag);
#ifdef WSSMP_DBG
          chk(28, i4, adjln);
#endif
          adjncy[i4 - 1] = tn;
          i5 += tn;
          i4++;
          while (i5 >= nm1)
            i5 -= nm1;
        }
      }
      invp[i - 1] = (j2 = i4) - j0 + 1;
      i2 = j0 + lsize[i - 1];
      for (l = j1; l < i2; l++) {
#ifdef WSSMP_DBG
        chk(29, l, adjln);
#endif
        j = adjncy[l - 1];
#ifdef WSSMP_DBG
        chk(30, j, n);
#endif
        numii = varbl[j - 1];
        if (numii > 0) {
          deg += numii;
#ifdef WSSMP_DBG
          chk(31, i4, adjln);
#endif
          adjncy[i4 - 1] = j;
          i5 += j;
          i4++;
          while (i5 >= nm1)
            i5 -= nm1;
        }
      }
      if (invp[i - 1] == 1 && j2 == i4) {
        numii = -varbl[i - 1];
        xadj[i - 1] = -node;
        nodeg -= numii;
        counter += numii;
        numpiv += numii;
        invp[i - 1] = varbl[i - 1] = 0;
      } else {
        if (dgree[i - 1] > deg)
          dgree[i - 1] = deg;
#ifdef WSSMP_DBG
        chk(32, j2, adjln);
        chk(33, j0, adjln);
#endif
        adjncy[i4 - 1] = adjncy[j2 - 1];
        adjncy[j2 - 1] = adjncy[j0 - 1];
        adjncy[j0 - 1] = node;
        lsize[i - 1] = i4 - j0 + 1;
        i5++;
#ifdef WSSMP_DBG
        chk(35, i5, n);
#endif
        j = head[i5 - 1];
        if (j > 0) {
          snxt[i - 1] = perm[j - 1];
          perm[j - 1] = i;
        } else {
          snxt[i - 1] = -j;
          head[i5 - 1] = -i;
        }
        perm[i - 1] = i5;
      }
    }
#ifdef WSSMP_DBG
    chk(36, node, n);
#endif
    dgree[node - 1] = nodeg;
    if (maxmum < nodeg)
      maxmum = nodeg;
    fltag += maxmum;
#ifdef WSSMP_DBG
    if (fltag + n + 1 < 0)
      printf("ERR**: fltag = %d (A)\n", fltag);
#endif
    for (j3 = j4; j3 < j5; ++j3) {
#ifdef WSSMP_DBG
      chk(37, j3, adjln);
#endif
      i = adjncy[j3 - 1];
#ifdef WSSMP_DBG
      chk(38, i, n);
#endif
      if (varbl[i - 1] < 0) {
        i5 = perm[i - 1];
#ifdef WSSMP_DBG
        chk(39, i5, n);
#endif
        j = head[i5 - 1];
        if (j) {
          if (j < 0) {
            head[i5 - 1] = 0, i = -j;
          } else {
#ifdef WSSMP_DBG
            chk(40, j, n);
#endif
            i = perm[j - 1];
            perm[j - 1] = 0;
          }
          while (i) {
#ifdef WSSMP_DBG
            chk(41, i, n);
#endif
            if (!snxt[i - 1]) {
              i = 0;
            } else {
              k = invp[i - 1];
              i2 = xadj[i - 1] + (scln = lsize[i - 1]);
              for (l = xadj[i - 1] + 1; l < i2; l++) {
#ifdef WSSMP_DBG
                chk(42, l, adjln);
                chk(43, adjncy[l - 1], n);
#endif
                flag[adjncy[l - 1] - 1] = fltag;
              }
              jpp = i;
              j = snxt[i - 1];
              while (j) {
#ifdef WSSMP_DBG
                chk(44, j, n);
#endif
                if (lsize[j - 1] == scln && invp[j - 1] == k) {
                  i2 = xadj[j - 1] + scln;
                  j8 = 1;
                  for (l = xadj[j - 1] + 1; l < i2; l++) {
#ifdef WSSMP_DBG
                    chk(45, l, adjln);
                    chk(46, adjncy[l - 1], n);
#endif
                    if (flag[adjncy[l - 1] - 1] != fltag) {
                      j8 = 0;
                      break;
                    }
                  }
                  if (j8) {
                    xadj[j - 1] = -i;
                    varbl[i - 1] += varbl[j - 1];
                    varbl[j - 1] = invp[j - 1] = 0;
#ifdef WSSMP_DBG
                    chk(47, j, n);
                    chk(48, jpp, n);
#endif
                    j = snxt[j - 1];
                    snxt[jpp - 1] = j;
                  } else {
                    jpp = j;
#ifdef WSSMP_DBG
                    chk(49, j, n);
#endif
                    j = snxt[j - 1];
                  }
                } else {
                  jpp = j;
#ifdef WSSMP_DBG
                  chk(50, j, n);
#endif
                  j = snxt[j - 1];
                }
              }
              fltag++;
#ifdef WSSMP_DBG
              if (fltag + n + 1 < 0)
                printf("ERR**: fltag = %d (B)\n", fltag);
#endif
#ifdef WSSMP_DBG
              chk(51, i, n);
#endif
              i = snxt[i - 1];
            }
          }
        }
      }
    }
    j8 = nm1 - counter;
    switch (speed) {
    case 1:
      for (j = j3 = j4; j3 < j5; j3++) {
#ifdef WSSMP_DBG
        chk(52, j3, adjln);
#endif
        i = adjncy[j3 - 1];
#ifdef WSSMP_DBG
        chk(53, i, n);
#endif
        numii = varbl[i - 1];
        if (numii < 0) {
          k = 0;
          i4 = (l = xadj[i - 1]) + invp[i - 1];
          for (; l < i4; l++) {
#ifdef WSSMP_DBG
            chk(54, l, adjln);
            chk(55, adjncy[l - 1], n);
#endif
            i5 = dgree[adjncy[l - 1] - 1];
            if (k < i5)
              k = i5;
          }
          x = static_cast< double >(k - 1);
          varbl[i - 1] = abs(numii);
          j9 = dgree[i - 1] + nodeg;
          deg = (j8 > j9 ? j9 : j8) + numii;
          if (deg < 1)
            deg = 1;
          y = static_cast< double >(deg);
          dgree[i - 1] = deg;
          /*
                                    printf("%i %f should >= %i %f\n",deg,y,k-1,x);
                                    if (y < x) printf("** \n"); else printf("\n");
                         */
          y = y * y - y;
          x = y - (x * x - x);
          if (x < 1.1)
            x = 1.1;
          deg = static_cast< WSI >(sqrt(x));
          /*         if (deg < 1) deg = 1; */
          erscore[i - 1] = deg;
#ifdef WSSMP_DBG
          chk(56, deg, n - 1);
#endif
          nnode = head[deg - 1];
          if (nnode)
            perm[nnode - 1] = i;
          snxt[i - 1] = nnode;
          perm[i - 1] = 0;
#ifdef WSSMP_DBG
          chk(57, j, adjln);
#endif
          head[deg - 1] = adjncy[j - 1] = i;
          j++;
          if (deg < mindeg)
            mindeg = deg;
        }
      }
      break;
    case 2:
      for (j = j3 = j4; j3 < j5; j3++) {
#ifdef WSSMP_DBG
        chk(58, j3, adjln);
#endif
        i = adjncy[j3 - 1];
#ifdef WSSMP_DBG
        chk(59, i, n);
#endif
        numii = varbl[i - 1];
        if (numii < 0) {
          x = static_cast< double >(dgree[adjncy[xadj[i - 1] - 1] - 1] - 1);
          varbl[i - 1] = abs(numii);
          j9 = dgree[i - 1] + nodeg;
          deg = (j8 > j9 ? j9 : j8) + numii;
          if (deg < 1)
            deg = 1;
          y = static_cast< double >(deg);
          dgree[i - 1] = deg;
          /*
                                    printf("%i %f should >= %i %f",deg,y,dgree[adjncy[xadj[i - 1] - 1] - 1]-1,x);
                                    if (y < x) printf("** \n"); else printf("\n");
                         */
          y = y * y - y;
          x = y - (x * x - x);
          if (x < 1.1)
            x = 1.1;
          deg = static_cast< WSI >(sqrt(x));
          /*         if (deg < 1) deg = 1; */
          erscore[i - 1] = deg;
#ifdef WSSMP_DBG
          chk(60, deg, n - 1);
#endif
          nnode = head[deg - 1];
          if (nnode)
            perm[nnode - 1] = i;
          snxt[i - 1] = nnode;
          perm[i - 1] = 0;
#ifdef WSSMP_DBG
          chk(61, j, adjln);
#endif
          head[deg - 1] = adjncy[j - 1] = i;
          j++;
          if (deg < mindeg)
            mindeg = deg;
        }
      }
      break;
    default:
      for (j = j3 = j4; j3 < j5; j3++) {
#ifdef WSSMP_DBG
        chk(62, j3, adjln);
#endif
        i = adjncy[j3 - 1];
#ifdef WSSMP_DBG
        chk(63, i, n);
#endif
        numii = varbl[i - 1];
        if (numii < 0) {
          varbl[i - 1] = abs(numii);
          j9 = dgree[i - 1] + nodeg;
          deg = (j8 > j9 ? j9 : j8) + numii;
          if (deg < 1)
            deg = 1;
          dgree[i - 1] = deg;
#ifdef WSSMP_DBG
          chk(64, deg, n - 1);
#endif
          nnode = head[deg - 1];
          if (nnode)
            perm[nnode - 1] = i;
          snxt[i - 1] = nnode;
          perm[i - 1] = 0;
#ifdef WSSMP_DBG
          chk(65, j, adjln);
#endif
          head[deg - 1] = adjncy[j - 1] = i;
          j++;
          if (deg < mindeg)
            mindeg = deg;
        }
      }
      break;
    }
    if (currloc) {
#ifdef WSSMP_DBG
      chk(66, node, n);
#endif
      //locatns += (lsize[node - 1] - currloc);
      locaux = j;
    }
    varbl[node - 1] = numpiv + nodeg;
    lsize[node - 1] = j - j4;
    if (!lsize[node - 1])
      flag[node - 1] = xadj[node - 1] = 0;
  }
  for (l = 1; l <= n; l++) {
    if (!invp[l - 1]) {
      for (i = -xadj[l - 1]; invp[i - 1] >= 0; i = -xadj[i - 1]) {
      };
      tn = i;
#ifdef WSSMP_DBG
      chk(67, tn, n);
#endif
      k = -invp[tn - 1];
      i = l;
#ifdef WSSMP_DBG
      chk(68, i, n);
#endif
      while (invp[i - 1] >= 0) {
        nnode = -xadj[i - 1];
        xadj[i - 1] = -tn;
        if (!invp[i - 1])
          invp[i - 1] = k++;
        i = nnode;
      }
      invp[tn - 1] = -k;
    }
  }
  for (l = 0; l < n; l++) {
    i = abs(invp[l]);
#ifdef WSSMP_DBG
    chk(69, i, n);
#endif
    invp[l] = i;
    perm[i - 1] = l + 1;
  }
  return;
}
// This code is not needed, but left in in case needed sometime
#if 0
/*C--------------------------------------------------------------------------*/
void amlfdr(WSI *n, WSI xadj[], WSI adjncy[], WSI dgree[], WSI *adjln,
            WSI *locaux, WSI varbl[], WSI snxt[], WSI perm[],
            WSI head[], WSI invp[], WSI lsize[], WSI flag[], WSI *ispeed)
{
     WSI nn, nlocaux, nadjln, speed, i, j, mx, mxj, *erscore;

#ifdef WSSMP_DBG
     printf("Calling amlfdr for n, speed = %d, %d\n", *n, *ispeed);
#endif

     if ((nn = *n) == 0) return;

#ifdef WSSMP_DBG
     if (nn == 31) {
          printf("n = %d; adjln = %d; locaux = %d; ispeed = %d\n",
                 *n, *adjln, *locaux, *ispeed);
     }
#endif

     if (nn < NORDTHRESH) {
          for (i = 0; i < nn; i++) lsize[i] = i;
          for (i = nn; i > 0; i--) {
               mx = dgree[0];
               mxj = 0;
               for (j = 1; j < i; j++)
                    if (dgree[j] > mx) {
                         mx = dgree[j];
                         mxj = j;
                    }
               invp[lsize[mxj]] = i;
               dgree[mxj] = dgree[i-1];
               lsize[mxj] = lsize[i-1];
          }
          return;
     }
     speed = *ispeed;
     if (speed < 3) {
          /*
              erscore = (WSI *)malloc(nn * sizeof(WSI));
              if (erscore == NULL) speed = 3;
          */
          wscmal (&nn, &i, &erscore);
          if (i != 0) speed = 3;
     }
     if (speed > 2) erscore = dgree;
     if (speed < 3) {
          for (i = 0; i < nn; i++) {
               perm[i] = 0;
               invp[i] = 0;
               head[i] = 0;
               flag[i] = 1;
               varbl[i] = 1;
               lsize[i] = dgree[i];
               erscore[i] = dgree[i];
          }
     } else {
          for (i = 0; i < nn; i++) {
               perm[i] = 0;
               invp[i] = 0;
               head[i] = 0;
               flag[i] = 1;
               varbl[i] = 1;
               lsize[i] = dgree[i];
          }
     }
     nlocaux = *locaux;
     nadjln = *adjln;

     myamlf(nn, xadj, adjncy, dgree, varbl, snxt, perm, invp,
            head, lsize, flag, erscore, nlocaux, nadjln, speed);
     /*
       if (speed < 3) free(erscore);
     */
     if (speed < 3) wscfr(&erscore);
     return;
}
#endif // end of taking out amlfdr
/*C--------------------------------------------------------------------------*/
#endif
// Orders rows
int ClpCholeskyBase::orderAMD()
{
  permuteInverse_ = new int[numberRows_];
  permute_ = new int[numberRows_];
  // Do ordering
  int returnCode = 0;
  // get full matrix
  int space = 2 * sizeFactor_ + 10000 + 4 * numberRows_;
  int *temp = new int[space];
  int *count = new int[numberRows_];
  int *tempStart = new int[numberRows_ + 1];
  memset(count, 0, numberRows_ * sizeof(int));
  for (int iRow = 0; iRow < numberRows_; iRow++) {
    count[iRow] += static_cast< int >(choleskyStart_[iRow + 1] - choleskyStart_[iRow] - 1);
    for (CoinBigIndex j = choleskyStart_[iRow] + 1; j < choleskyStart_[iRow + 1]; j++) {
      int jRow = choleskyRow_[j];
      count[jRow]++;
    }
  }
#define OFFSET 1
  int sizeFactor = 0;
  for (int iRow = 0; iRow < numberRows_; iRow++) {
    int length = count[iRow];
    permute_[iRow] = length;
    // add 1 to starts
    tempStart[iRow] = sizeFactor + OFFSET;
    count[iRow] = sizeFactor;
    sizeFactor += length;
  }
  tempStart[numberRows_] = sizeFactor + OFFSET;
  // add 1 to rows
  for (int iRow = 0; iRow < numberRows_; iRow++) {
    assert(choleskyRow_[choleskyStart_[iRow]] == iRow);
    for (CoinBigIndex j = choleskyStart_[iRow] + 1; j < choleskyStart_[iRow + 1]; j++) {
      int jRow = choleskyRow_[j];
      int put = count[iRow];
      temp[put] = jRow + OFFSET;
      count[iRow]++;
      put = count[jRow];
      temp[put] = iRow + OFFSET;
      count[jRow]++;
    }
  }
  for (int iRow = 1; iRow < numberRows_; iRow++)
    assert(count[iRow - 1] == tempStart[iRow] - OFFSET);
  delete[] choleskyRow_;
  choleskyRow_ = temp;
  delete[] choleskyStart_;
  choleskyStart_ = tempStart;
  int locaux = sizeFactor + OFFSET;
  delete[] count;
  int speed = integerParameters_[0];
  if (speed < 1 || speed > 2)
    speed = 3;
  int *use = new int[((speed < 3) ? 7 : 6) * numberRows_];
  int *dgree = use;
  int *varbl = dgree + numberRows_;
  int *snxt = varbl + numberRows_;
  int *head = snxt + numberRows_;
  int *lsize = head + numberRows_;
  int *flag = lsize + numberRows_;
  int *erscore;
  for (int i = 0; i < numberRows_; i++) {
    dgree[i] = choleskyStart_[i + 1] - choleskyStart_[i];
    head[i] = dgree[i];
    snxt[i] = 0;
    permute_[i] = 0;
    permuteInverse_[i] = 0;
    head[i] = 0;
    flag[i] = 1;
    varbl[i] = 1;
    lsize[i] = dgree[i];
  }
  if (speed < 3) {
    erscore = flag + numberRows_;
    for (int i = 0; i < numberRows_; i++)
      erscore[i] = dgree[i];
  } else {
    erscore = dgree;
  }
  myamlf(numberRows_, choleskyStart_, choleskyRow_,
    dgree, varbl, snxt, permute_, permuteInverse_,
    head, lsize, flag, erscore, locaux, space, speed);
  for (int iRow = 0; iRow < numberRows_; iRow++) {
    permute_[iRow]--;
  }
  for (int iRow = 0; iRow < numberRows_; iRow++) {
    permuteInverse_[permute_[iRow]] = iRow;
  }
  for (int iRow = 0; iRow < numberRows_; iRow++) {
    assert(permuteInverse_[iRow] >= 0 && permuteInverse_[iRow] < numberRows_);
  }
  delete[] use;
  delete[] choleskyRow_;
  choleskyRow_ = NULL;
  delete[] choleskyStart_;
  choleskyStart_ = NULL;
  return returnCode;
}
/* Does Symbolic factorization given permutation.
   This is called immediately after order.  If user provides this then
   user must provide factorize and solve.  Otherwise the default factorization is used
   returns non-zero if not enough memory */
int ClpCholeskyBase::symbolic()
{
  const CoinBigIndex *columnStart = model_->clpMatrix()->getVectorStarts();
  const int *columnLength = model_->clpMatrix()->getVectorLengths();
  const int *row = model_->clpMatrix()->getIndices();
  const CoinBigIndex *rowStart = rowCopy_->getVectorStarts();
  const int *rowLength = rowCopy_->getVectorLengths();
  const int *column = rowCopy_->getIndices();
  int numberRowsModel = model_->numberRows();
  int numberColumns = model_->numberColumns();
  int numberTotal = numberColumns + numberRowsModel;
  CoinPackedMatrix *quadratic = NULL;
  ClpQuadraticObjective *quadraticObj = (dynamic_cast< ClpQuadraticObjective * >(model_->objectiveAsObject()));
  if (quadraticObj)
    quadratic = quadraticObj->quadraticObjective();
  // We need an array for counts
  int *used = new int[numberRows_ + 1];
  // If KKT then re-order so negative first
  if (doKKT_) {
    int nn = 0;
    int np = 0;
    int iRow;
    for (iRow = 0; iRow < numberRows_; iRow++) {
      int originalRow = permute_[iRow];
      if (originalRow < numberTotal)
        permute_[nn++] = originalRow;
      else
        used[np++] = originalRow;
    }
    CoinMemcpyN(used, np, permute_ + nn);
    for (iRow = 0; iRow < numberRows_; iRow++)
      permuteInverse_[permute_[iRow]] = iRow;
  }
  CoinZeroN(used, numberRows_);
  int iRow;
  int iColumn;
  bool noMemory = false;
  int *Astart = new int[numberRows_ + 1];
  int *Arow = NULL;
  try {
    Arow = new int[sizeFactor_];
  } catch (...) {
    // no memory
    delete[] Astart;
    return -1;
  }
  choleskyStart_ = new int[numberRows_ + 1];
  link_ = new int[numberRows_];
  workInteger_ = new int[numberRows_];
  indexStart_ = new int[numberRows_];
  clique_ = new int[numberRows_];
  // Redo so permuted upper triangular
  sizeFactor_ = 0;
  int *which = Arow;
  if (!doKKT_) {
    for (iRow = 0; iRow < numberRows_; iRow++) {
      int number = 0;
      int iOriginalRow = permute_[iRow];
      Astart[iRow] = sizeFactor_;
      CoinBigIndex startRow = rowStart[iOriginalRow];
      CoinBigIndex endRow = rowStart[iOriginalRow] + rowLength[iOriginalRow];
      for (CoinBigIndex k = startRow; k < endRow; k++) {
        int iColumn = column[k];
        if (!whichDense_ || !whichDense_[iColumn]) {
          CoinBigIndex start = columnStart[iColumn];
          CoinBigIndex end = columnStart[iColumn] + columnLength[iColumn];
          for (CoinBigIndex j = start; j < end; j++) {
            int jRow = row[j];
            int jNewRow = permuteInverse_[jRow];
            if (jNewRow < iRow) {
              if (!used[jNewRow]) {
                used[jNewRow] = 1;
                which[number++] = jNewRow;
              }
            }
          }
        }
      }
      sizeFactor_ += number;
      int j;
      for (j = 0; j < number; j++)
        used[which[j]] = 0;
      // Sort
      std::sort(which, which + number);
      // move which on
      which += number;
    }
  } else {
    // KKT
    // transpose
    ClpMatrixBase *rowCopy = model_->clpMatrix()->reverseOrderedCopy();
    const CoinBigIndex *rowStart = rowCopy->getVectorStarts();
    const int *rowLength = rowCopy->getVectorLengths();
    const int *column = rowCopy->getIndices();
    // temp
    bool permuted = false;
    for (iRow = 0; iRow < numberRows_; iRow++) {
      if (permute_[iRow] != iRow) {
        permuted = true;
        break;
      }
    }
    if (permuted) {
      // Need to permute - ugly
      if (!quadratic) {
        for (iRow = 0; iRow < numberRows_; iRow++) {
          Astart[iRow] = sizeFactor_;
          int iOriginalRow = permute_[iRow];
          if (iOriginalRow < numberColumns) {
            // A may be upper triangular by mistake
            iColumn = iOriginalRow;
            CoinBigIndex start = columnStart[iColumn];
            CoinBigIndex end = columnStart[iColumn] + columnLength[iColumn];
            for (CoinBigIndex j = start; j < end; j++) {
              int kRow = row[j] + numberTotal;
              kRow = permuteInverse_[kRow];
              if (kRow < iRow)
                Arow[sizeFactor_++] = kRow;
            }
          } else if (iOriginalRow < numberTotal) {
            int kRow = permuteInverse_[iOriginalRow + numberRowsModel];
            if (kRow < iRow)
              Arow[sizeFactor_++] = kRow;
          } else {
            int kRow = iOriginalRow - numberTotal;
            CoinBigIndex start = rowStart[kRow];
            CoinBigIndex end = rowStart[kRow] + rowLength[kRow];
            for (CoinBigIndex j = start; j < end; j++) {
              int jRow = column[j];
              int jNewRow = permuteInverse_[jRow];
              if (jNewRow < iRow)
                Arow[sizeFactor_++] = jNewRow;
            }
            // slack - should it be permute
            kRow = permuteInverse_[kRow + numberColumns];
            if (kRow < iRow)
              Arow[sizeFactor_++] = kRow;
          }
          // Sort
          std::sort(Arow + Astart[iRow], Arow + sizeFactor_);
        }
      } else {
        // quadratic
        // transpose
        CoinPackedMatrix quadraticT;
        quadraticT.reverseOrderedCopyOf(*quadratic);
        const int *columnQuadratic = quadraticT.getIndices();
        const CoinBigIndex *columnQuadraticStart = quadraticT.getVectorStarts();
        const int *columnQuadraticLength = quadraticT.getVectorLengths();
        for (iRow = 0; iRow < numberRows_; iRow++) {
          Astart[iRow] = sizeFactor_;
          int iOriginalRow = permute_[iRow];
          if (iOriginalRow < numberColumns) {
            // Quadratic bit
            CoinBigIndex j;
            for (j = columnQuadraticStart[iOriginalRow];
                 j < columnQuadraticStart[iOriginalRow] + columnQuadraticLength[iOriginalRow]; j++) {
              int jColumn = columnQuadratic[j];
              int jNewColumn = permuteInverse_[jColumn];
              if (jNewColumn < iRow)
                Arow[sizeFactor_++] = jNewColumn;
            }
            // A may be upper triangular by mistake
            iColumn = iOriginalRow;
            CoinBigIndex start = columnStart[iColumn];
            CoinBigIndex end = columnStart[iColumn] + columnLength[iColumn];
            for (j = start; j < end; j++) {
              int kRow = row[j] + numberTotal;
              kRow = permuteInverse_[kRow];
              if (kRow < iRow)
                Arow[sizeFactor_++] = kRow;
            }
          } else if (iOriginalRow < numberTotal) {
            int kRow = permuteInverse_[iOriginalRow + numberRowsModel];
            if (kRow < iRow)
              Arow[sizeFactor_++] = kRow;
          } else {
            int kRow = iOriginalRow - numberTotal;
            CoinBigIndex start = rowStart[kRow];
            CoinBigIndex end = rowStart[kRow] + rowLength[kRow];
            for (CoinBigIndex j = start; j < end; j++) {
              int jRow = column[j];
              int jNewRow = permuteInverse_[jRow];
              if (jNewRow < iRow)
                Arow[sizeFactor_++] = jNewRow;
            }
            // slack - should it be permute
            kRow = permuteInverse_[kRow + numberColumns];
            if (kRow < iRow)
              Arow[sizeFactor_++] = kRow;
          }
          // Sort
          std::sort(Arow + Astart[iRow], Arow + sizeFactor_);
        }
      }
    } else {
      if (!quadratic) {
        for (iRow = 0; iRow < numberRows_; iRow++) {
          Astart[iRow] = sizeFactor_;
        }
      } else {
        // Quadratic
        // transpose
        CoinPackedMatrix quadraticT;
        quadraticT.reverseOrderedCopyOf(*quadratic);
        const int *columnQuadratic = quadraticT.getIndices();
        const CoinBigIndex *columnQuadraticStart = quadraticT.getVectorStarts();
        const int *columnQuadraticLength = quadraticT.getVectorLengths();
        //const double * quadraticElement = quadraticT.getElements();
        for (iRow = 0; iRow < numberColumns; iRow++) {
          int iOriginalRow = permute_[iRow];
          Astart[iRow] = sizeFactor_;
          for (CoinBigIndex j = columnQuadraticStart[iOriginalRow];
               j < columnQuadraticStart[iOriginalRow] + columnQuadraticLength[iOriginalRow]; j++) {
            int jColumn = columnQuadratic[j];
            int jNewColumn = permuteInverse_[jColumn];
            if (jNewColumn < iRow)
              Arow[sizeFactor_++] = jNewColumn;
          }
        }
      }
      int iRow;
      // slacks
      for (iRow = 0; iRow < numberRowsModel; iRow++) {
        Astart[iRow + numberColumns] = sizeFactor_;
      }
      for (iRow = 0; iRow < numberRowsModel; iRow++) {
        Astart[iRow + numberTotal] = sizeFactor_;
        CoinBigIndex start = rowStart[iRow];
        CoinBigIndex end = rowStart[iRow] + rowLength[iRow];
        for (CoinBigIndex j = start; j < end; j++) {
          Arow[sizeFactor_++] = column[j];
        }
        // slack
        Arow[sizeFactor_++] = numberColumns + iRow;
      }
    }
    delete rowCopy;
  }
  Astart[numberRows_] = sizeFactor_;
  firstDense_ = numberRows_;
  symbolic1(Astart, Arow);
  // Now fill in indices
  try {
    // too big
    choleskyRow_ = new int[sizeFactor_];
  } catch (...) {
    // no memory
    noMemory = true;
  }
  double sizeFactor = sizeFactor_;
  if (!noMemory) {
    // Do lower triangular
    sizeFactor_ = 0;
    int *which = Arow;
    if (!doKKT_) {
      for (iRow = 0; iRow < numberRows_; iRow++) {
        int number = 0;
        int iOriginalRow = permute_[iRow];
        Astart[iRow] = sizeFactor_;
        if (!rowsDropped_[iOriginalRow]) {
          CoinBigIndex startRow = rowStart[iOriginalRow];
          CoinBigIndex endRow = rowStart[iOriginalRow] + rowLength[iOriginalRow];
          for (CoinBigIndex k = startRow; k < endRow; k++) {
            int iColumn = column[k];
            if (!whichDense_ || !whichDense_[iColumn]) {
              CoinBigIndex start = columnStart[iColumn];
              CoinBigIndex end = columnStart[iColumn] + columnLength[iColumn];
              for (CoinBigIndex j = start; j < end; j++) {
                int jRow = row[j];
                int jNewRow = permuteInverse_[jRow];
                if (jNewRow > iRow && !rowsDropped_[jRow]) {
                  if (!used[jNewRow]) {
                    used[jNewRow] = 1;
                    which[number++] = jNewRow;
                  }
                }
              }
            }
          }
          sizeFactor_ += number;
          int j;
          for (j = 0; j < number; j++)
            used[which[j]] = 0;
          // Sort
          std::sort(which, which + number);
          // move which on
          which += number;
        }
      }
    } else {
      // KKT
      // temp
      bool permuted = false;
      for (iRow = 0; iRow < numberRows_; iRow++) {
        if (permute_[iRow] != iRow) {
          permuted = true;
          break;
        }
      }
      // but fake it
      for (iRow = 0; iRow < numberRows_; iRow++) {
        //permute_[iRow]=iRow; // force no permute
        //permuteInverse_[permute_[iRow]]=iRow;
      }
      if (permuted) {
        // Need to permute - ugly
        if (!quadratic) {
          for (iRow = 0; iRow < numberRows_; iRow++) {
            Astart[iRow] = sizeFactor_;
            int iOriginalRow = permute_[iRow];
            if (iOriginalRow < numberColumns) {
              iColumn = iOriginalRow;
              CoinBigIndex start = columnStart[iColumn];
              CoinBigIndex end = columnStart[iColumn] + columnLength[iColumn];
              for (CoinBigIndex j = start; j < end; j++) {
                int kRow = row[j] + numberTotal;
                kRow = permuteInverse_[kRow];
                if (kRow > iRow)
                  Arow[sizeFactor_++] = kRow;
              }
            } else if (iOriginalRow < numberTotal) {
              int kRow = permuteInverse_[iOriginalRow + numberRowsModel];
              if (kRow > iRow)
                Arow[sizeFactor_++] = kRow;
            } else {
              int kRow = iOriginalRow - numberTotal;
              CoinBigIndex start = rowStart[kRow];
              CoinBigIndex end = rowStart[kRow] + rowLength[kRow];
              for (CoinBigIndex j = start; j < end; j++) {
                int jRow = column[j];
                int jNewRow = permuteInverse_[jRow];
                if (jNewRow > iRow)
                  Arow[sizeFactor_++] = jNewRow;
              }
              // slack - should it be permute
              kRow = permuteInverse_[kRow + numberColumns];
              if (kRow > iRow)
                Arow[sizeFactor_++] = kRow;
            }
            // Sort
            std::sort(Arow + Astart[iRow], Arow + sizeFactor_);
          }
        } else {
          // quadratic
          const int *columnQuadratic = quadratic->getIndices();
          const CoinBigIndex *columnQuadraticStart = quadratic->getVectorStarts();
          const int *columnQuadraticLength = quadratic->getVectorLengths();
          for (iRow = 0; iRow < numberRows_; iRow++) {
            Astart[iRow] = sizeFactor_;
            int iOriginalRow = permute_[iRow];
            if (iOriginalRow < numberColumns) {
              // Quadratic bit
              CoinBigIndex j;
              for (j = columnQuadraticStart[iOriginalRow];
                   j < columnQuadraticStart[iOriginalRow] + columnQuadraticLength[iOriginalRow]; j++) {
                int jColumn = columnQuadratic[j];
                int jNewColumn = permuteInverse_[jColumn];
                if (jNewColumn > iRow)
                  Arow[sizeFactor_++] = jNewColumn;
              }
              iColumn = iOriginalRow;
              CoinBigIndex start = columnStart[iColumn];
              CoinBigIndex end = columnStart[iColumn] + columnLength[iColumn];
              for (j = start; j < end; j++) {
                int kRow = row[j] + numberTotal;
                kRow = permuteInverse_[kRow];
                if (kRow > iRow)
                  Arow[sizeFactor_++] = kRow;
              }
            } else if (iOriginalRow < numberTotal) {
              int kRow = permuteInverse_[iOriginalRow + numberRowsModel];
              if (kRow > iRow)
                Arow[sizeFactor_++] = kRow;
            } else {
              int kRow = iOriginalRow - numberTotal;
              CoinBigIndex start = rowStart[kRow];
              CoinBigIndex end = rowStart[kRow] + rowLength[kRow];
              for (CoinBigIndex j = start; j < end; j++) {
                int jRow = column[j];
                int jNewRow = permuteInverse_[jRow];
                if (jNewRow > iRow)
                  Arow[sizeFactor_++] = jNewRow;
              }
              // slack - should it be permute
              kRow = permuteInverse_[kRow + numberColumns];
              if (kRow > iRow)
                Arow[sizeFactor_++] = kRow;
            }
            // Sort
            std::sort(Arow + Astart[iRow], Arow + sizeFactor_);
          }
        }
      } else {
        if (!quadratic) {
          for (iColumn = 0; iColumn < numberColumns; iColumn++) {
            Astart[iColumn] = sizeFactor_;
            CoinBigIndex start = columnStart[iColumn];
            CoinBigIndex end = columnStart[iColumn] + columnLength[iColumn];
            for (CoinBigIndex j = start; j < end; j++) {
              Arow[sizeFactor_++] = row[j] + numberTotal;
            }
          }
        } else {
          // Quadratic
          const int *columnQuadratic = quadratic->getIndices();
          const CoinBigIndex *columnQuadraticStart = quadratic->getVectorStarts();
          const int *columnQuadraticLength = quadratic->getVectorLengths();
          //const double * quadraticElement = quadratic->getElements();
          for (iColumn = 0; iColumn < numberColumns; iColumn++) {
            Astart[iColumn] = sizeFactor_;
            CoinBigIndex j;
            for (j = columnQuadraticStart[iColumn];
                 j < columnQuadraticStart[iColumn] + columnQuadraticLength[iColumn]; j++) {
              int jColumn = columnQuadratic[j];
              if (jColumn > iColumn)
                Arow[sizeFactor_++] = jColumn;
            }
            CoinBigIndex start = columnStart[iColumn];
            CoinBigIndex end = columnStart[iColumn] + columnLength[iColumn];
            for (j = start; j < end; j++) {
              Arow[sizeFactor_++] = row[j] + numberTotal;
            }
          }
        }
        // slacks
        for (iRow = 0; iRow < numberRowsModel; iRow++) {
          Astart[iRow + numberColumns] = sizeFactor_;
          Arow[sizeFactor_++] = iRow + numberTotal;
        }
        // Transpose - nonzero diagonal (may regularize)
        for (iRow = 0; iRow < numberRowsModel; iRow++) {
          Astart[iRow + numberTotal] = sizeFactor_;
        }
      }
      Astart[numberRows_] = sizeFactor_;
    }
    symbolic2(Astart, Arow);
    if (sizeIndex_ < sizeFactor_) {
      int *indices = NULL;
      try {
        indices = new int[sizeIndex_];
      } catch (...) {
        // no memory
        noMemory = true;
      }
      if (!noMemory) {
        CoinMemcpyN(choleskyRow_, sizeIndex_, indices);
        delete[] choleskyRow_;
        choleskyRow_ = indices;
      }
    }
  }
  delete[] used;
  // Use cholesky regions
  delete[] Astart;
  delete[] Arow;
  double flops = 0.0;
  for (iRow = 0; iRow < numberRows_; iRow++) {
    int length = choleskyStart_[iRow + 1] - choleskyStart_[iRow];
    flops += static_cast< double >(length) * (length + 2.0);
  }
  if (model_->messageHandler()->logLevel() > 0)
    std::cout << sizeFactor << " elements in sparse Cholesky, flop count " << flops << std::endl;
  try {
    sparseFactor_ = new longDouble[sizeFactor_];
#if CLP_LONG_CHOLESKY != 1
    workDouble_ = new longDouble[numberRows_];
#else
    // actually long double
    workDouble_ = reinterpret_cast< double * >(new CoinWorkDouble[numberRows_]);
#endif
    diagonal_ = new longDouble[numberRows_];
  } catch (...) {
    // no memory
    noMemory = true;
  }
  if (noMemory) {
    delete[] choleskyRow_;
    choleskyRow_ = NULL;
    delete[] choleskyStart_;
    choleskyStart_ = NULL;
    delete[] permuteInverse_;
    permuteInverse_ = NULL;
    delete[] permute_;
    permute_ = NULL;
    delete[] choleskyStart_;
    choleskyStart_ = NULL;
    delete[] indexStart_;
    indexStart_ = NULL;
    delete[] link_;
    link_ = NULL;
    delete[] workInteger_;
    workInteger_ = NULL;
    delete[] sparseFactor_;
    sparseFactor_ = NULL;
    delete[] workDouble_;
    workDouble_ = NULL;
    delete[] diagonal_;
    diagonal_ = NULL;
    delete[] clique_;
    clique_ = NULL;
    return -1;
  }
  return 0;
}
int ClpCholeskyBase::symbolic1(const int *Astart, const int *Arow)
{
  int *marked = reinterpret_cast< int * >(workInteger_);
  int iRow;
  // may not need to do this here but makes debugging easier
  for (iRow = 0; iRow < numberRows_; iRow++) {
    marked[iRow] = -1;
    link_[iRow] = -1;
    choleskyStart_[iRow] = 0; // counts
  }
  for (iRow = 0; iRow < numberRows_; iRow++) {
    marked[iRow] = iRow;
    for (CoinBigIndex j = Astart[iRow]; j < Astart[iRow + 1]; j++) {
      int kRow = Arow[j];
      while (marked[kRow] != iRow) {
        if (link_[kRow] < 0)
          link_[kRow] = iRow;
        choleskyStart_[kRow]++;
        marked[kRow] = iRow;
        kRow = link_[kRow];
      }
    }
  }
  sizeFactor_ = 0;
  for (iRow = 0; iRow < numberRows_; iRow++) {
    int number = choleskyStart_[iRow];
    choleskyStart_[iRow] = sizeFactor_;
    sizeFactor_ += number;
  }
  choleskyStart_[numberRows_] = sizeFactor_;
  return sizeFactor_;
  ;
}
void ClpCholeskyBase::symbolic2(const int *Astart, const int *Arow)
{
  int *mergeLink = clique_;
  int *marker = reinterpret_cast< int * >(workInteger_);
  int iRow;
  for (iRow = 0; iRow < numberRows_; iRow++) {
    marker[iRow] = -1;
    mergeLink[iRow] = -1;
    link_[iRow] = -1; // not needed but makes debugging easier
  }
  int start = 0;
  int end = 0;
  choleskyStart_[0] = 0;

  for (iRow = 0; iRow < numberRows_; iRow++) {
    int nz = 0;
    int merge = mergeLink[iRow];
    bool marked = false;
    if (merge < 0)
      marker[iRow] = iRow;
    else
      marker[iRow] = merge;
    start = end;
    int startSub = start;
    link_[iRow] = numberRows_;
    CoinBigIndex j;
    for (j = Astart[iRow]; j < Astart[iRow + 1]; j++) {
      int kRow = Arow[j];
      int k = iRow;
      int linked = link_[iRow];
#ifndef NDEBUG
      int count = 0;
#endif
      while (linked <= kRow) {
        k = linked;
        linked = link_[k];
#ifndef NDEBUG
        count++;
        assert(count < numberRows_);
#endif
      }
      nz++;
      link_[k] = kRow;
      link_[kRow] = linked;
      if (marker[kRow] != marker[iRow])
        marked = true;
    }
    bool reuse = false;
    // Check if we can re-use indices
    if (!marked && merge >= 0 && mergeLink[merge] < 0) {
      // can re-use all
      startSub = indexStart_[merge] + 1;
      nz = choleskyStart_[merge + 1] - (choleskyStart_[merge] + 1);
      reuse = true;
    } else {
      // See if we can re-use any
      int k = mergeLink[iRow];
      int maxLength = 0;
      while (k >= 0) {
        int length = choleskyStart_[k + 1] - (choleskyStart_[k] + 1);
        int start = indexStart_[k] + 1;
        int stop = start + length;
        if (length > maxLength) {
          maxLength = length;
          startSub = start;
        }
        int linked = iRow;
        for (CoinBigIndex j = start; j < stop; j++) {
          int kRow = choleskyRow_[j];
          int kk = linked;
          linked = link_[kk];
          while (linked < kRow) {
            kk = linked;
            linked = link_[kk];
          }
          if (linked != kRow) {
            nz++;
            link_[kk] = kRow;
            link_[kRow] = linked;
            linked = kRow;
          }
        }
        k = mergeLink[k];
      }
      if (nz == maxLength)
        reuse = true; // can re-use
    }
    //reuse=false; //temp
    if (!reuse) {
      end += nz;
      startSub = start;
      int kRow = iRow;
      for (int j = start; j < end; j++) {
        kRow = link_[kRow];
        choleskyRow_[j] = kRow;
        assert(kRow < numberRows_);
        marker[kRow] = iRow;
      }
      marker[iRow] = iRow;
    }
    indexStart_[iRow] = startSub;
    choleskyStart_[iRow + 1] = choleskyStart_[iRow] + nz;
    if (nz > 1) {
      int kRow = choleskyRow_[startSub];
      mergeLink[iRow] = mergeLink[kRow];
      mergeLink[kRow] = iRow;
    }
    // should not be needed
    //std::sort(choleskyRow_+indexStart_[iRow]
    //      ,choleskyRow_+indexStart_[iRow]+nz);
    //#define CLP_DEBUG
#ifdef CLP_DEBUG
    int last = -1;
    for (j = indexStart_[iRow]; j < indexStart_[iRow] + nz; j++) {
      int kRow = choleskyRow_[j];
      assert(kRow > last);
      last = kRow;
    }
#endif
  }
  sizeFactor_ = choleskyStart_[numberRows_];
  sizeIndex_ = start;
  // find dense segment here
  int numberleft = numberRows_;
  for (iRow = 0; iRow < numberRows_; iRow++) {
    CoinBigIndex left = sizeFactor_ - choleskyStart_[iRow];
    double n = numberleft;
    double threshold = n * (n - 1.0) * 0.5 * goDense_;
    if (left >= threshold)
      break;
    numberleft--;
  }
  //iRow=numberRows_;
  int nDense = numberRows_ - iRow;
#define DENSE_THRESHOLD 8
  // don't do if dense columns
  if (nDense >= DENSE_THRESHOLD && !dense_) {
    COIN_DETAIL_PRINT(printf("Going dense for last %d rows\n", nDense));
    // make sure we don't disturb any indices
    int k = 0;
    for (int jRow = 0; jRow < iRow; jRow++) {
      int nz = choleskyStart_[jRow + 1] - choleskyStart_[jRow];
      k = CoinMax(k, indexStart_[jRow] + nz);
    }
    indexStart_[iRow] = k;
    int j;
    for (j = iRow + 1; j < numberRows_; j++) {
      choleskyRow_[k++] = j;
      indexStart_[j] = k;
    }
    sizeIndex_ = k;
    assert(k <= sizeFactor_); // can't happen with any reasonable defaults
    k = choleskyStart_[iRow];
    for (j = iRow + 1; j <= numberRows_; j++) {
      k += numberRows_ - j;
      choleskyStart_[j] = k;
    }
    // allow for blocked dense
    ClpCholeskyDense dense;
    sizeFactor_ = choleskyStart_[iRow] + dense.space(nDense);
    firstDense_ = iRow;
    if (doKKT_) {
      // redo permute so negative ones first
      int putN = firstDense_;
      int putP = 0;
      int numberRowsModel = model_->numberRows();
      int numberColumns = model_->numberColumns();
      int numberTotal = numberColumns + numberRowsModel;
      for (iRow = firstDense_; iRow < numberRows_; iRow++) {
        int originalRow = permute_[iRow];
        if (originalRow < numberTotal)
          permute_[putN++] = originalRow;
        else
          permuteInverse_[putP++] = originalRow;
      }
      for (iRow = putN; iRow < numberRows_; iRow++) {
        permute_[iRow] = permuteInverse_[iRow - putN];
      }
      for (iRow = 0; iRow < numberRows_; iRow++) {
        permuteInverse_[permute_[iRow]] = iRow;
      }
    }
  }
  // Clean up clique info
  for (iRow = 0; iRow < numberRows_; iRow++)
    clique_[iRow] = 0;
  int lastClique = -1;
  bool inClique = false;
  for (iRow = 1; iRow < firstDense_; iRow++) {
    int sizeLast = choleskyStart_[iRow] - choleskyStart_[iRow - 1];
    int sizeThis = choleskyStart_[iRow + 1] - choleskyStart_[iRow];
    if (indexStart_[iRow] == indexStart_[iRow - 1] + 1 && sizeThis == sizeLast - 1 && sizeThis) {
      // in clique
      if (!inClique) {
        inClique = true;
        lastClique = iRow - 1;
      }
    } else if (inClique) {
      int sizeClique = iRow - lastClique;
      for (int i = lastClique; i < iRow; i++) {
        clique_[i] = sizeClique;
        sizeClique--;
      }
      inClique = false;
    }
  }
  if (inClique) {
    int sizeClique = iRow - lastClique;
    for (int i = lastClique; i < iRow; i++) {
      clique_[i] = sizeClique;
      sizeClique--;
    }
  }
  //for (iRow=0;iRow<numberRows_;iRow++)
  //clique_[iRow]=0;
}
/* Factorize - filling in rowsDropped and returning number dropped */
int ClpCholeskyBase::factorize(const CoinWorkDouble *diagonal, int *rowsDropped)
{
  const CoinBigIndex *columnStart = model_->clpMatrix()->getVectorStarts();
  const int *columnLength = model_->clpMatrix()->getVectorLengths();
  const int *row = model_->clpMatrix()->getIndices();
  const double *element = model_->clpMatrix()->getElements();
  const CoinBigIndex *rowStart = rowCopy_->getVectorStarts();
  const int *rowLength = rowCopy_->getVectorLengths();
  const int *column = rowCopy_->getIndices();
  const double *elementByRow = rowCopy_->getElements();
  int numberColumns = model_->clpMatrix()->getNumCols();
  //perturbation
  CoinWorkDouble perturbation = model_->diagonalPerturbation() * model_->diagonalNorm();
  //perturbation=perturbation*perturbation*100000000.0;
  if (perturbation > 1.0) {
#ifdef COIN_DEVELOP
    //if (model_->model()->logLevel()&4)
    std::cout << "large perturbation " << perturbation << std::endl;
#endif
    perturbation = CoinSqrt(perturbation);
    perturbation = 1.0;
  }
  int iRow;
  int iColumn;
  longDouble *work = workDouble_;
  CoinZeroN(work, numberRows_);
  int newDropped = 0;
  CoinWorkDouble largest = 1.0;
  CoinWorkDouble smallest = COIN_DBL_MAX;
  int numberDense = 0;
  if (!doKKT_) {
    const CoinWorkDouble *diagonalSlack = diagonal + numberColumns;
    if (dense_)
      numberDense = dense_->numberRows();
    if (whichDense_) {
      longDouble *denseDiagonal = dense_->diagonal();
      longDouble *dense = denseColumn_;
      int iDense = 0;
      for (int iColumn = 0; iColumn < numberColumns; iColumn++) {
        if (whichDense_[iColumn]) {
          CoinZeroN(dense, numberRows_);
          CoinBigIndex start = columnStart[iColumn];
          CoinBigIndex end = columnStart[iColumn] + columnLength[iColumn];
          if (diagonal[iColumn]) {
            denseDiagonal[iDense++] = 1.0 / diagonal[iColumn];
            for (CoinBigIndex j = start; j < end; j++) {
              int jRow = row[j];
              dense[jRow] = element[j];
            }
          } else {
            denseDiagonal[iDense++] = 1.0;
          }
          dense += numberRows_;
        }
      }
    }
    CoinWorkDouble delta2 = model_->delta(); // add delta*delta to diagonal
    delta2 *= delta2;
    // largest in initial matrix
    CoinWorkDouble largest2 = 1.0e-20;
    for (iRow = 0; iRow < numberRows_; iRow++) {
      longDouble *put = sparseFactor_ + choleskyStart_[iRow];
      int *which = choleskyRow_ + indexStart_[iRow];
      int iOriginalRow = permute_[iRow];
      int number = choleskyStart_[iRow + 1] - choleskyStart_[iRow];
      if (!rowLength[iOriginalRow])
        rowsDropped_[iOriginalRow] = 1;
      if (!rowsDropped_[iOriginalRow]) {
        CoinBigIndex startRow = rowStart[iOriginalRow];
        CoinBigIndex endRow = rowStart[iOriginalRow] + rowLength[iOriginalRow];
        work[iRow] = diagonalSlack[iOriginalRow] + delta2;
        for (CoinBigIndex k = startRow; k < endRow; k++) {
          int iColumn = column[k];
          if (!whichDense_ || !whichDense_[iColumn]) {
            CoinBigIndex start = columnStart[iColumn];
            CoinBigIndex end = columnStart[iColumn] + columnLength[iColumn];
            CoinWorkDouble multiplier = diagonal[iColumn] * elementByRow[k];
            for (CoinBigIndex j = start; j < end; j++) {
              int jRow = row[j];
              int jNewRow = permuteInverse_[jRow];
              if (jNewRow >= iRow && !rowsDropped_[jRow]) {
                CoinWorkDouble value = element[j] * multiplier;
                work[jNewRow] += value;
              }
            }
          }
        }
        diagonal_[iRow] = work[iRow];
        largest2 = CoinMax(largest2, CoinAbs(work[iRow]));
        work[iRow] = 0.0;
        int j;
        for (j = 0; j < number; j++) {
          int jRow = which[j];
          put[j] = work[jRow];
          largest2 = CoinMax(largest2, CoinAbs(work[jRow]));
          work[jRow] = 0.0;
        }
      } else {
        // dropped
        diagonal_[iRow] = 1.0;
        int j;
        for (j = 1; j < number; j++) {
          put[j] = 0.0;
        }
      }
    }
    //check sizes
    largest2 *= 1.0e-20;
    largest = CoinMin(largest2, CHOL_SMALL_VALUE);
    int numberDroppedBefore = 0;
    for (iRow = 0; iRow < numberRows_; iRow++) {
      int dropped = rowsDropped_[iRow];
      // Move to int array
      rowsDropped[iRow] = dropped;
      if (!dropped) {
        CoinWorkDouble diagonal = diagonal_[iRow];
        if (diagonal > largest2) {
          diagonal_[iRow] = diagonal + perturbation;
        } else {
          diagonal_[iRow] = diagonal + perturbation;
          rowsDropped[iRow] = 2;
          numberDroppedBefore++;
          //printf("dropped - small diagonal %g\n",diagonal);
        }
      }
    }
    doubleParameters_[10] = CoinMax(1.0e-20, largest);
    integerParameters_[20] = 0;
    doubleParameters_[3] = 0.0;
    doubleParameters_[4] = COIN_DBL_MAX;
    integerParameters_[34] = 0; // say all must be positive
    factorizePart2(rowsDropped);
    newDropped = integerParameters_[20] + numberDroppedBefore;
    largest = doubleParameters_[3];
    smallest = doubleParameters_[4];
    if (model_->messageHandler()->logLevel() > 1)
      std::cout << "Cholesky - largest " << largest << " smallest " << smallest << std::endl;
    choleskyCondition_ = largest / smallest;
    if (whichDense_) {
      int i;
      for (i = 0; i < numberRows_; i++) {
        assert(diagonal_[i] >= 0.0);
        diagonal_[i] = CoinSqrt(diagonal_[i]);
      }
      // Update dense columns (just L)
      // Zero out dropped rows
      for (i = 0; i < numberDense; i++) {
        longDouble *a = denseColumn_ + i * numberRows_;
        for (int j = 0; j < numberRows_; j++) {
          if (rowsDropped[j])
            a[j] = 0.0;
        }
        for (i = 0; i < numberRows_; i++) {
          int iRow = permute_[i];
          workDouble_[i] = a[iRow];
        }
        for (i = 0; i < numberRows_; i++) {
          CoinWorkDouble value = workDouble_[i];
          CoinBigIndex offset = indexStart_[i] - choleskyStart_[i];
          CoinBigIndex j;
          for (j = choleskyStart_[i]; j < choleskyStart_[i + 1]; j++) {
            int iRow = choleskyRow_[j + offset];
            workDouble_[iRow] -= sparseFactor_[j] * value;
          }
        }
        for (i = 0; i < numberRows_; i++) {
          int iRow = permute_[i];
          a[iRow] = workDouble_[i] * diagonal_[i];
        }
      }
      dense_->resetRowsDropped();
      longDouble *denseBlob = dense_->aMatrix();
      longDouble *denseDiagonal = dense_->diagonal();
      // Update dense matrix
      for (i = 0; i < numberDense; i++) {
        const longDouble *a = denseColumn_ + i * numberRows_;
        // do diagonal
        CoinWorkDouble value = denseDiagonal[i];
        const longDouble *b = denseColumn_ + i * numberRows_;
        for (int k = 0; k < numberRows_; k++)
          value += a[k] * b[k];
        denseDiagonal[i] = value;
        for (int j = i + 1; j < numberDense; j++) {
          CoinWorkDouble value = 0.0;
          const longDouble *b = denseColumn_ + j * numberRows_;
          for (int k = 0; k < numberRows_; k++)
            value += a[k] * b[k];
          *denseBlob = value;
          denseBlob++;
        }
      }
      // dense cholesky (? long double)
      int *dropped = new int[numberDense];
      dense_->factorizePart2(dropped);
      delete[] dropped;
    }
    // try allowing all every time
    //printf("trying ?\n");
    //for (iRow=0;iRow<numberRows_;iRow++) {
    //rowsDropped[iRow]=0;
    //rowsDropped_[iRow]=0;
    //}
    bool cleanCholesky;
    //if (model_->numberIterations()<20||(model_->numberIterations()&1)==0)
    if (model_->numberIterations() < 2000)
      cleanCholesky = true;
    else
      cleanCholesky = false;
    if (cleanCholesky) {
      //drop fresh makes some formADAT easier
      if (newDropped || numberRowsDropped_) {
        newDropped = 0;
        for (int i = 0; i < numberRows_; i++) {
          char dropped = static_cast< char >(rowsDropped[i]);
          rowsDropped_[i] = dropped;
          rowsDropped_[i] = 0;
          if (dropped == 2) {
            //dropped this time
            rowsDropped[newDropped++] = i;
            rowsDropped_[i] = 0;
          }
        }
        numberRowsDropped_ = newDropped;
        newDropped = -(2 + newDropped);
      }
    } else {
      if (newDropped) {
        newDropped = 0;
        for (int i = 0; i < numberRows_; i++) {
          char dropped = static_cast< char >(rowsDropped[i]);
          rowsDropped_[i] = dropped;
          if (dropped == 2) {
            //dropped this time
            rowsDropped[newDropped++] = i;
            rowsDropped_[i] = 1;
          }
        }
      }
      numberRowsDropped_ += newDropped;
#if 0
      if (numberRowsDropped_) {
        std::cout << "Rank " << numberRows_ - numberRowsDropped_ << " ( " << numberRowsDropped_ << " dropped)";
        if (newDropped) {
          std::cout << " ( " << newDropped << " dropped this time)";
        }
        std::cout << std::endl;
      }
#endif
    }
  } else {
    //KKT
    CoinPackedMatrix *quadratic = NULL;
    ClpQuadraticObjective *quadraticObj = (dynamic_cast< ClpQuadraticObjective * >(model_->objectiveAsObject()));
    if (quadraticObj)
      quadratic = quadraticObj->quadraticObjective();
    // matrix
    int numberRowsModel = model_->numberRows();
    int numberColumns = model_->numberColumns();
    int numberTotal = numberColumns + numberRowsModel;
    // temp
    bool permuted = false;
    for (iRow = 0; iRow < numberRows_; iRow++) {
      if (permute_[iRow] != iRow) {
        permuted = true;
        break;
      }
    }
    // but fake it
    for (iRow = 0; iRow < numberRows_; iRow++) {
      //permute_[iRow]=iRow; // force no permute
      //permuteInverse_[permute_[iRow]]=iRow;
    }
    if (permuted) {
      CoinWorkDouble delta2 = model_->delta(); // add delta*delta to bottom
      delta2 *= delta2;
      // Need to permute - ugly
      if (!quadratic) {
        for (iRow = 0; iRow < numberRows_; iRow++) {
          longDouble *put = sparseFactor_ + choleskyStart_[iRow];
          int *which = choleskyRow_ + indexStart_[iRow];
          int iOriginalRow = permute_[iRow];
          if (iOriginalRow < numberColumns) {
            iColumn = iOriginalRow;
            CoinWorkDouble value = diagonal[iColumn];
            if (CoinAbs(value) > 1.0e-100) {
              value = 1.0 / value;
              largest = CoinMax(largest, CoinAbs(value));
              diagonal_[iRow] = -value;
              CoinBigIndex start = columnStart[iColumn];
              CoinBigIndex end = columnStart[iColumn] + columnLength[iColumn];
              for (CoinBigIndex j = start; j < end; j++) {
                int kRow = row[j] + numberTotal;
                kRow = permuteInverse_[kRow];
                if (kRow > iRow) {
                  work[kRow] = element[j];
                  largest = CoinMax(largest, CoinAbs(element[j]));
                }
              }
            } else {
              diagonal_[iRow] = -value;
            }
          } else if (iOriginalRow < numberTotal) {
            CoinWorkDouble value = diagonal[iOriginalRow];
            if (CoinAbs(value) > 1.0e-100) {
              value = 1.0 / value;
              largest = CoinMax(largest, CoinAbs(value));
            } else {
              value = 1.0e100;
            }
            diagonal_[iRow] = -value;
            int kRow = permuteInverse_[iOriginalRow + numberRowsModel];
            if (kRow > iRow)
              work[kRow] = -1.0;
          } else {
            diagonal_[iRow] = delta2;
            int kRow = iOriginalRow - numberTotal;
            CoinBigIndex start = rowStart[kRow];
            CoinBigIndex end = rowStart[kRow] + rowLength[kRow];
            for (CoinBigIndex j = start; j < end; j++) {
              int jRow = column[j];
              int jNewRow = permuteInverse_[jRow];
              if (jNewRow > iRow) {
                work[jNewRow] = elementByRow[j];
                largest = CoinMax(largest, CoinAbs(elementByRow[j]));
              }
            }
            // slack - should it be permute
            kRow = permuteInverse_[kRow + numberColumns];
            if (kRow > iRow)
              work[kRow] = -1.0;
          }
          CoinBigIndex j;
          int number = choleskyStart_[iRow + 1] - choleskyStart_[iRow];
          for (j = 0; j < number; j++) {
            int jRow = which[j];
            put[j] = work[jRow];
            work[jRow] = 0.0;
          }
        }
      } else {
        // quadratic
        const int *columnQuadratic = quadratic->getIndices();
        const CoinBigIndex *columnQuadraticStart = quadratic->getVectorStarts();
        const int *columnQuadraticLength = quadratic->getVectorLengths();
        const double *quadraticElement = quadratic->getElements();
        for (iRow = 0; iRow < numberRows_; iRow++) {
          longDouble *put = sparseFactor_ + choleskyStart_[iRow];
          int *which = choleskyRow_ + indexStart_[iRow];
          int iOriginalRow = permute_[iRow];
          if (iOriginalRow < numberColumns) {
            CoinBigIndex j;
            iColumn = iOriginalRow;
            CoinWorkDouble value = diagonal[iColumn];
            if (CoinAbs(value) > 1.0e-100) {
              value = 1.0 / value;
              for (j = columnQuadraticStart[iColumn];
                   j < columnQuadraticStart[iColumn] + columnQuadraticLength[iColumn]; j++) {
                int jColumn = columnQuadratic[j];
                int jNewColumn = permuteInverse_[jColumn];
                if (jNewColumn > iRow) {
                  work[jNewColumn] = -quadraticElement[j];
                } else if (iColumn == jColumn) {
                  value += quadraticElement[j];
                }
              }
              largest = CoinMax(largest, CoinAbs(value));
              diagonal_[iRow] = -value;
              CoinBigIndex start = columnStart[iColumn];
              CoinBigIndex end = columnStart[iColumn] + columnLength[iColumn];
              for (j = start; j < end; j++) {
                int kRow = row[j] + numberTotal;
                kRow = permuteInverse_[kRow];
                if (kRow > iRow) {
                  work[kRow] = element[j];
                  largest = CoinMax(largest, CoinAbs(element[j]));
                }
              }
            } else {
              diagonal_[iRow] = -value;
            }
          } else if (iOriginalRow < numberTotal) {
            CoinWorkDouble value = diagonal[iOriginalRow];
            if (CoinAbs(value) > 1.0e-100) {
              value = 1.0 / value;
              largest = CoinMax(largest, CoinAbs(value));
            } else {
              value = 1.0e100;
            }
            diagonal_[iRow] = -value;
            int kRow = permuteInverse_[iOriginalRow + numberRowsModel];
            if (kRow > iRow)
              work[kRow] = -1.0;
          } else {
            diagonal_[iRow] = delta2;
            int kRow = iOriginalRow - numberTotal;
            CoinBigIndex start = rowStart[kRow];
            CoinBigIndex end = rowStart[kRow] + rowLength[kRow];
            for (CoinBigIndex j = start; j < end; j++) {
              int jRow = column[j];
              int jNewRow = permuteInverse_[jRow];
              if (jNewRow > iRow) {
                work[jNewRow] = elementByRow[j];
                largest = CoinMax(largest, CoinAbs(elementByRow[j]));
              }
            }
            // slack - should it be permute
            kRow = permuteInverse_[kRow + numberColumns];
            if (kRow > iRow)
              work[kRow] = -1.0;
          }
          CoinBigIndex j;
          int number = choleskyStart_[iRow + 1] - choleskyStart_[iRow];
          for (j = 0; j < number; j++) {
            int jRow = which[j];
            put[j] = work[jRow];
            work[jRow] = 0.0;
          }
          for (j = 0; j < numberRows_; j++)
            assert(!work[j]);
        }
      }
    } else {
      if (!quadratic) {
        for (iColumn = 0; iColumn < numberColumns; iColumn++) {
          longDouble *put = sparseFactor_ + choleskyStart_[iColumn];
          int *which = choleskyRow_ + indexStart_[iColumn];
          CoinWorkDouble value = diagonal[iColumn];
          if (CoinAbs(value) > 1.0e-100) {
            value = 1.0 / value;
            largest = CoinMax(largest, CoinAbs(value));
            diagonal_[iColumn] = -value;
            CoinBigIndex start = columnStart[iColumn];
            CoinBigIndex end = columnStart[iColumn] + columnLength[iColumn];
            for (CoinBigIndex j = start; j < end; j++) {
              //choleskyRow_[numberElements]=row[j]+numberTotal;
              //sparseFactor_[numberElements++]=element[j];
              work[row[j] + numberTotal] = element[j];
              largest = CoinMax(largest, CoinAbs(element[j]));
            }
          } else {
            diagonal_[iColumn] = -value;
          }
          CoinBigIndex j;
          int number = choleskyStart_[iColumn + 1] - choleskyStart_[iColumn];
          for (j = 0; j < number; j++) {
            int jRow = which[j];
            put[j] = work[jRow];
            work[jRow] = 0.0;
          }
        }
      } else {
        // Quadratic
        const int *columnQuadratic = quadratic->getIndices();
        const CoinBigIndex *columnQuadraticStart = quadratic->getVectorStarts();
        const int *columnQuadraticLength = quadratic->getVectorLengths();
        const double *quadraticElement = quadratic->getElements();
        for (iColumn = 0; iColumn < numberColumns; iColumn++) {
          longDouble *put = sparseFactor_ + choleskyStart_[iColumn];
          int *which = choleskyRow_ + indexStart_[iColumn];
          int number = choleskyStart_[iColumn + 1] - choleskyStart_[iColumn];
          CoinWorkDouble value = diagonal[iColumn];
          CoinBigIndex j;
          if (CoinAbs(value) > 1.0e-100) {
            value = 1.0 / value;
            for (j = columnQuadraticStart[iColumn];
                 j < columnQuadraticStart[iColumn] + columnQuadraticLength[iColumn]; j++) {
              int jColumn = columnQuadratic[j];
              if (jColumn > iColumn) {
                work[jColumn] = -quadraticElement[j];
              } else if (iColumn == jColumn) {
                value += quadraticElement[j];
              }
            }
            largest = CoinMax(largest, CoinAbs(value));
            diagonal_[iColumn] = -value;
            CoinBigIndex start = columnStart[iColumn];
            CoinBigIndex end = columnStart[iColumn] + columnLength[iColumn];
            for (j = start; j < end; j++) {
              work[row[j] + numberTotal] = element[j];
              largest = CoinMax(largest, CoinAbs(element[j]));
            }
            for (j = 0; j < number; j++) {
              int jRow = which[j];
              put[j] = work[jRow];
              work[jRow] = 0.0;
            }
          } else {
            value = 1.0e100;
            diagonal_[iColumn] = -value;
            for (j = 0; j < number; j++) {
              int jRow = which[j];
              put[j] = work[jRow];
            }
          }
        }
      }
      // slacks
      for (iColumn = numberColumns; iColumn < numberTotal; iColumn++) {
        longDouble *put = sparseFactor_ + choleskyStart_[iColumn];
        int *which = choleskyRow_ + indexStart_[iColumn];
        CoinWorkDouble value = diagonal[iColumn];
        if (CoinAbs(value) > 1.0e-100) {
          value = 1.0 / value;
          largest = CoinMax(largest, CoinAbs(value));
        } else {
          value = 1.0e100;
        }
        diagonal_[iColumn] = -value;
        work[iColumn - numberColumns + numberTotal] = -1.0;
        CoinBigIndex j;
        int number = choleskyStart_[iColumn + 1] - choleskyStart_[iColumn];
        for (j = 0; j < number; j++) {
          int jRow = which[j];
          put[j] = work[jRow];
          work[jRow] = 0.0;
        }
      }
      // Finish diagonal
      CoinWorkDouble delta2 = model_->delta(); // add delta*delta to bottom
      delta2 *= delta2;
      for (iRow = 0; iRow < numberRowsModel; iRow++) {
        longDouble *put = sparseFactor_ + choleskyStart_[iRow + numberTotal];
        diagonal_[iRow + numberTotal] = delta2;
        CoinBigIndex j;
        int number = choleskyStart_[iRow + numberTotal + 1] - choleskyStart_[iRow + numberTotal];
        for (j = 0; j < number; j++) {
          put[j] = 0.0;
        }
      }
    }
    //check sizes
    largest *= 1.0e-20;
    largest = CoinMin(largest, CHOL_SMALL_VALUE);
    doubleParameters_[10] = CoinMax(1.0e-20, largest);
    integerParameters_[20] = 0;
    doubleParameters_[3] = 0.0;
    doubleParameters_[4] = COIN_DBL_MAX;
    // Set up LDL cutoff
    integerParameters_[34] = numberTotal;
    // KKT
    int *rowsDropped2 = new int[numberRows_];
    CoinZeroN(rowsDropped2, numberRows_);
    factorizePart2(rowsDropped2);
    newDropped = integerParameters_[20];
    largest = doubleParameters_[3];
    smallest = doubleParameters_[4];
    if (model_->messageHandler()->logLevel() > 1)
      std::cout << "Cholesky - largest " << largest << " smallest " << smallest << std::endl;
    choleskyCondition_ = largest / smallest;
    // Should save adjustments in ..R_
    CoinWorkDouble *primalR = model_->primalR();
    CoinWorkDouble *dualR = model_->dualR();
    for (iRow = 0; iRow < numberTotal; iRow++) {
      if (rowsDropped2[iRow]) {
        //printf("row region1 %d dropped\n",iRow);
        //rowsDropped_[iRow]=1;
        rowsDropped_[iRow] = 0;
        primalR[iRow] = doubleParameters_[20];
      } else {
        rowsDropped_[iRow] = 0;
        primalR[iRow] = 0.0;
      }
    }
    for (; iRow < numberRows_; iRow++) {
      if (rowsDropped2[iRow]) {
        //printf("row region2 %d dropped\n",iRow);
        //rowsDropped_[iRow]=1;
        rowsDropped_[iRow] = 0;
        dualR[iRow - numberTotal] = doubleParameters_[34];
      } else {
        rowsDropped_[iRow] = 0;
        dualR[iRow - numberTotal] = 0.0;
      }
    }
    delete[] rowsDropped2;
  }
  status_ = 0;
  return newDropped;
}
/* Factorize - filling in rowsDropped and returning number dropped
   in integerParam.
*/
void ClpCholeskyBase::factorizePart2(int *rowsDropped)
{
  CoinWorkDouble largest = doubleParameters_[3];
  CoinWorkDouble smallest = doubleParameters_[4];
  // probably done before
  largest = 0.0;
  smallest = COIN_DBL_MAX;
  double dropValue = doubleParameters_[10];
  int firstPositive = integerParameters_[34];
  longDouble *d = ClpCopyOfArray(diagonal_, numberRows_);
  int iRow;
  // minimum size before clique done
  //#define MINCLIQUE INT_MAX
#define MINCLIQUE 3
  longDouble *work = workDouble_;
  int *first = workInteger_;

  for (iRow = 0; iRow < numberRows_; iRow++) {
    link_[iRow] = -1;
    work[iRow] = 0.0;
    first[iRow] = choleskyStart_[iRow];
  }

  int lastClique = -1;
  bool inClique = false;
  bool newClique = false;
  bool endClique = false;
  int lastRow = 0;
  int nextRow2 = -1;

  for (iRow = 0; iRow < firstDense_ + 1; iRow++) {
    if (iRow < firstDense_) {
      endClique = false;
      if (clique_[iRow] > 0) {
        // this is in a clique
        inClique = true;
        if (clique_[iRow] > lastClique) {
          // new Clique
          newClique = true;
          // If we have clique going then signal to do old one
          endClique = (lastClique > 0);
        } else {
          // Still in clique
          newClique = false;
        }
      } else {
        // not in clique
        inClique = false;
        newClique = false;
        // If we have clique going then signal to do old one
        endClique = (lastClique > 0);
      }
      lastClique = clique_[iRow];
    } else if (inClique) {
      // Finish off
      endClique = true;
    } else {
      break;
    }
    if (endClique) {
      // We have just finished updating a clique - do block pivot and clean up
      int jRow;
      for (jRow = lastRow; jRow < iRow; jRow++) {
        int jCount = jRow - lastRow;
        CoinWorkDouble diagonalValue = diagonal_[jRow];
        CoinBigIndex start = choleskyStart_[jRow];
        CoinBigIndex end = choleskyStart_[jRow + 1];
        for (int kRow = lastRow; kRow < jRow; kRow++) {
          jCount--;
          CoinBigIndex get = choleskyStart_[kRow] + jCount;
          CoinWorkDouble a_jk = sparseFactor_[get];
          CoinWorkDouble value1 = d[kRow] * a_jk;
          diagonalValue -= a_jk * value1;
          for (CoinBigIndex j = start; j < end; j++)
            sparseFactor_[j] -= value1 * sparseFactor_[++get];
        }
        // check
        int originalRow = permute_[jRow];
        if (originalRow < firstPositive) {
          // must be negative
          if (diagonalValue <= -dropValue) {
            smallest = CoinMin(smallest, -diagonalValue);
            largest = CoinMax(largest, -diagonalValue);
            d[jRow] = diagonalValue;
            diagonalValue = 1.0 / diagonalValue;
          } else {
            rowsDropped[originalRow] = 2;
            d[jRow] = -1.0e100;
            diagonalValue = 0.0;
            integerParameters_[20]++;
          }
        } else {
          // must be positive
          if (diagonalValue >= dropValue) {
            smallest = CoinMin(smallest, diagonalValue);
            largest = CoinMax(largest, diagonalValue);
            d[jRow] = diagonalValue;
            diagonalValue = 1.0 / diagonalValue;
          } else {
            rowsDropped[originalRow] = 2;
            d[jRow] = 1.0e100;
            diagonalValue = 0.0;
            integerParameters_[20]++;
          }
        }
        diagonal_[jRow] = diagonalValue;
        for (CoinBigIndex j = start; j < end; j++) {
          sparseFactor_[j] *= diagonalValue;
        }
      }
      if (nextRow2 >= 0) {
        for (jRow = lastRow; jRow < iRow - 1; jRow++) {
          link_[jRow] = jRow + 1;
        }
        link_[iRow - 1] = link_[nextRow2];
        link_[nextRow2] = lastRow;
      }
    }
    if (iRow == firstDense_)
      break; // we were just cleaning up
    if (newClique) {
      // initialize new clique
      lastRow = iRow;
    }
    // for each column L[*,kRow] that affects L[*,iRow]
    CoinWorkDouble diagonalValue = diagonal_[iRow];
    int nextRow = link_[iRow];
    int kRow = 0;
    while (1) {
      kRow = nextRow;
      if (kRow < 0)
        break; // out of loop
      nextRow = link_[kRow];
      // Modify by outer product of L[*,irow] by L[*,krow] from first
      int k = first[kRow];
      int end = choleskyStart_[kRow + 1];
      assert(k < end);
      CoinWorkDouble a_ik = sparseFactor_[k++];
      CoinWorkDouble value1 = d[kRow] * a_ik;
      // update first
      first[kRow] = k;
      diagonalValue -= value1 * a_ik;
      int offset = indexStart_[kRow] - choleskyStart_[kRow];
      if (k < end) {
        int jRow = choleskyRow_[k + offset];
        if (clique_[kRow] < MINCLIQUE) {
          link_[kRow] = link_[jRow];
          link_[jRow] = kRow;
          for (; k < end; k++) {
            int jRow = choleskyRow_[k + offset];
            work[jRow] += sparseFactor_[k] * value1;
          }
        } else {
          // Clique
          CoinBigIndex currentIndex = k + offset;
          int linkSave = link_[jRow];
          link_[jRow] = kRow;
          work[kRow] = value1; // ? or a_jk
          int last = kRow + clique_[kRow];
          for (int kkRow = kRow + 1; kkRow < last; kkRow++) {
            CoinBigIndex j = first[kkRow];
            //int iiRow = choleskyRow_[j+indexStart_[kkRow]-choleskyStart_[kkRow]];
            CoinWorkDouble a = sparseFactor_[j];
            CoinWorkDouble dValue = d[kkRow] * a;
            diagonalValue -= a * dValue;
            work[kkRow] = dValue;
            first[kkRow]++;
            link_[kkRow - 1] = kkRow;
          }
          nextRow = link_[last - 1];
          link_[last - 1] = linkSave;
          int length = static_cast< int >(end - k);
          for (int i = 0; i < length; i++) {
            int lRow = choleskyRow_[currentIndex++];
            CoinWorkDouble t0 = work[lRow];
            for (int kkRow = kRow; kkRow < last; kkRow++) {
              CoinBigIndex j = first[kkRow] + i;
              t0 += work[kkRow] * sparseFactor_[j];
            }
            work[lRow] = t0;
          }
        }
      }
    }
    // Now apply
    if (inClique) {
      // in clique
      diagonal_[iRow] = diagonalValue;
      int start = choleskyStart_[iRow];
      int end = choleskyStart_[iRow + 1];
      int currentIndex = indexStart_[iRow];
      nextRow2 = -1;
      int get = start + clique_[iRow] - 1;
      if (get < end) {
        nextRow2 = choleskyRow_[currentIndex + get - start];
        first[iRow] = get;
      }
      for (int j = start; j < end; j++) {
        int kRow = choleskyRow_[currentIndex++];
        sparseFactor_[j] -= work[kRow]; // times?
        work[kRow] = 0.0;
      }
    } else {
      // not in clique
      int originalRow = permute_[iRow];
      if (originalRow < firstPositive) {
        // must be negative
        if (diagonalValue <= -dropValue) {
          smallest = CoinMin(smallest, -diagonalValue);
          largest = CoinMax(largest, -diagonalValue);
          d[iRow] = diagonalValue;
          diagonalValue = 1.0 / diagonalValue;
        } else {
          rowsDropped[originalRow] = 2;
          d[iRow] = -1.0e100;
          diagonalValue = 0.0;
          integerParameters_[20]++;
        }
      } else {
        // must be positive
        if (diagonalValue >= dropValue) {
          smallest = CoinMin(smallest, diagonalValue);
          largest = CoinMax(largest, diagonalValue);
          d[iRow] = diagonalValue;
          diagonalValue = 1.0 / diagonalValue;
        } else {
          rowsDropped[originalRow] = 2;
          d[iRow] = 1.0e100;
          diagonalValue = 0.0;
          integerParameters_[20]++;
        }
      }
      diagonal_[iRow] = diagonalValue;
      int offset = indexStart_[iRow] - choleskyStart_[iRow];
      int start = choleskyStart_[iRow];
      int end = choleskyStart_[iRow + 1];
      assert(first[iRow] == start);
      if (start < end) {
        int nextRow = choleskyRow_[start + offset];
        link_[iRow] = link_[nextRow];
        link_[nextRow] = iRow;
        for (int j = start; j < end; j++) {
          int jRow = choleskyRow_[j + offset];
          CoinWorkDouble value = sparseFactor_[j] - work[jRow];
          work[jRow] = 0.0;
          sparseFactor_[j] = diagonalValue * value;
        }
      }
    }
  }
  if (firstDense_ < numberRows_) {
    // do dense
    // update dense part
    updateDense(d, /*work,*/ first);
    ClpCholeskyDense dense;
    // just borrow space
    int nDense = numberRows_ - firstDense_;
    if (doKKT_) {
      for (iRow = firstDense_; iRow < numberRows_; iRow++) {
        int originalRow = permute_[iRow];
        if (originalRow >= firstPositive) {
          firstPositive = iRow - firstDense_;
          break;
        }
      }
    }
    dense.reserveSpace(this, nDense);
    int *dropped = new int[nDense];
    memset(dropped, 0, nDense * sizeof(int));
    dense.setDoubleParameter(3, largest);
    dense.setDoubleParameter(4, smallest);
    dense.setDoubleParameter(10, dropValue);
    dense.setIntegerParameter(20, 0);
    dense.setIntegerParameter(34, firstPositive);
    dense.setModel(model_);
    dense.factorizePart2(dropped);
    largest = dense.getDoubleParameter(3);
    smallest = dense.getDoubleParameter(4);
    integerParameters_[20] += dense.getIntegerParameter(20);
    for (iRow = firstDense_; iRow < numberRows_; iRow++) {
      int originalRow = permute_[iRow];
      rowsDropped[originalRow] = dropped[iRow - firstDense_];
    }
    delete[] dropped;
  }
  delete[] d;
  doubleParameters_[3] = largest;
  doubleParameters_[4] = smallest;
  return;
}
// Updates dense part (broken out for profiling)
void ClpCholeskyBase::updateDense(longDouble *d, /*longDouble * work,*/ int *first)
{
  for (int iRow = 0; iRow < firstDense_; iRow++) {
    int start = first[iRow];
    int end = choleskyStart_[iRow + 1];
    if (start < end) {
      int offset = indexStart_[iRow] - choleskyStart_[iRow];
      if (clique_[iRow] < 2) {
        CoinWorkDouble dValue = d[iRow];
        for (int k = start; k < end; k++) {
          int kRow = choleskyRow_[k + offset];
          assert(kRow >= firstDense_);
          CoinWorkDouble a_ik = sparseFactor_[k];
          CoinWorkDouble value1 = dValue * a_ik;
          diagonal_[kRow] -= value1 * a_ik;
          int base = choleskyStart_[kRow] - kRow - 1;
          for (int j = k + 1; j < end; j++) {
            int jRow = choleskyRow_[j + offset];
            CoinWorkDouble a_jk = sparseFactor_[j];
            sparseFactor_[base + jRow] -= a_jk * value1;
          }
        }
      } else if (clique_[iRow] < 3) {
        // do as pair
        CoinWorkDouble dValue0 = d[iRow];
        CoinWorkDouble dValue1 = d[iRow + 1];
        int offset1 = first[iRow + 1] - start;
        // skip row
        iRow++;
        for (int k = start; k < end; k++) {
          int kRow = choleskyRow_[k + offset];
          assert(kRow >= firstDense_);
          CoinWorkDouble a_ik0 = sparseFactor_[k];
          CoinWorkDouble value0 = dValue0 * a_ik0;
          CoinWorkDouble a_ik1 = sparseFactor_[k + offset1];
          CoinWorkDouble value1 = dValue1 * a_ik1;
          diagonal_[kRow] -= value0 * a_ik0 + value1 * a_ik1;
          int base = choleskyStart_[kRow] - kRow - 1;
          for (int j = k + 1; j < end; j++) {
            int jRow = choleskyRow_[j + offset];
            CoinWorkDouble a_jk0 = sparseFactor_[j];
            CoinWorkDouble a_jk1 = sparseFactor_[j + offset1];
            sparseFactor_[base + jRow] -= a_jk0 * value0 + a_jk1 * value1;
          }
        }
#define MANY_REGISTERS
#ifdef MANY_REGISTERS
      } else if (clique_[iRow] == 3) {
#else
      } else {
#endif
        // do as clique
        // maybe later get fancy on big cliques and do transpose copy
        // seems only worth going to 3 on Intel
        CoinWorkDouble dValue0 = d[iRow];
        CoinWorkDouble dValue1 = d[iRow + 1];
        CoinWorkDouble dValue2 = d[iRow + 2];
        // get offsets and skip rows
        int offset1 = first[++iRow] - start;
        int offset2 = first[++iRow] - start;
        for (int k = start; k < end; k++) {
          int kRow = choleskyRow_[k + offset];
          assert(kRow >= firstDense_);
          CoinWorkDouble diagonalValue = diagonal_[kRow];
          CoinWorkDouble a_ik0 = sparseFactor_[k];
          CoinWorkDouble value0 = dValue0 * a_ik0;
          CoinWorkDouble a_ik1 = sparseFactor_[k + offset1];
          CoinWorkDouble value1 = dValue1 * a_ik1;
          CoinWorkDouble a_ik2 = sparseFactor_[k + offset2];
          CoinWorkDouble value2 = dValue2 * a_ik2;
          int base = choleskyStart_[kRow] - kRow - 1;
          diagonal_[kRow] = diagonalValue - value0 * a_ik0 - value1 * a_ik1 - value2 * a_ik2;
          for (int j = k + 1; j < end; j++) {
            int jRow = choleskyRow_[j + offset];
            CoinWorkDouble a_jk0 = sparseFactor_[j];
            CoinWorkDouble a_jk1 = sparseFactor_[j + offset1];
            CoinWorkDouble a_jk2 = sparseFactor_[j + offset2];
            sparseFactor_[base + jRow] -= a_jk0 * value0 + a_jk1 * value1 + a_jk2 * value2;
          }
        }
#ifdef MANY_REGISTERS
      } else {
        // do as clique
        // maybe later get fancy on big cliques and do transpose copy
        // maybe only worth going to 3 on Intel (but may have hidden registers)
        CoinWorkDouble dValue0 = d[iRow];
        CoinWorkDouble dValue1 = d[iRow + 1];
        CoinWorkDouble dValue2 = d[iRow + 2];
        CoinWorkDouble dValue3 = d[iRow + 3];
        // get offsets and skip rows
        int offset1 = first[++iRow] - start;
        int offset2 = first[++iRow] - start;
        int offset3 = first[++iRow] - start;
        for (int k = start; k < end; k++) {
          int kRow = choleskyRow_[k + offset];
          assert(kRow >= firstDense_);
          CoinWorkDouble diagonalValue = diagonal_[kRow];
          CoinWorkDouble a_ik0 = sparseFactor_[k];
          CoinWorkDouble value0 = dValue0 * a_ik0;
          CoinWorkDouble a_ik1 = sparseFactor_[k + offset1];
          CoinWorkDouble value1 = dValue1 * a_ik1;
          CoinWorkDouble a_ik2 = sparseFactor_[k + offset2];
          CoinWorkDouble value2 = dValue2 * a_ik2;
          CoinWorkDouble a_ik3 = sparseFactor_[k + offset3];
          CoinWorkDouble value3 = dValue3 * a_ik3;
          int base = choleskyStart_[kRow] - kRow - 1;
          diagonal_[kRow] = diagonalValue - (value0 * a_ik0 + value1 * a_ik1 + value2 * a_ik2 + value3 * a_ik3);
          for (int j = k + 1; j < end; j++) {
            int jRow = choleskyRow_[j + offset];
            CoinWorkDouble a_jk0 = sparseFactor_[j];
            CoinWorkDouble a_jk1 = sparseFactor_[j + offset1];
            CoinWorkDouble a_jk2 = sparseFactor_[j + offset2];
            CoinWorkDouble a_jk3 = sparseFactor_[j + offset3];
            sparseFactor_[base + jRow] -= a_jk0 * value0 + a_jk1 * value1 + a_jk2 * value2 + a_jk3 * value3;
          }
        }
#endif
      }
    }
  }
}
/* Uses factorization to solve. */
void ClpCholeskyBase::solve(CoinWorkDouble *region)
{
  if (!whichDense_) {
    solve(region, 3);
  } else {
    // dense columns
    int i;
    solve(region, 1);
    // do change;
    int numberDense = dense_->numberRows();
    CoinWorkDouble *change = new CoinWorkDouble[numberDense];
    for (i = 0; i < numberDense; i++) {
      const longDouble *a = denseColumn_ + i * numberRows_;
      CoinWorkDouble value = 0.0;
      for (int iRow = 0; iRow < numberRows_; iRow++)
        value += a[iRow] * region[iRow];
      change[i] = value;
    }
    // solve
    dense_->solve(change);
    for (i = 0; i < numberDense; i++) {
      const longDouble *a = denseColumn_ + i * numberRows_;
      CoinWorkDouble value = change[i];
      for (int iRow = 0; iRow < numberRows_; iRow++)
        region[iRow] -= value * a[iRow];
    }
    delete[] change;
    // and finish off
    solve(region, 2);
  }
}
/* solve - 1 just first half, 2 just second half - 3 both.
   If 1 and 2 then diagonal has sqrt of inverse otherwise inverse
*/
void ClpCholeskyBase::solve(CoinWorkDouble *region, int type)
{
#ifdef CLP_DEBUG
  double *regionX = NULL;
  if (sizeof(CoinWorkDouble) != sizeof(double) && type == 3) {
    regionX = ClpCopyOfArray(region, numberRows_);
  }
#endif
  CoinWorkDouble *work = reinterpret_cast< CoinWorkDouble * >(workDouble_);
  int i;
  int j;
  for (i = 0; i < numberRows_; i++) {
    int iRow = permute_[i];
    work[i] = region[iRow];
  }
  switch (type) {
  case 1:
    for (i = 0; i < numberRows_; i++) {
      CoinWorkDouble value = work[i];
      int offset = indexStart_[i] - choleskyStart_[i];
      for (j = choleskyStart_[i]; j < choleskyStart_[i + 1]; j++) {
        int iRow = choleskyRow_[j + offset];
        work[iRow] -= sparseFactor_[j] * value;
      }
    }
    for (i = 0; i < numberRows_; i++) {
      int iRow = permute_[i];
      region[iRow] = work[i] * diagonal_[i];
    }
    break;
  case 2:
    for (i = numberRows_ - 1; i >= 0; i--) {
      int offset = indexStart_[i] - choleskyStart_[i];
      CoinWorkDouble value = work[i] * diagonal_[i];
      for (j = choleskyStart_[i]; j < choleskyStart_[i + 1]; j++) {
        int iRow = choleskyRow_[j + offset];
        value -= sparseFactor_[j] * work[iRow];
      }
      work[i] = value;
      int iRow = permute_[i];
      region[iRow] = value;
    }
    break;
  case 3:
    for (i = 0; i < firstDense_; i++) {
      int offset = indexStart_[i] - choleskyStart_[i];
      CoinWorkDouble value = work[i];
      for (j = choleskyStart_[i]; j < choleskyStart_[i + 1]; j++) {
        int iRow = choleskyRow_[j + offset];
        work[iRow] -= sparseFactor_[j] * value;
      }
    }
    if (firstDense_ < numberRows_) {
      // do dense
      ClpCholeskyDense dense;
      // just borrow space
      int nDense = numberRows_ - firstDense_;
      dense.reserveSpace(this, nDense);
      dense.solve(work + firstDense_);
      for (i = numberRows_ - 1; i >= firstDense_; i--) {
        CoinWorkDouble value = work[i];
        int iRow = permute_[i];
        region[iRow] = value;
      }
    }
    for (i = firstDense_ - 1; i >= 0; i--) {
      int offset = indexStart_[i] - choleskyStart_[i];
      CoinWorkDouble value = work[i] * diagonal_[i];
      for (j = choleskyStart_[i]; j < choleskyStart_[i + 1]; j++) {
        int iRow = choleskyRow_[j + offset];
        value -= sparseFactor_[j] * work[iRow];
      }
      work[i] = value;
      int iRow = permute_[i];
      region[iRow] = value;
    }
    break;
  }
#ifdef CLP_DEBUG
  if (regionX) {
    longDouble *work = workDouble_;
    int i;
    int j;
    double largestO = 0.0;
    for (i = 0; i < numberRows_; i++) {
      largestO = CoinMax(largestO, CoinAbs(regionX[i]));
    }
    for (i = 0; i < numberRows_; i++) {
      int iRow = permute_[i];
      work[i] = regionX[iRow];
    }
    for (i = 0; i < firstDense_; i++) {
      int offset = indexStart_[i] - choleskyStart_[i];
      CoinWorkDouble value = work[i];
      for (j = choleskyStart_[i]; j < choleskyStart_[i + 1]; j++) {
        int iRow = choleskyRow_[j + offset];
        work[iRow] -= sparseFactor_[j] * value;
      }
    }
    if (firstDense_ < numberRows_) {
      // do dense
      ClpCholeskyDense dense;
      // just borrow space
      int nDense = numberRows_ - firstDense_;
      dense.reserveSpace(this, nDense);
      dense.solve(work + firstDense_);
      for (i = numberRows_ - 1; i >= firstDense_; i--) {
        CoinWorkDouble value = work[i];
        int iRow = permute_[i];
        regionX[iRow] = value;
      }
    }
    for (i = firstDense_ - 1; i >= 0; i--) {
      int offset = indexStart_[i] - choleskyStart_[i];
      CoinWorkDouble value = work[i] * diagonal_[i];
      for (j = choleskyStart_[i]; j < choleskyStart_[i + 1]; j++) {
        int iRow = choleskyRow_[j + offset];
        value -= sparseFactor_[j] * work[iRow];
      }
      work[i] = value;
      int iRow = permute_[i];
      regionX[iRow] = value;
    }
    double largest = 0.0;
    double largestV = 0.0;
    for (i = 0; i < numberRows_; i++) {
      largest = CoinMax(largest, CoinAbs(region[i] - regionX[i]));
      largestV = CoinMax(largestV, CoinAbs(region[i]));
    }
    printf("largest difference %g, largest %g, largest original %g\n",
      largest, largestV, largestO);
    delete[] regionX;
  }
#endif
}
#if 0 //CLP_LONG_CHOLESKY
/* Uses factorization to solve. */
void
ClpCholeskyBase::solve (CoinWorkDouble * region)
{
     assert (!whichDense_) ;
     CoinWorkDouble * work = reinterpret_cast<CoinWorkDouble *> (workDouble_);
     int i;
     int j;
     for (i = 0; i < numberRows_; i++) {
          int iRow = permute_[i];
          work[i] = region[iRow];
     }
     for (i = 0; i < firstDense_; i++) {
          int offset = indexStart_[i] - choleskyStart_[i];
          CoinWorkDouble value = work[i];
          for (j = choleskyStart_[i]; j < choleskyStart_[i+1]; j++) {
               int iRow = choleskyRow_[j+offset];
               work[iRow] -= sparseFactor_[j] * value;
          }
     }
     if (firstDense_ < numberRows_) {
          // do dense
          ClpCholeskyDense dense;
          // just borrow space
          int nDense = numberRows_ - firstDense_;
          dense.reserveSpace(this, nDense);
          dense.solve(work + firstDense_);
          for (i = numberRows_ - 1; i >= firstDense_; i--) {
               CoinWorkDouble value = work[i];
               int iRow = permute_[i];
               region[iRow] = value;
          }
     }
     for (i = firstDense_ - 1; i >= 0; i--) {
          int offset = indexStart_[i] - choleskyStart_[i];
          CoinWorkDouble value = work[i] * diagonal_[i];
          for (j = choleskyStart_[i]; j < choleskyStart_[i+1]; j++) {
               int iRow = choleskyRow_[j+offset];
               value -= sparseFactor_[j] * work[iRow];
          }
          work[i] = value;
          int iRow = permute_[i];
          region[iRow] = value;
     }
}
#endif

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