/* ************************************************************
%                            [ADA,absd] = getada3(ADA, A,Ajc1,Aord, udsqr,K)
% GETADA3  Compute ADA(i,j) = (D(d^2)*A.t(:,i))' *A.t(:,j),
%   and exploit sparsity as much as possible.
%   absd - length m output vector, containing
%     absd(i) = abs((D(d^2)*A.t(:,i))' *abs(A.t(:,i)).
%     Hence, diag(ADA)./absd gives a measure of cancelation (in [0,1]).
%
% SEE ALSO sedumi, getada1, getada2
% ******************** INTERNAL FUNCTION OF SEDUMI ********************
  
   function [ADA,absd] = getada3(ADA, A,Ajc1,Aord, udsqr,K)

    This file is part of SeDuMi 1.05
    Copyright (C) 2001 Jos F. Sturm
      Dept. Econometrics & O.R., Tilburg University, the Netherlands.
      Supported by the Netherlands Organization for Scientific Research (NWO).
    Affiliation SeDuMi 1.03 and 1.04Beta (2000):
      Dept. Quantitative Economics, Maastricht University, the Netherlands.
    Affiliations up to SeDuMi 1.02 (AUG1998):
      CRL, McMaster University, Canada.
      Supported by the Netherlands Organization for Scientific Research (NWO).
  
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
  
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
  
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

   ************************************************************ */

#include <string.h>
#include <math.h>
#include "mex.h"
#include "blksdp.h"

#define ADA_OUT myplhs[0]
#define ABSD_OUT myplhs[1]
#define NPAROUT 2

#define ADA_IN prhs[0]       /* sparsity struct ADA */
#define AT_IN prhs[1]        /* N x m sparse At */
#define AJC1_IN prhs[2]      /* start of PSD blocks in At */
#define AORD_IN prhs[3]
#define UDSQR_IN prhs[4]     /* Scale data */
#define K_IN  prhs[5]
#define NPARIN 6

/* ========================= G E N E R A L ========================= */

void spzeros(double *x,const int *xir,const int n)
{
  int i;
  for(i = 0; i < n; i++)
    x[xir[i]] = 0.0;
}

/* ************************************************************
   PROCEDURE exmerge - mergeing 2 exclusive, increasing integer arrays.
   INPUT
     x - length nx array, increasing entries.
     y - length ny array, its entries are increasing, and do not occur in x.
     nx,ny - order of x and y.
   OUTPUT
     z - length nx+ny vector
   WORK
     cwork - length ny
     iwork - length ny+2+floor(log_2(1+ny)).
   ************************************************************ */
void exmerge(int *x, const int *y, const int nx, const int ny,
             const int iwsize, char *cwork, int *ipos)
{
  int i,j,inz;
/* ------------------------------------------------------------
   Search all "insertion" positions of y-entries in list x
   ------------------------------------------------------------ */
  intmbsearch(ipos,cwork, x,nx,y,ny, ipos+ny+2,iwsize-(ny+2));
/* ------------------------------------------------------------
   Shift x-entries down to make room for y-entries
   ------------------------------------------------------------ */
  for(i = ny; i > 0; i--){         /* shift i entries down */
    inz = ipos[i];
    if((j = ipos[i+1]-inz) > 0)
      memmove(x+inz+i,x+inz,j*sizeof(int));
  }
/* ------------------------------------------------------------
   Insert y-entries
   ------------------------------------------------------------ */
  ++ipos;
  for(i = 0; i < ny; i++)
    x[ipos[i]+i] = y[i];          /* add i, number prev. inserted y's */
#ifndef NDEBUG
  for(i = 1; i < nx+ny; i++)
    mxAssert(x[i] > x[i-1],"");
#endif
}

/* ************************************************************
   PROCEDURE cpspdiag -- Let d := diag(X), where X is sparse m x m.
   INPUT
     m - number of columns in ada
     x - contains COMPLETE SYMMETRIC sparsity structure,
   OUTPUT
     d - length m vector, diag(X).
   ************************************************************ */
