/* $Id: CoinOslC.h 1374 2011-01-04 00:08:33Z lou $ */
#ifndef COIN_OSL_C_INCLUDE
/*
  Copyright (C) 1987, 2009, International Business Machines Corporation
  and others.  All Rights Reserved.

  This code is licensed under the terms of the Eclipse Public License (EPL).
*/
#define COIN_OSL_C_INCLUDE

#ifndef CLP_OSL
#define CLP_OSL 0
#endif
#define C_EKK_GO_SPARSE 200

#ifdef HAVE_ENDIAN_H
#include <endian.h>
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define INTEL
#endif
#endif

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

#define SPARSE_UPDATE
#define NO_SHIFT
#include "CoinHelperFunctions.hpp"

#include <stddef.h>
#ifdef __cplusplus
extern "C"{
#endif

int c_ekkbtrn(const EKKfactinfo *fact,
	    double *dwork1,
	    int * mpt,int first_nonzero);
int c_ekkbtrn_ipivrw(const EKKfactinfo *fact,
		   double *dwork1,
		   int * mpt, int ipivrw,int * spare);

int c_ekketsj(/*const*/ EKKfactinfo *fact,
	    double *dwork1,
	    int *mpt2, double dalpha, int orig_nincol,
	    int npivot, int *nuspikp,
	    const int ipivrw, int * spare);
int c_ekkftrn(const EKKfactinfo *fact,
	    double *dwork1,
	    double * dpermu,int * mpt, int numberNonZero);

int c_ekkftrn_ft(EKKfactinfo *fact,
	       double *dwork1, int *mpt, int *nincolp);
void c_ekkftrn2(EKKfactinfo *fact, double *dwork1,
	      double * dpermu1,int * mpt1, int *nincolp,
	     double *dwork1_ft, int *mpt_ft, int *nincolp_ft);

int c_ekklfct(EKKfactinfo *fact);
int c_ekkslcf(const EKKfactinfo *fact);
inline void c_ekkscpy(int n, const int *marr1,int *marr2)
{ CoinMemcpyN(marr1,n,marr2);}
inline void c_ekkdcpy(int n, const double *marr1,double *marr2)
{ CoinMemcpyN(marr1,n,marr2);}
int c_ekk_IsSet(const int * array,int bit);
void c_ekk_Set(int * array,int bit);
void c_ekk_Unset(int * array,int bit);

void c_ekkzero(int length, int n, void * array);
inline void c_ekkdzero(int n, double *marray)
{CoinZeroN(marray,n);}
inline void c_ekkizero(int n, int *marray)
{CoinZeroN(marray,n);}
inline void c_ekkczero(int n, char *marray)
{CoinZeroN(marray,n);}
#ifdef __cplusplus
          }
#endif

#define c_ekkscpy_0_1(s,ival,array) CoinFillN(array,s,ival)
#define c_ekks1cpy( n,marr1,marr2)  CoinMemcpyN(marr1,n, marr2)
void clp_setup_pointers(EKKfactinfo * fact);
void clp_memory(int type);
double * clp_double(int number_entries);
int * clp_int(int number_entries);
void * clp_malloc(int number_entries);
void clp_free(void * oldArray);

#define SLACK_VALUE -1.0
#define	C_EKK_REMOVE_LINK(hpiv,hin,link,ipivot)	\
  {						\
    int ipre = link[ipivot].pre;		\
    int isuc = link[ipivot].suc;		\
    if (ipre > 0) {				\
      link[ipre].suc = isuc;			\
    }						\
    if (ipre <= 0) {				\
      hpiv[hin[ipivot]] = isuc;			\
    }						\
    if (isuc > 0) {				\
      link[isuc].pre = ipre;			\
    }						\
  }

#define	C_EKK_ADD_LINK(hpiv,nzi,link, npr)	\
  {						\
    int ifiri = hpiv[nzi];			\
    hpiv[nzi] = npr;				\
    link[npr].suc = ifiri;			\
    link[npr].pre = 0;				\
    if (ifiri != 0) {				\
      link[ifiri].pre = npr;			\
    }						\
  }
#include <assert.h>
#ifdef	NO_SHIFT

#define	SHIFT_INDEX(limit)	(limit)
#define	UNSHIFT_INDEX(limit)	(limit)
#define	SHIFT_REF(arr,ind)	(arr)[ind]