void cpspdiag(double *d, const jcir x, const int m)
{
  int j, inz;
  const int *diagFound;
  
/* ------------------------------------------------------------
   For each column j: let dj = x(j,j)
   ------------------------------------------------------------ */
  for(j = 0; j < m; j++){
    if((diagFound = ibsearch(&j,x.ir+x.jc[j],x.jc[j+1]-x.jc[j])) != NULL){
      inz = diagFound - x.ir;                 /* convert pointer to index */
      d[j] = x.pr[inz];                       /* diag entry found */
    }
    else
      d[j] = 0.0;                   /* no diag entry -> d[j] = 0.0 */
  }
}

/* ************************************************************
   PROCEDURE spmakesym -- Let X := X+X'-diag(diag(X)), with X a
      real sparse square matrix.
   INPUT
     m - number of columns in ada
   UPDATED
     x - on input, contains COMPLETE SYMMETRIC sparsity structure,
         and the values (possibly 0's on some locations). On return,
         X = X+X'-diag(diag(X)), i.e. X is symmetrized, and the off-
         diagonal entries are "DUPLICATED" (allowing part in xij and xji).
   WORK
     iwork - length m array of integers. Points to "below row j"
       part of columns (trilstart). (initial contents irrelevant)
   ************************************************************ */
void spmakesym(jcir x, const int m, int *iwork)
{
  int i, j, inz, jend;
  double xij;
  
/* ------------------------------------------------------------
   Initialize: let iwork(0:m-1) = ada.jc(0:m-1)
   ------------------------------------------------------------ */
  memcpy(iwork, x.jc, m * sizeof(int));   /* don't copy x.jc[m] */
/* ------------------------------------------------------------
   For each column j:   for each index i > j:
   let xij = x(i,j) + x(j,i).
   Let iwork point to next nonzero in col i
   Guard: x.ir[iwork(i)] >= j for all i >= j.
   ------------------------------------------------------------ */
  for(j = 0; j < m; j++){
    jend = x.jc[j+1];                        /* Let [inz,jend) be tril(:,j) */
    inz = iwork[j];
    if(inz < jend){
      if(x.ir[inz] == j)                         /* skip diagonal entry */
        inz++;
      while(inz < jend){                         /* off-diagonal entries */
        i = x.ir[inz];
        xij = x.pr[iwork[i]] + x.pr[inz];  /* xji + xij */
        x.pr[iwork[i]++] = xij;
        x.pr[inz++] = xij;
      }
    }
  }
}

/* ************************************************************
   PROCEDURE dzblkpartit
   INPUT
     dzir - length dznnz array, NOT sorted
     xblk - length max(dzir) array, xblk(dzir) maps into 0:nblk-1.
     dznnz - order of dzir
     nblk - 1+max(xblk), number of blocks
   OUTPUT
     dzjc - length nblk+1 array. Has blockstarts so that all subscripts
       in dzir fit in the resulting partition.
   ************************************************************ */
void dzblkpartit(int *dzjc, const int *dzir, const int *xblk,
                 const int dznnz, const int nblk)
{
  int i,j;
/* ------------------------------------------------------------
   Init dzjc = all-0
   ------------------------------------------------------------ */
  for(i = 0; i <= nblk; i++)
    dzjc[i] = 0;
/* ------------------------------------------------------------
   Pre-partition dzir(dznnz) space into blocks
   ------------------------------------------------------------ */
  ++dzjc;
  for(i = 0; i < dznnz; i++)
    dzjc[xblk[dzir[i]]]++;      /* accumulate nnz */
  j = 0;
  for(i = 0; i < nblk; i++){    /* cumsum */
    j += dzjc[i];
    dzjc[i] = j;
  }
  mxAssert(dzjc[nblk-1] == dznnz,"");
}    


/* ************************************************************
   PROCEDURE: getada3
   INPUT
     ada.{jc,ir} - sparsity structure of ada.
     At - sparse N x m matrix.
     udsqr - lenud vector containing D, D(ud.perm,ud.perm) = Ud'*Ud.
     Ajc1 - m int array, Ajc1(:,1) points to start of PSD nz's in At.
     dzjc - psdN+1, partition of dz rowsubscipts into PSD blocks.
     dzstructjc, dzstructir - sparse N x m matrix, giving NEW PSD-nonzero
       positions of At(:,perm(j)).
     blkstart - length(K.s): starting indices of PSD  blocks
     xblk - length psdDim array, with k = xblk(i-blkstart[0]) iff
       blkstart[k] <= i < blkstart[k+1], k=0:nblk-1.
       psdDim:=blkstart[end]-blkstart[0].
     psdNL - K.s in integer
     perm, invperm - length(m) array, ordering in which ADA should be computed,
       and its inverse. We compute in order triu(ADA(perm,perm)), but store
       at original places. OPTIMAL order: start with sparsest dzstruct.
     m  - order of ADA, number of constraints.
     lenud - blkstart[end] - blkstart[0], PSD dimension.
     pcK - pointer to cone K structure.
     rpsdN - sum(K.s(i) | i is real sym PSD block).
   UPDATED
     ada.pr - ada(i,j) += ai'*D(d^2; PSD)*aj. PSD-part. Only entries
       in triu(ADA(perm,perm) are affected.
   OUTPUT
     absd - length(m) vector, contains abs(aj)'*abs(D(d^2)*aj).
   WORKING ARRAYS
     fwork - work vector, size
         fwsiz = lenud + 2 * max(rMaxn^2, 2*hMaxn^2).
     iwork - integer work array, size 
         iwsiz = 2*psdN + dznnz + max(srchsize, max(nk(PSD))).
	 where dznnz = dzstructjc[m] and
         srchsize = 2+maxadd+floor(log(1+maxadd))
     cwork - maxadd, where maxadd = max(dzstructjc(i+1)-dzstructjc(i))
   ************************************************************ */