#else

#define	SHIFT_INDEX(limit)	((limit)<<3)
#define	UNSHIFT_INDEX(limit)	((unsigned int)(limit)>>3)
#define	SHIFT_REF(arr,ind)	(*(double*)((char*)(arr) + (ind)))

#endif

#ifdef INTEL
#define	NOT_ZERO(x)	(((*((reinterpret_cast<unsigned char *>(&x))+7)) & 0x7F) != 0)
#else
#define	NOT_ZERO(x)	((x) != 0.0)
#endif

#define	SWAP(type,_x,_y)	{ type _tmp = (_x); (_x) = (_y); (_y) = _tmp;}

#define	UNROLL_LOOP_BODY1(code)			\
  {{code}}
#define	UNROLL_LOOP_BODY2(code)			\
  {{code} {code}}
#define	UNROLL_LOOP_BODY4(code)			\
  {{code} {code} {code} {code}}
#endif
#ifdef COIN_OSL_CMFC
/*     Return codes in IRTCOD/IRTCOD are */
/*     4: numerical problems */
/*     5: not enough space in row file */
/*     6: not enough space in column file */
/*    23: system error at label 320 */
{
#if 1
  int *hcoli	= fact->xecadr;
  double *dluval	= fact->xeeadr;
  double *dvalpv = fact->kw3adr;
  int *mrstrt	= fact->xrsadr;
  int *hrowi	= fact->xeradr;
  int *mcstrt	= fact->xcsadr;
  int *hinrow	= fact->xrnadr;
  int *hincol	= fact->xcnadr;
  int *hpivro	= fact->krpadr;
  int *hpivco	= fact->kcpadr;
#endif
  int nnentl	= fact->nnentl;
  int nnentu	= fact->nnentu;
  int kmxeta	= fact->kmxeta;
  int xnewro	= *xnewrop;
  int ncompactions	= *ncompactionsp;

  MACTION_T *maction = reinterpret_cast<MACTION_T*>(maction_void);

  int i, j, k;
  double d1;
  int j1, j2;
  int jj, kk, kr, nz, jj1, jj2, kce, kcs, kqq, npr;
  int fill, naft;
  int enpr;
  int nres, npre;
  int knpr, irow, iadd32, ibase;
  double pivot;
  int count, nznpr;
  int nlast, epivr1;
  int kipis;
  double dpivx;
  int kipie, kcpiv, knprs, knpre;
  bool cancel;
  double multip, elemnt;
  int ipivot, jpivot, epivro, epivco, lstart, ifdens, nfirst;
  int nzpivj, kfill, kstart;
  int nmove, ileft;
#ifndef C_EKKCMFY
  int iput, nspare;
  int noRoomForDense=0;
  int if_sparse_update=fact->if_sparse_update;
#endif
  int irtcod	= 0;
  const int nrow	= fact->nrow;

  /* Parameter adjustments */
  --maction;

  /* Function Body */
  lstart = nnetas - nnentl + 1;
  for (i = lstart; i <= nnetas; ++i) {
      hrowi[i] = SHIFT_INDEX(hcoli[i]);
  }
  ifdens = 0;

  for (i = 1; i <= nrow; ++i) {
    maction[i] = 0;
    mwork[i].pre = i - 1;
    mwork[i].suc = i + 1;
  }

  iadd32 = 0;
  nlast = nrow;
  nfirst = 1;
  mwork[1].pre = nrow;
  mwork[nrow].suc = 1;

  for (count = 1; count <= nrow; ++count) {

    /* Pick column singletons */
    if (! (hpivco[1] <= 0)) {
      int small_pivot = c_ekkcsin(fact,
				 rlink, clink,
				    nsingp);

      if (small_pivot) {
	irtcod = 7; /* pivot too small */
	if (fact->invok >= 0) {
	  goto L1050;
	}
      }
      if (fact->npivots >= nrow) {
	goto L1050;
      }
    }

    /* Pick row singletons */
    if (! (hpivro[1] <= 0)) {
      irtcod = c_ekkrsin(fact,
			 rlink, clink,
			 mwork,nfirst,
			 nsingp,

		     &xnewco, &xnewro,
		     &nnentu,
		     &kmxeta, &ncompactions,
			 &nnentl);
	if (irtcod != 0) {
	  if (irtcod < 0 || fact->invok >= 0) {
	    /* -5 */
	    goto L1050;
	  }
	  /* ASSERT:  irtcod == 7 - pivot too small */
	  /* why don't we return with an error? */
	}
	if (fact->npivots >= nrow) {
	    goto L1050;
	}
	lstart = nnetas - nnentl + 1;
    }

    /* Find a pivot element */
    irtcod = c_ekkfpvt(fact,
		      rlink, clink,
		     nsingp, xrejctp, &ipivot, &jpivot);
    if (irtcod != 0) {
      /* irtcod == 10 */
	goto L1050;
    }
    /*        Update list structures and prepare for numerical phase */
    c_ekkprpv(fact, rlink, clink,
		     *xrejctp, ipivot, jpivot);

    epivco = hincol[jpivot];
    ++fact->xnetal;
    mcstrt[fact->xnetal] = lstart - 1;
    hpivco[fact->xnetal] = ipivot;
    epivro = hinrow[ipivot];
    epivr1 = epivro - 1;
    kipis = mrstrt[ipivot];
    pivot = dluval[kipis];
    dpivx = 1. / pivot;
    kipie = kipis + epivr1;
    ++kipis;
#ifndef	C_EKKCMFY
    {
      double size = nrow - fact->npivots;
      if (size > GO_DENSE && (nnentu - fact->nuspike) * GO_DENSE_RATIO > size * size) {
	/* say going to dense coding */
	if (*nsingp == 0) {
	  ifdens = 1;
	}
      }
    }
#endif
    /* copy the pivot row entries into dvalpv */
    /* the maction array tells us the index into dvalpv for a given row */
    /* the alternative would be using a large array of doubles */
    for (k = kipis; k <= kipie; ++k) {
      irow = hcoli[k];
      dvalpv[k - kipis + 1] = dluval[k];
      maction[irow] = static_cast<MACTION_T>(k - kipis + 1);
    }

    /* Loop over nonzeros in pivot column */
    kcpiv = mcstrt[jpivot] - 1;
    for (nzpivj = 1; nzpivj <= epivco; ++nzpivj) {
      ++kcpiv;
      npr = hrowi[kcpiv];
      hrowi[kcpiv] = 0;	/* zero out for possible compaction later on */

      --hincol[jpivot];

      ++mcstrt[jpivot];
      /* loop invariant:  kcpiv == mcstrt[jpivot] - 1 */

      --hinrow[npr];
      enpr = hinrow[npr];
      knprs = mrstrt[npr];
      knpre = knprs + enpr;

      /* Search for element to be eliminated */
      knpr = knprs;
      while (1) {
	  UNROLL_LOOP_BODY4({
	    if (jpivot == hcoli[knpr]) {
	      break;
	    }
	    knpr++;
	  });
      }

      multip = -dluval[knpr] * dpivx;

      /* swap last entry with pivot */
      dluval[knpr] = dluval[knpre];
      hcoli[knpr] = hcoli[knpre];
      --knpre;

#if	1
      /* MONSTER_UNROLLED_CODE - see below */
      kfill = epivr1 - (knpre - knprs + 1);
      nres = ((knpre - knprs + 1) & 1) + knprs;
      cancel = false;
      d1 = 1e33;
      j1 = hcoli[nres];

      if (nres != knprs) {
	j = hcoli[knprs];
	if (maction[j] == 0) {
	  ++kfill;
	} else {
	  jj = maction[j];
	  maction[j] = static_cast<MACTION_T>(-maction[j]);
	  dluval[knprs] += multip * dvalpv[jj];
	  d1 = fabs(dluval[knprs]);
	}
      }
      j2 = hcoli[nres + 1];
      jj1 = maction[j1];
      for (kr = nres; kr < knpre; kr += 2) {
	jj2 = maction[j2];
	if (jj1 == 0) {
	  ++kfill;
	} else {
	  maction[j1] = static_cast<MACTION_T>(-maction[j1]);
	  dluval[kr] += multip * dvalpv[jj1];
	  cancel = cancel || ! (fact->zeroTolerance < d1);
	  d1 = fabs(dluval[kr]);
	}
	j1 = hcoli[kr + 2];
	if (jj2 == 0) {
	  ++kfill;
	} else {
	  maction[j2] = static_cast<MACTION_T>(-maction[j2]);
	  dluval[kr + 1] += multip * dvalpv[jj2];
	  cancel = cancel || ! (fact->zeroTolerance < d1);
	  d1 = fabs(dluval[kr + 1]);
	}
	jj1 = maction[j1];
	j2 = hcoli[kr + 3];
      }
      cancel = cancel || ! (fact->zeroTolerance < d1);
#else
      /*
       * This is apparently what the above code does.
       * In addition to being unrolled, the assignments to j[12] and jj[12]
       * are shifted so that the result of dereferencing maction doesn't
       * have to be used immediately afterwards for the branch test.
       * This would would cause a pipeline delay.  (The apparent dereference
       * of hcoli will be removed by the compiler using strength reduction).
       *
       * loop through the entries in the row being processed,
       * flipping the sign of the maction entries as we go along.
       * Afterwards, we look for positive entries to see what pivot
       * row entries will cause fill-in.  We count the number of fill-ins, too.
       * "cancel" says if the result of combining the pivot row with this one
       * causes an entry to get too small; if so, we discard those entries.
       */
      kfill = epivr1 - (knpre - knprs + 1);
      cancel = false;

      for (kr = knprs; kr <= knpre; kr++) {
	j1 = hcoli[kr];
	jj1 = maction[j1];
	if ( (jj1 == 0)) {
	  /* no entry - this pivot column entry will have to be added */
	  ++kfill;
	} else {
	  /* there is an entry for this column in the pivot row */
	  maction[j1] = -maction[j1];
	  dluval[kr] += multip * dvalpv[jj1];
	  d1 = fabs(dluval[kr]);
	  cancel = cancel || ! (fact->zeroTolerance < d1);
	}
      }
#endif
      kstart = knpre;
      fill = kfill;

      if (cancel) {
	/* KSTART is used as a stack pointer for nonzeros in factored row */
	kstart = knprs - 1;
	for (kr = knprs; kr <= knpre; ++kr) {
	  j = hcoli[kr];
	  if (fabs(dluval[kr]) > fact->zeroTolerance) {
	    ++kstart;
	    dluval[kstart] = dluval[kr];
	    hcoli[kstart] = j;
	  } else {
	    /* Remove element from column file */
	    --nnentu;
	    --hincol[j];
	    --enpr;
	    kcs = mcstrt[j];
	    kce = kcs + hincol[j];
	    for (kk = kcs; kk <= kce; ++kk) {
	      if (hrowi[kk] == npr) {
		hrowi[kk] = hrowi[kce];
		hrowi[kce] = 0;
		break;
	      }
	    }
	    /* ASSERT !(kk>kce) */
	  }
	}
	knpre = kstart;
      }
      /* Fill contains an upper bound on the amount of fill-in */
      if (fill == 0) {
	for (k = kipis; k <= kipie; ++k) {
	  maction[hcoli[k]] = static_cast<MACTION_T>(-maction[hcoli[k]]);
	}
      }
      else {
	naft = mwork[npr].suc;
	kqq = mrstrt[naft] - knpre - 1;

	if (fill > kqq) {
	  /* Fill-in exceeds space left. Check if there is enough */
	  /* space in row file for the new row. */
	  nznpr = enpr + fill;
	  if (! (xnewro + nznpr + 1 < lstart)) {
	    if (! (nnentu + nznpr + 1 < lstart)) {
	      irtcod = -5;
	      goto L1050;
	    }
	    /* idea 1 is to compress every time xnewro increases by x thousand */
	    /* idea 2 is to copy nucleus rows with a reasonable gap */
	    /* then copy each row down when used */
	    /* compressions would just be 1 remainder which eventually will */
	    /* fit in cache */
	    {
	      int iput = c_ekkrwcs(fact,dluval, hcoli, mrstrt, hinrow, mwork, nfirst);
	      kmxeta += xnewro - iput ;
	      xnewro = iput - 1;
	      ++ncompactions;
	    }

	    kipis = mrstrt[ipivot] + 1;
	    kipie = kipis + epivr1 - 1;
	    knprs = mrstrt[npr];
	  }

	  /* I think this assignment should be inside the previous if-stmt */
	  /* otherwise, it does nothing */
	  /*assert(knpre == knprs + enpr - 1);*/
	  knpre = knprs + enpr - 1;

	  /*
	   * copy this row to the end of the row file and adjust its links.
	   * The links keep track of the order of rows in memory.
	   * Rows are only moved from the middle all the way to the end.
	   */
	  if (npr != nlast) {
	    npre = mwork[npr].pre;
	    if (npr == nfirst) {
	      nfirst = naft;
	    }
	    /*             take out of chain */
	    mwork[naft].pre = npre;
	    mwork[npre].suc = naft;
	    /*             and put in at end */
	    mwork[nfirst].pre = npr;
	    mwork[nlast].suc = npr;
	    mwork[npr].pre = nlast;
	    mwork[npr].suc = nfirst;
	    nlast = npr;
	    kstart = xnewro;
	    mrstrt[npr] = kstart + 1;
	    nmove = knpre - knprs + 1;
	    ibase = kstart + 1 - knprs;
	    for (kr = knprs; kr <= knpre; ++kr) {
	      dluval[ibase + kr] = dluval[kr];
	      hcoli[ibase + kr] = hcoli[kr];
	    }
	    kstart += nmove;
	  } else {
	    kstart = knpre;
	  }

	  /* extra space ? */
	  /*
	   * The mystery of iadd32.
	   * This code assigns to xnewro, possibly using iadd32.
	   * However, in that case xnewro is assigned to just after
	   * the for-loop below, and there is no intervening reference.
	   * Therefore, I believe that this code can be entirely eliminated;
	   * it is the leftover of an interrupted or dropped experiment.
	   * Presumably, this was trying to implement the ideas about
	   * padding expressed above.
	   */
	  if (iadd32 != 0) {
	    xnewro += iadd32;
	  } else {
	    if (kstart + (nrow << 1) + 100 < lstart) {
	      ileft = ((nrow - fact->npivots + 32) & -32);
	      if (kstart + ileft * ileft + 32 < lstart) {
		iadd32 = ileft;
		xnewro = CoinMax(kstart,xnewro);
		xnewro = (xnewro & -32) + ileft;
	      } else {
		xnewro = ((kstart + 31) & -32);
	      }
	    } else {
	      xnewro = kstart;
	    }
	  }

	  hinrow[npr] = enpr;
	} else if (! (nnentu + kqq + 2 < lstart)) {
	  irtcod = -5;
	  goto L1050;
	}
	/* Scan pivot row again to generate fill in. */
	for (kr = kipis; kr <= kipie; ++kr) {
	  j = hcoli[kr];
	  jj = maction[j];
	  if (jj >0) {
	    elemnt = multip * dvalpv[jj];
	    if (fabs(elemnt) > fact->zeroTolerance) {
	      ++kstart;
	      dluval[kstart] = elemnt;
	      //printf("pivot %d at %d col %d el %g\n",
	      // npr,kstart,j,elemnt);
	      hcoli[kstart] = j;
	      ++nnentu;
	      nz = hincol[j];
	      kcs = mcstrt[j];
	      kce = kcs + nz - 1;
	      if (kce == xnewco) {
		if (xnewco + 1 >= lstart) {
		  if (xnewco + nz + 1 >= lstart) {
		    /*                  Compress column file */
		    if (nnentu + nz + 1 < lstart) {
		      xnewco = c_ekkclco(fact,hrowi, mcstrt, hincol, xnewco);
		      ++ncompactions;

		      kcpiv = mcstrt[jpivot] - 1;
		      kcs = mcstrt[j];
		      /*                  HINCOL MAY HAVE CHANGED? (JJHF) ??? */
		      nz = hincol[j];
		      kce = kcs + nz - 1;
		    } else {
		      irtcod = -5;
		      goto L1050;
		    }
		  }
		  /*              Copy column */
		  mcstrt[j] = xnewco + 1;
		  ibase = mcstrt[j] - kcs;
		  for (kk = kcs; kk <= kce; ++kk) {
		    hrowi[ibase + kk] = hrowi[kk];
		    hrowi[kk] = 0;
		  }
		  kce = xnewco + kce - kcs + 1;
		  xnewco = kce + 1;
		} else {
		  ++xnewco;
		}
	      } else if (hrowi[kce + 1] != 0) {
		/* here we use the fact that hrowi entries not "in use" are zeroed */
		if (xnewco + nz + 1 >= lstart) {
		  /* Compress column file */
		  if (nnentu + nz + 1 < lstart) {
		    xnewco = c_ekkclco(fact,hrowi, mcstrt, hincol, xnewco);
		    ++ncompactions;

		    kcpiv = mcstrt[jpivot] - 1;
		    kcs = mcstrt[j];
		    /*                  HINCOL MAY HAVE CHANGED? (JJHF) ??? */
		    nz = hincol[j];
		    kce = kcs + nz - 1;
		  } else {
		    irtcod = -5;
		    goto L1050;
		  }
		}
		/* move the column to the end of the column file */
		mcstrt[j] = xnewco + 1;
		ibase = mcstrt[j] - kcs;
		for (kk = kcs; kk <= kce; ++kk) {
		  hrowi[ibase + kk] = hrowi[kk];
		  hrowi[kk] = 0;
		}
		kce = xnewco + kce - kcs + 1;
		xnewco = kce + 1;
	      }
	      /* store element */
	      hrowi[kce + 1] = npr;
	      hincol[j] = nz + 1;
	    }
	  } else {
	    maction[j] = static_cast<MACTION_T>(-maction[j]);
	  }
	}
	if (fill > kqq) {
	  xnewro = kstart;
	}
      }
      hinrow[npr] = kstart - mrstrt[npr] + 1;
      /* Check if row or column file needs compression */
      if (! (xnewco + 1 < lstart)) {
	xnewco = c_ekkclco(fact,hrowi, mcstrt, hincol, xnewco);
	++ncompactions;

	kcpiv = mcstrt[jpivot] - 1;
      }
      if (! (xnewro + 1 < lstart)) {
	int iput = c_ekkrwcs(fact,dluval, hcoli, mrstrt, hinrow, mwork, nfirst);
	kmxeta += xnewro - iput ;
	xnewro = iput - 1;
	++ncompactions;

	kipis = mrstrt[ipivot] + 1;
	kipie = kipis + epivr1 - 1;
      }
      /* Store elementary row transformation */
      ++nnentl;
      --nnentu;
      --lstart;
      dluval[lstart] = multip;

      hrowi[lstart] = SHIFT_INDEX(npr);
#define INLINE_AFPV 3
      /* We could do this while computing values but
	 it makes it much more complex.  At least we should get
	 reasonable cache behavior by doing it each row */
#if INLINE_AFPV
      {
	int j;
	int nel, krs;
	int koff;
	int * index;
	double * els;
	nel = hinrow[npr];
	krs = mrstrt[npr];
	index=&hcoli[krs];
	els=&dluval[krs];
#if INLINE_AFPV<3
#if INLINE_AFPV==1
	double maxaij = 0.0;
	koff = 0;
	j=0;
	while (j<nel) {
	  double d = fabs(els[j]);
	  if (maxaij < d) {
	    maxaij = d;
	    koff=j;
	  }
	  j++;
	}
#else
	assert (nel);
	koff=0;
	double maxaij=fabs(els[0]);
	for (j=1;j<nel;j++) {
	  double d = fabs(els[j]);
	  if (maxaij < d) {
	    maxaij = d;
	    koff=j;
	  }
	}
#endif
#else
	double maxaij = 0.0;
	koff = 0;
	j=0;
	if ((nel&1)!=0) {
	  maxaij=fabs(els[0]);
	  j=1;
	}

	while (j<nel) {
	  UNROLL_LOOP_BODY2({
	      double d = fabs(els[j]);
	      if (maxaij < d) {
		maxaij = d;
		koff=j;
	      }
	      j++;
	    });
	}
#endif
	SWAP(int, index[koff], index[0]);
	SWAP(double, els[koff], els[0]);
      }
#endif

      {
	int nzi = hinrow[npr];
	if (nzi > 0) {
	  C_EKK_ADD_LINK(hpivro, nzi, rlink, npr);
	}
      }
    }

    /* after pivot move biggest to first in each row */
#if INLINE_AFPV==0
    int nn = mcstrt[fact->xnetal] - lstart + 1;
    c_ekkafpv(hrowi+lstart, hcoli, dluval, mrstrt, hinrow, nn);
#endif

    /* Restore work array */
    for (k = kipis; k <= kipie; ++k) {
      maction[hcoli[k]] = 0;
    }

    if (*xrejctp > 0) {
      for (k = kipis; k <= kipie; ++k) {
	int j = hcoli[k];
	int nzj = hincol[j];
	if (! (nzj <= 0) &&
	    ! ((clink[j].pre > nrow && nzj != 1))) {
	  C_EKK_ADD_LINK(hpivco, nzj, clink, j);
	}
      }
    } else {
      for (k = kipis; k <= kipie; ++k) {
	int j = hcoli[k];
	int nzj = hincol[j];
	if (! (nzj <= 0)) {
	  C_EKK_ADD_LINK(hpivco, nzj, clink, j);
	}
      }
    }
    fact->nuspike += hinrow[ipivot];

    /* Go to dense coding if appropriate */
#ifndef	C_EKKCMFY
    if (ifdens != 0) {
      int ndense = nrow - fact->npivots;
      if (! (xnewro + ndense * ndense >= lstart)) {

	/* set up sort order in MACTION */
	c_ekkizero( nrow, reinterpret_cast<int *> (maction+1));
	iput = 0;
	for (i = 1; i <= nrow; ++i) {
	  if (clink[i].pre >= 0) {
	    ++iput;
	    maction[i] = static_cast<short int>(iput);
	  }
	}
	/* and get number spare needed */
	nspare = 0;
	for (i = 1; i <= nrow; ++i) {
	  if (rlink[i].pre >= 0) {
	    nspare = nspare + ndense - hinrow[i];
	  }
	}
	if (iput != nrow - fact->npivots) {
	  /* must be singular */
	  c_ekkizero( nrow, reinterpret_cast<int *> (maction+1));
	} else {
	  /* pack down then back up */
	  int iput = c_ekkrwcs(fact,dluval, hcoli, mrstrt, hinrow, mwork, nfirst);
	  kmxeta += xnewro - iput ;
	  xnewro = iput - 1;
	  ++ncompactions;

	  --ncompactions;
	  if (xnewro + nspare + ndense * ndense >= lstart) {
	    c_ekkizero( nrow, reinterpret_cast<int *> (maction+1));
	  }
	  else {
	    xnewro += nspare;
	    c_ekkrwct(fact,dluval, hcoli, mrstrt, hinrow, mwork,
		    rlink, maction, dvalpv,
		    nlast,  xnewro);
	    kmxeta += xnewro ;
	    if (nnentu + nnentl > nrow * 5 &&
		(ndense*ndense)>(nnentu+nnentl)>>2 &&
		!if_sparse_update) {
	      fact->ndenuc = ndense;
	    }
	    irtcod = c_ekkcmfd(fact,
			     (reinterpret_cast<int*>(dvalpv)+1),
			     rlink, clink,
			     (reinterpret_cast<int*>(maction+1))+1,
			     nnetas,
			     &nnentl, &nnentu,
			     nsingp);
	    /* irtcod == 0 || irtcod == 10 */
	    /* 10 == found 0.0 pivot */
	    goto L1050;
	  }
	}
      } else {
	/* say not enough room */
	/*printf("no room %d\n",ndense);*/
	if (1) {
	  /* return and increase size of etas if possible */
	  if (!noRoomForDense) {
	    int etasize =CoinMax(4*fact->nnentu+(nnetas-fact->nnentl)+1000,fact->eta_size);
	    noRoomForDense=ndense;
	    fact->eta_size=CoinMin(static_cast<int>(1.2*fact->eta_size),etasize);
	    if (fact->maxNNetas>0&&fact->eta_size>
		fact->maxNNetas) {
	      fact->eta_size=fact->maxNNetas;
	    }
	  }
	}
      }
    }
#endif	/* C_EKKCMFY */
  }

 L1050:
  {
    int iput = c_ekkrwcs(fact,dluval, hcoli, mrstrt, hinrow, mwork, nfirst);
    kmxeta += xnewro - iput;
    xnewro = iput - 1;
    ++ncompactions;
  }

  nnentu = xnewro;
  /* save order of row copy for c_ekkshfv */
  mwork[nrow+1].pre = nfirst;
  mwork[nrow+1].suc = nlast;

  fact->nnentl = nnentl;
  fact->nnentu = nnentu;
  fact->kmxeta = kmxeta;
  *xnewrop = xnewro;
  *ncompactionsp = ncompactions;

  return (irtcod);
} /* c_ekkcmfc */
#endif