void getada3(jcir ada, double *absd, jcir At, const double *udsqr,
             const int *Ajc1, const int *dzjc,
             const int *dzstructjc, const int *dzstructir,
             const int *blkstart, const int *xblk, const int *psdNL,
             const int *perm, const int *invperm,
             const int m, const int lenud, const coneK *pcK,
             double *fwork, int fwsiz, int *iwork, int iwsiz,
             char *cwork)
{
  int i,j,k, knz,inz, dznnz, addnnz, permj, rsdpN, nblk, nnzbj;
  double *daj;
  double adaij, termj, absadajj;
  int *dzknnz, *dzir, *blksj;

  rsdpN = pcK->rsdpN;
/* ------------------------------------------------------------
   Partition working arrays
   int: dzknnz(psdN=length(K.s)), blksj(psdN), dzir(dznnz = dzstructjc[m]),
     iwork[iwsiz],
     with iwsiz = max(dznnz, max(nk(PSD))).
   double:    daj(lenud), fwork[fwsiz],
     with fwsiz = 2 * max(rMaxn^2, 2*hMaxn^2).
   ------------------------------------------------------------ */
  nblk = pcK->sdpN;
  dznnz = dzstructjc[m];
  if(dznnz <= 0)
    return;                                 /* nothing to do if no PSD*/
  dzknnz = iwork;                           /* dzknnz(nblk) */
  blksj = dzknnz + nblk;                    /* blksj(nblk) */
  dzir = blksj + nblk;                     /* dzir(dznnz) */
  iwork  = dzir + dznnz;                    /* iwork(iwsiz) */
  iwsiz -= 2*nblk + dznnz;
  if(iwsiz < MAX(pcK->rMaxn,pcK->hMaxn))
    mexErrMsgTxt("iwork too small in getada3()");
  daj    = fwork;                           /* lenfull */
  fwork = daj + lenud;                      /* fwsiz */
  fwsiz -= lenud;
  if(fwsiz < 2 * MAX(SQR(pcK->rMaxn),2 * SQR(pcK->hMaxn)))
    mexErrMsgTxt("fwork too small in getada3()");
/* ------------------------------------------------------------
   Make "lenfull" vector index valid into daj.
   ------------------------------------------------------------ */
  daj -= blkstart[0];     /* We'll use daj[blkstart[0]:end] */
/* ------------------------------------------------------------
   Initialize dzknnz = 0, meaning dz=[]. Later we will merge
   columns from dzstruct, with dz, and partition into selected blocks.
   ------------------------------------------------------------ */
  mxAssert(dznnz > 0, "");  /* we know that there exist nonempty PSD: */
  for(i = 0; i < nblk; i++)
    dzknnz[i] = 0;
  for(j = 1; dzstructjc[j] == 0; j++); /* 1st nonzero PSD constraint */
/* ============================================================
   MAIN getada LOOP: loop over nodes perm(0:m-1)
   ============================================================ */
  for(--j; j < m; j++){
    permj = perm[j];
/* ------------------------------------------------------------
   Make dzir: the PSD-nonzero locations, with pointers
   to the selected PSD blocks. nz-locs = merge(dzir,dzstruct(:,j)).
   ------------------------------------------------------------ */
    i = dzstructjc[j];
    while( i < dzstructjc[j+1]){
      k = xblk[dzstructir[i]];
      knz = i;                                /* add dzstructir(knz:i-1) */
      intbsearch(&i, dzstructir, dzstructjc[j+1], blkstart[k+1]);
      mxAssert(i > knz,"");
      exmerge(dzir+dzjc[k], dzstructir+knz, dzknnz[k],i-knz,iwsiz,
              cwork,iwork);
      dzknnz[k] += i-knz;                     /* number added */
    }
/* ------------------------------------------------------------
   Compute daj = P(d)*aj = vec(D*Aj*D).
   ------------------------------------------------------------ */
    nnzbj = spsqrscale(daj,blksj,dzjc,dzir,dzknnz, udsqr,
                       At.ir,At.pr,Ajc1[permj],At.jc[permj+1],
                       blkstart, xblk, psdNL, rsdpN, fwork, iwork);
    /* iwork(max(K.s)), fwork(2 * (rMaxn^2 + 2*hMaxn^2))*/
    mxAssert(nnzbj <= nblk, ""); /* number of nz-matrix-blocks */
/* ------------------------------------------------------------
   For all i with invpermi < j:
   ada_ij = a_i'*daj.
   ------------------------------------------------------------ */
    for(inz = ada.jc[permj]; inz < ada.jc[permj+1]; inz++){
      i = ada.ir[inz];
      if(invperm[i] <= j){
        adaij = ada.pr[inz];
        if(invperm[i] < j)
          for(knz = Ajc1[i]; knz < At.jc[i+1]; knz++)
            adaij +=  At.pr[knz] * daj[At.ir[knz]];
        else{         /* diag entry: absd[j] = sum(abs(aj.*daj)) */
          absadajj = adaij;
          for(knz = Ajc1[i]; knz < At.jc[i+1]; knz++){
            termj = At.pr[knz] * daj[At.ir[knz]];
            adaij +=  termj;
            absadajj += fabs(termj);
          }
          absd[permj] = absadajj;
        }
        ada.pr[inz] = adaij;
      }
    }
/* ------------------------------------------------------------
   Set daj = all-0
   ------------------------------------------------------------ */
    for(knz = 0; knz < nnzbj; knz++){
      i = blksj[knz];
      spzeros(daj,dzir+dzjc[i],dzknnz[i]);
    }
  } /* j = 0:m-1 */
  mxAssert(dzjc[nblk-1]+dzknnz[nblk-1] == dznnz,"");
}


/* ============================================================
   MEXFUNCTION
   ============================================================ */
/* ************************************************************
   PROCEDURE mexFunction - Entry for Matlab
   ************************************************************ */
void mexFunction(const int nlhs, mxArray *plhs[],
                 const int nrhs, const mxArray *prhs[])
{
  mxArray *myplhs[NPAROUT];
  coneK cK;
  const mxArray *MY_FIELD;
  int lenfull, lenud, m, i, j, k, fwsiz, iwsiz, dznnz, maxadd;
  const double *permPr, *Ajc1Pr, *blkstartPr, *udsqr;
  const int *dzstructjc, *dzstructir;
  double *fwork, *absd;
  int *blkstart, *iwork, *Ajc1, *psdNL, *xblk, *perm, *invperm, *dzjc;
  char *cwork;
  jcir At, ada;
/* ------------------------------------------------------------
   Check for proper number of arguments
   ------------------------------------------------------------ */
  if(nrhs < NPARIN)
    mexErrMsgTxt("getADA requires more input arguments.");
  if(nlhs > NPAROUT)
    mexErrMsgTxt("getADA produces less output arguments.");
/* ------------------------------------------------------------
   Disassemble cone K structure
   ------------------------------------------------------------ */
  conepars(K_IN, &cK);
/* ------------------------------------------------------------
   Compute some statistics based on cone K structure
   ------------------------------------------------------------ */
  lenud = cK.rDim + cK.hDim;                  /* for PSD */
  lenfull = cK.lpN +  cK.qDim + lenud;
/* ------------------------------------------------------------
   Allocate working array blkstart(|K.s|+1).
   ------------------------------------------------------------ */
  blkstart = (int *) mxCalloc(cK.sdpN + 1, sizeof(int));
/* ------------------------------------------------------------
   Translate blkstart from Fortran-double to C-int
   ------------------------------------------------------------ */
  if( (MY_FIELD = mxGetField(K_IN,0,"blkstart"))==NULL)  /*K.blkstart*/
    mexErrMsgTxt("Missing K.blkstart.");
  if(mxGetM(MY_FIELD) * mxGetN(MY_FIELD) != 2+cK.lorN+cK.sdpN)
    mexErrMsgTxt("Size mismatch K.blkstart.");
  blkstartPr = mxGetPr(MY_FIELD) + cK.lorN + 1;          /* point to start of PSD */
  for(i = 0; i <= cK.sdpN; i++){                            /* to integers */
    j = blkstartPr[i];
    blkstart[i] = --j;
  }
/* ------------------------------------------------------------
   INPUT sparse constraint matrix At:
   ------------------------------------------------------------ */
  if(mxGetM(AT_IN) != lenfull)                            /* At */
    mexErrMsgTxt("Size mismatch At");
  m = mxGetN(AT_IN);
  if(!mxIsSparse(AT_IN))
    mexErrMsgTxt("At should be sparse.");
  At.pr = mxGetPr(AT_IN);
  At.jc = mxGetJc(AT_IN);
  At.ir = mxGetIr(AT_IN);
/* ------------------------------------------------------------
   Get SCALING VECTOR: udsqr
   ------------------------------------------------------------ */
  if(mxGetM(UDSQR_IN) * mxGetN(UDSQR_IN) != lenud)             /* udsqr */
    mexErrMsgTxt("udsqr size mismatch.");
  udsqr = mxGetPr(UDSQR_IN);
/* ------------------------------------------------------------
   Get Ajc1
   ------------------------------------------------------------ */
  if(mxGetM(AJC1_IN)*mxGetN(AJC1_IN) != m)
    mexErrMsgTxt("Ajc1 size mismatch");
  Ajc1Pr = mxGetPr(AJC1_IN);
/* ------------------------------------------------------------
   DISASSEMBLE Aord structure: Aord.{dz,sperm}
   ------------------------------------------------------------ */
  if(!mxIsStruct(AORD_IN))
    mexErrMsgTxt("Aord should be a structure.");
  if( (MY_FIELD = mxGetField(AORD_IN,0,"dz")) == NULL)      /* Aord.dz */
    mexErrMsgTxt("Missing field Aord.dz.");
  if(mxGetN(MY_FIELD) < m)
    mexErrMsgTxt("Size mismatch Aord.dz.");
  if(mxGetM(MY_FIELD) != lenfull)
    mexErrMsgTxt("Aord.dz size mismatch");
  if(!mxIsSparse(MY_FIELD))
    mexErrMsgTxt("Aord.dz should be sparse.");
  dzstructjc = mxGetJc(MY_FIELD);
  dzstructir = mxGetIr(MY_FIELD);
  if( (MY_FIELD = mxGetField(AORD_IN,0,"sperm")) == NULL)      /* Aord.sperm */
    mexErrMsgTxt("Missing field Aord.sperm.");
  if(mxGetM(MY_FIELD) * mxGetN(MY_FIELD) != m)
    mexErrMsgTxt("Aord.sperm size mismatch");
  permPr = mxGetPr(MY_FIELD);
/* ------------------------------------------------------------
   Allocate output matrix ADA as a duplicate of ADA_IN:
   ------------------------------------------------------------ */
  if(mxGetM(ADA_IN) != m || mxGetN(ADA_IN) != m)
    mexErrMsgTxt("Size mismatch ADA.");
  if(!mxIsSparse(ADA_IN))
    mexErrMsgTxt("ADA should be sparse.");
  ADA_OUT = mxDuplicateArray(ADA_IN);                   /* ADA = ADA_IN */
  ada.jc = mxGetJc(ADA_OUT);
  ada.ir = mxGetIr(ADA_OUT);
  ada.pr = mxGetPr(ADA_OUT);
/* ------------------------------------------------------------
   Create output vector absd(m)
   ------------------------------------------------------------ */
  ABSD_OUT = mxCreateDoubleMatrix(m,1,mxREAL);
  absd = mxGetPr(ABSD_OUT);
/* ------------------------------------------------------------
   The following ONLY if there are PSD blocks:
   ------------------------------------------------------------ */
  if(cK.sdpN > 0){
    maxadd = dzstructjc[1];
    for(i = 1; i < m; i++)
      if(dzstructjc[i+1] > dzstructjc[i] + maxadd)
        maxadd = dzstructjc[i+1] - dzstructjc[i];
/* ------------------------------------------------------------
   ALLOCATE integer work array iwork(iwsiz), with
   iwsiz = MAX(m, 2*nblk + dznnz +
     max(maxadd+2+log_2(1+maxadd), max(nk(PSD)))),
   where dznnz = dzstructjc[m].
   ------------------------------------------------------------ */
    dznnz = dzstructjc[m];
    iwsiz = floor(log(1+maxadd)/log(2));              /* double to int */
    iwsiz += maxadd + 2;
    iwsiz = 2*cK.sdpN + dznnz + MAX(iwsiz,MAX(cK.rMaxn,cK.hMaxn));
    iwork = (int *) mxCalloc(MAX(iwsiz,m), sizeof(int));
/* ------------------------------------------------------------
   ALLOCATE integer working arrays:
   Ajc1(m) psdNL[cK.sdpN], dzjc(cK.sdpN+1), perm(m), invperm(m), xblk(lenud).
   cwork(maxadd).
   ------------------------------------------------------------ */
    Ajc1 = (int *) mxCalloc(MAX(m,1), sizeof(int));
    psdNL = (int *) mxCalloc(1+2*cK.sdpN + lenud, sizeof(int));
    xblk = psdNL + cK.sdpN;    /* Not own alloc: we'll subtract blkstart[0] */
    dzjc = xblk + lenud;       /*dzjc(sdpN+1) */
    perm = (int *) mxCalloc(MAX(2 * m,1), sizeof(int));
    invperm = perm + m;                                 /* invperm(m) */
    cwork = (char *) mxCalloc(MAX(1,maxadd), sizeof(char));
/* ------------------------------------------------------------
   ALLOCATE float working array:
   fwork[fwsiz] with fwsiz = lenud + 2 * max(rMaxn^2, 2*hMaxn^2).
   ------------------------------------------------------------ */
    fwsiz = lenud + 2 * MAX(SQR(cK.rMaxn),2*SQR(cK.hMaxn));
    fwork  = (double *) mxCalloc(MAX(fwsiz,1), sizeof(double));
/* ------------------------------------------------------------
   perm to integer C-style
   ------------------------------------------------------------ */
    for(i = 0; i < m; i++){
      j = permPr[i];
      perm[i] = --j;
    }
/* ------------------------------------------------------------
   Let invperm(perm) = 0:m-1.
   ------------------------------------------------------------ */
    for(i = 0; i < m; i++)
      invperm[perm[i]] = i;
/* ------------------------------------------------------------
   Let psdNL = K.s in integer, Ajc1 = Ajc1Pr in integer.
   ------------------------------------------------------------ */
    for(i = 0; i < cK.sdpN; i++)                /* K.s */
      psdNL[i] = cK.sdpNL[i];
    for(i = 0; i < m; i++)
      Ajc1[i] = Ajc1Pr[i];
/* ------------------------------------------------------------
   Let k = xblk(j-blkstart[0]) iff
   blkstart[k] <= j < blkstart[k+1], k=0:nblk-1.
   ------------------------------------------------------------ */
    j = blkstart[0];
    xblk -= j;     /* Make blkstart[0]:blkstart[end] valid indices */
    for(k = 0; k < cK.sdpN; k++){
      i = blkstart[k+1];
      while(j < i)
        xblk[j++] = k;
    }
/* ------------------------------------------------------------
   ACTUAL COMPUTATION: handle constraint aj=At(:,perm(j)), j=0:m-1.
   ------------------------------------------------------------ */
    dzblkpartit(dzjc, dzstructir, xblk, dznnz, cK.sdpN);
    getada3(ada, absd, At,udsqr,Ajc1, dzjc,dzstructjc,dzstructir, blkstart,
            xblk,psdNL, perm,invperm, m,lenud, &cK, fwork,fwsiz,
            iwork,iwsiz, cwork);
/* ------------------------------------------------------------
   RELEASE WORKING ARRAYS (for PSD blocks only).
   ------------------------------------------------------------ */
    mxFree(fwork);
    mxFree(cwork);
    mxFree(perm);
    mxFree(psdNL);
    mxFree(Ajc1);
  }  /* ~isempty(K.s) */
/* ------------------------------------------------------------
   If no PSD-blocks, than we merely compute absd = diag(ADA)
   ALLOCATE integer work array iwork(m), with
   ------------------------------------------------------------ */
  else{
    iwork = (int *) mxCalloc(MAX(1,m), sizeof(int));
    cpspdiag(absd, ada,m);
  }
/* ------------------------------------------------------------
   Let ADA = (ADA+ADA')/2, so that it gets symmetric.
   ------------------------------------------------------------ */
  spmakesym(ada,m,iwork);         /* uses iwork(m) */
/* ------------------------------------------------------------
   RELEASE WORKING ARRAYS iwork and blkstart.
   ------------------------------------------------------------ */
  mxFree(iwork);
  mxFree(blkstart);
/* ------------------------------------------------------------
   Copy requested output parameters (at least 1), release others.
   ------------------------------------------------------------ */
  i = MAX(nlhs, 1);
  memcpy(plhs,myplhs, i * sizeof(mxArray *));
  for(; i < NPAROUT; i++)
    mxDestroyArray(myplhs[i]);
}
