/* =========================================================================
   ================================= pasa ==================================
   =========================================================================
       ________________________________________________________________
      |Solve an optimization problem with linear and bound constraints |
      |                                                                |
      |      min f(x)  subject to  lo <= x <= hi,  Bl <= Ax <= Bu      |
      |                                                                |
      |  An active set method is employed. The algorithm uses the      |
      |  gradient projection method to identify active constraints     |
      |  and a projected conjugate gradient algorithm in an active     |
      |  face of the constraint polyhedron.                            |
      |                                                                |
      |                Version 1.0  (September 1, 2019)                |
      |                        William W. Hager                        |
      |                   Department of Mathematics                    |
      |                     University of Florida                      |
      |                 Gainesville, Florida 32611 USA                 |
      |               https://people.clas.ufl.edu/hager/               |
      |                         hager@ufl.edu                          |
      |                                                                |
      |                 Copyright by William W. Hager                  |
      |________________________________________________________________|

       ________________________________________________________________
      |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., 51 Franklin St, Fifth Floor, Boston, |
      |MA  02110-1301  USA                                             |
      |________________________________________________________________|

      Alternative licenses are also available.
      Please contact William Hager for details.
*/

#include "pasa.h"

int pasa
(
    PASAdata  *pasadata /* Structure containing pasa input data, it is
                           initialized using pasa_setup and then modified
                           to describe the user's problem */
)
{
    int Aexists, exponent, loExists, hiExists, Bounds, fixed, ProbType,
        repeat, status, toggle, use_napheap, use_pproj, use_penalty ;
    PASAINT Afixed, Asize, i, j, k, kold, l, mem, nfree, ni, nr, ns, nsing,
            p0, p, q, ucol, *Ai, *Ap, *colperm, *ifree, *iwork,
           *ineqList, *rowList, *si, *sj, *singcount, *singperm,
           *row_sing ;
    PASAFLOAT UBl, UBu, r, s, t, u, *Ax, *cgwork, *b, *Bl, *Bu, *c, *sc,
              *singc, *lo, *slo, *singlo, *hi, *shi, *singhi, *sx, *temp, *work;
    char const FT [] = "FT" ;
    PASAcom Com ;
    PASAhess Hess ; /* Hessian structure */
    PASAcopy Copy ; /* copy of problem data in pproj format */
    PPdata ppdataDEBUG ; /* input data for PPROJ */
    PPprob *Prob ;

    status = PASA_OK ;
    /* extract the problem data from the pasadata structure */
    PASAINT            nrow = pasadata->nrow ;
    PASAINT            ncol = pasadata->ncol ;
    PASAINT         *userAp = pasadata->Ap ;
    PASAINT         *userAi = pasadata->Ai ;
    PASAFLOAT       *userAx = pasadata->Ax ;
    PASAFLOAT       *userlo = pasadata->lo ;
    PASAFLOAT       *userhi = pasadata->hi ;
    PASAFLOAT       *userBl = pasadata->bl ;
    PASAFLOAT       *userBu = pasadata->bu ;
    PASAFLOAT        *userc = pasadata->c ;
    PASAFLOAT        *userd = pasadata->d ;
    PASAFLOAT        *usera = pasadata->a ;
    PASAFLOAT            *x = pasadata->x ;
    PASAFLOAT       *lambda = pasadata->lambda ;
    PASAparms        *Parms = pasadata->Parms ;
    PASAstats        *Stats = pasadata->Stats ;
    PASAparm      *pasaparm = Parms->pasa ;
    PASAstat      *pasastat = Stats->pasa ;
    CGparm          *cgparm = Parms->cg ;
    CGstat          *cgstat = Stats->cg ;
    PPparm       *pprojparm = Parms->pproj ;
    PPstat       *pprojstat = Stats->pproj ;
    NAPparm        *napparm = Parms->napheap ;
    NAPstat        *napstat = Stats->napheap ;
    PASAFLOAT      grad_tol = pasaparm->grad_tol ;
    PASAFLOAT        *xWork = pasadata->xWork ;
    PASAINT          *iWork = pasadata->iWork ;
    int const    PrintLevel = pasaparm->PrintLevel ;
    int const  GradProjOnly = pasaparm->GradProjOnly ;
    int const            sI = sizeof (PASAINT) ;
    int const            sF = sizeof (PASAFLOAT) ;
    NAPdata        *napdata = pasadata->napdata ;
    PPdata          *ppdata = pasadata->ppdata ;
    CGdata          *cgdata = pasadata->cgdata ;

    Com.pasadata  = pasadata ;
    Com.Stats     = Stats ;
    Com.pasaparm  = pasaparm ;
    Com.pasastat  = pasastat ;
    Com.napparm   = napparm ;
    Com.napstat   = napstat ;
    Com.pprojparm = pprojparm ;
    Com.pprojstat = pprojstat ;
    Com.cgparm    = cgparm ;
    Com.cgstat    = cgstat ;
    Com.Copy      = &Copy ;
    Com.Hess      = &Hess ;
    Com.ucol      = ucol = ncol ; /* user specified columns */
    Com.Copy->col_start = NULL ;
    pasastat->grad_tol = grad_tol ;

    Stats->use_pasa    = FALSE ;
    Stats->use_napheap = FALSE ;
    Stats->use_pproj   = FALSE ;
    Stats->use_cg      = FALSE ;

    if ( ncol <= 0 )
    {
        status = PASA_PROBLEM_DIMENSION_NOT_GIVEN ;
        pasa_wrapup (status, TRUE, &Com) ;
        return (status) ;
    }
    /* print the pasa parameters if requested by user */
    int const PrintParm = pasaparm->PrintParm ;
    if ( PrintParm )
    {
        pasa_print_parm (pasadata) ;
    }

    Com.temp_userBl    = NULL ;
    Com.temp_userBu    = NULL ;
    Com.temp_ifree     = NULL ;

    i = (pasaparm->UNC  == TRUE) + (pasaparm->BNC     == TRUE) +
        (pasaparm->LP   == TRUE) + (pasaparm->QP      == TRUE) +
        (pasaparm->NL   == TRUE) + (pasaparm->NAPSACK == TRUE) +
        (pasaparm->PROJ == TRUE) ;
    if ( i > 1 )
    {
        status = PASA_PROBLEM_OVERSPECIFIED ;
        pasa_wrapup (status, TRUE, &Com) ;
        return (status) ;
    }

    if ( i == 1 ) /* user uniquely specified the problem classification */
    {
        if ( PrintLevel )
        {
            printf ("User specified problem classification:\n") ;
        }
        if      ( pasaparm->UNC      == TRUE ) ProbType = PASA_UNC ;
        else if ( pasaparm->BNC      == TRUE ) ProbType = PASA_BNC ;
        else if ( pasaparm->LP       == TRUE ) ProbType = PASA_LP ;
        else if ( pasaparm->QP       == TRUE ) ProbType = PASA_QP ;
        else if ( pasaparm->NL       == TRUE ) ProbType = PASA_NL ;
        else if ( pasaparm->NAPSACK  == TRUE ) ProbType = PASA_NAPSACK ;
        else                                   ProbType = PASA_PROJ ;
    }
    else
    {
        if ( ((pasadata->cg_hprod != NULL) || (pasadata->hprod != NULL))
              && (pasadata->value != NULL) )
        {
            if ( PrintLevel )
            {
                printf ("\nWarning: There is an ambiguity in the objective\n"
                          "         since the user provides both a pointer\n"
                          "         to a routine to evaluate the objective,\n"
                          "         and another pointer to a routine to\n"
                          "         evaluate a quadratic. PASA assumes that\n"
                          "         the function is a quadratic.\n\n") ;
            }
        }
        if ( (pasadata->c != NULL)        && (pasadata->hprod == NULL) &&
             (pasadata->cg_hprod == NULL) && (pasadata->value != NULL) )
        {
            if ( PrintLevel )
            {
                printf ("\nWarning: There is an ambiguity in the objective\n"
                          "         since the user provides both a pointer\n"
                          "         to a routine to evaluate the objective,\n"
                          "         and another pointer to a linear cost\n"
                          "         vector. PASA assumes the objective is\n"
                          "         linear and uses the given linear cost\n"
                          "         vector.\n\n") ;
            }
        }
        status = pasa_autodetect (pasadata) ;
        if ( status > 0 )
        {
            pasa_wrapup (status, TRUE, &Com) ;
            return (status) ;
        }
        /* if status < 0, it defines the problem classification */
        ProbType = status ;
        if ( PrintLevel )
        {
            printf ("Problem classification deduced by autodetect:\n") ;
        }
    }
    if ( PrintLevel)
    {
        char *mess [7] ;
        mess [0] = "unconstrained optimization" ;
        mess [1] = "bound constrained optimization" ;
        mess [2] = "linear program" ;
        mess [3] = "quadratic program" ;
        mess [4] = "nonlinear program with polyhedral constraints" ;
        mess [5] = "separable convex quadratic objective with knapsack "
                   "constraints" ;
        mess [6] = "projection onto a polyhedron" ;
        printf ("%s\n", mess [PASA_UNC-ProbType]) ;
    }

    int const     UNC = (ProbType == PASA_UNC)     ? TRUE : FALSE ;
    int const     BNC = (ProbType == PASA_BNC)     ? TRUE : FALSE ;
    int const      LP = (ProbType == PASA_LP)      ? TRUE : FALSE ;
    int const      QP = (ProbType == PASA_QP)      ? TRUE : FALSE ;
    int const      NL = (ProbType == PASA_NL)      ? TRUE : FALSE ;
    int const NAPSACK = (ProbType == PASA_NAPSACK) ? TRUE : FALSE ;
    int const    PROJ = (ProbType == PASA_PROJ)    ? TRUE : FALSE ;
    Com.QP = QP ;
    Com.LP = LP ;

    /* check that each problem type has the necessary data */
    if ( UNC || BNC || NL )
    {
        /* both the objective and its gradient need to be provided */
        if ( (pasadata->value == NULL) || (pasadata->grad == NULL) )
        {
            status = PASA_FUNCTION_VALUE_OR_GRADIENT_MISSING ;
            pasa_wrapup (status, TRUE, &Com) ;
            return (status) ;
        }
    }

    if ( NL || QP || LP || PROJ )
    {
        /* if a sparse matrix is present, then all 3 arrays are needed */
        if ( (userAp != NULL) || (userAi != NULL) || (userAx != NULL) )
        {
            if ( (userAp == NULL) || (userAi == NULL) || (userAx == NULL) )
            {
                status = PASA_MATRIX_INCOMPLETE ;
                pasa_wrapup (status, TRUE, &Com) ;
                return (status) ;
            }
        }

        /* check if the matrix is given, but not the right/left side */
        if ( (userBl == NULL) && (userBu == NULL) && (userAp != NULL) )
        {
            status = PASA_MATRIX_GIVEN_BUT_RHS_MISSING ;
            pasa_wrapup (status, TRUE, &Com) ;
            return (status) ;
        }

        /* check if the right/left side are given, but not the matrix */
        if ( ((userBl != NULL) || (userBu != NULL)) && (userAp == NULL) )
        {
            status = PASA_RHS_GIVEN_BUT_MATRIX_MISSING ;
            pasa_wrapup (status, TRUE, &Com) ;
            return (status) ;
        }

        /* if the problem is a QP and it has constraints, then hprod
           must be given (as opposed to cg_hprod which is specific to cg) */
        if ( QP )
        {
            if ( ((userAp != NULL) || (usera != NULL) || (userlo != NULL) ||
                  (userhi != NULL)) && (pasadata->hprod == NULL) )
            {
                status = PASA_WRONG_HPROD_GIVEN ;
                pasa_wrapup (status, TRUE, &Com) ;
                return (status) ;
            }
            if ( pasadata->c == NULL )
            {
                status = PASA_QUADRATIC_OBJECTIVE_NO_LINEAR_TERM ;
                pasa_wrapup (status, TRUE, &Com) ;
                return (status) ;
            }
        }
    }

    /* if nrow is EMPTY (not specified), then try to determine its value */
    if ( nrow == EMPTY )
    {
        if      ( pasadata->a != NULL )
        {
            nrow = 1 ;
        }
        else if (    userAp != NULL   )
        {
            nrow = 1 + pasa_isup (userAi, userAp[ncol]);
        }
        /* else problem either unconstrained or bound constrained */
        else
        {
            nrow = 0 ;
        }
    }

    /* if there is a dual multiplier, but it is NULL, then allocate it,
       and return it to the user in pasadata->lambda. In pasadata, we
       also store a pointer in lambda_created to any allocated array.
       pasa_terminate will free an array that was allocated in pasa. */
    if ( (nrow > 0) && (lambda == NULL) )
    {
        if ( pasaparm->use_lambda == TRUE )
        {
            status = PASA_LAMBDA_IS_NULL_BUT_USE_LAMBDA_IS_TRUE ;
            pasa_wrapup (status, TRUE, &Com) ;
            return (status) ;
        }
        lambda = pasadata->lambda = pasadata->lambda_created =
                   (PASAFLOAT *) pasa_malloc (&status, nrow, sizeof (PPFLOAT)) ;
        if ( status == PASA_OUT_OF_MEMORY )
        {
            pasa_wrapup (status, TRUE, &Com) ;
            return (status) ;
        }
    }

    /* If the user did not allocate memory for the solution, then allocate
       memory. If the problem also requires a starting guess, then set
       the guess to zero. */
    if ( x == NULL )
    {
        x = pasadata->x = pasadata->x_created =
            (PASAFLOAT *) pasa_malloc (&status, ncol, sF) ;
        if ( status == PASA_OUT_OF_MEMORY )
        {
            pasa_wrapup (status, TRUE, &Com) ;
            return (status) ;
        }
        /* if the problem requires a starting guess, then set it to zero */
        if ( UNC || BNC || QP || NL )
        {
            pasa_initx (x, PASAZERO, ncol) ;
            if ( PrintLevel )
            {
                printf ("Warning: This problem class requires a starting\n"
                        "         guess for the solution. Since no starting\n"
                        "         guess was given, we use zero.\n") ;
            }
        }
    }
    Com.x = x ; /* user's guess (later: returned solution) */

    /* directly call napheap if the problem has the right structure */
    if ( NAPSACK || ((nrow == 1) &&
         ((userd != NULL) || ((userc != NULL) && (pasadata->hprod == NULL)))) )
    {
        napdata->n = ncol ;
        napdata->x = x ;
        napdata->d = userd ;
        if ( usera == NULL ) /* matrix input in sparse format */
        {
            napdata->a = (PASAFLOAT *) pasa_malloc (&status, ncol, sF) ;
            if ( status == PASA_OUT_OF_MEMORY )
            {
                pasa_wrapup (status, TRUE, &Com) ;
                return (status) ;
            }
            pasa_initx (napdata->a, PASAZERO, ncol) ;
            p = 0 ;
            for (j = 0; j < ncol; j++)
            {
                q = userAp [j+1] ;
                if ( p < q )
                {
                    napdata->a [j] = userAx [p] ;
                    p = q ;
                }
            }
        }
        else napdata->a = usera ;

        napdata->lo = userlo ;
        napdata->hi = userhi ;

        if ( userc != NULL )
        {
            napdata->y = (PASAFLOAT *) pasa_malloc (&status, ncol, sF) ;
            if ( status == PASA_OUT_OF_MEMORY )
            {
                pasa_wrapup (status, TRUE, &Com) ;
                return (status) ;
            }
            /* note the linear cost vector in napheap has a negative sign */
            pasa_scale (napdata->y, pasadata->c, -PASAONE, ncol) ;
        }
        else napdata->y = NULL ;

        if ( userBl == NULL )
        {
             napdata->blo = -PASAINF ;
        }
        else
        {
             napdata->blo = *userBl ;
        }

        if ( userBu == NULL )
        {
             napdata->bhi = PASAINF ;
        }
        else
        {
             napdata->bhi = *userBu ;
        }

        /* set some parameters */
        if ( pasaparm->use_lambda )
        {
            napdata->lambda = -*lambda ; /* napheap uses opposite sign mult */
        }

        if ( PrintParm )
        {
            napheap_print_parm (napdata) ;
        }

        /* call napheap */
        status = napheap (napdata) ;

        /* compute the objective value */
        s = t = PASAZERO ;
        int const cExists = (userc == NULL) ? FALSE : TRUE ;
        if ( userd != NULL ) /* quadratic objective */
        {
            for (j = 0; j < ncol; j++)
            {
                PASAFLOAT const xj = x [j] ;
                t += xj*xj*pasadata->d [j] ;
                if ( cExists ) s += xj*pasadata->c [j] ;
            }
            pasastat->f = .5*t + s ;
        }
        else /* linear objective */
        {
            for (j = 0; j < ncol; j++)
            {
                s += x [j]*pasadata->c [j] ;
            }
            pasastat->f = s ;
        }

        /* solution computed to machine precision accuracy */
        pasastat->err = PASAZERO ;

        /* print the error status if requested */
        pasa_wrapup (status, TRUE, &Com) ;

        /* if user wants statistics, print them */
        Stats->use_napheap = TRUE ;
        if ( pasaparm->PrintStat == TRUE ) pasa_print_stats (pasadata) ;

        /* return the multiplier to pasa, flip sign for consistency with pasa */
        pasadata->lambda [0] = -napdata->lambda ;

        /* free y if it was created */
        if ( userc != NULL ) pasa_free (napdata->y) ;

        /* free a if it was created */
        if ( usera == NULL ) pasa_free (napdata->a) ;

        return (status) ;
    }

    /* directly call cg_descent if the problem is unconstrained */
    if ( UNC || (QP && (userAp == NULL) && (usera  == NULL) &&
                       (userlo == NULL) && (userhi == NULL)) )
    {
        cgdata->x        = x ;
        cgdata->n        = ncol ;
        cgdata->value    = pasadata->value ;
        cgdata->grad     = pasadata->grad ;
        cgdata->valgrad  = pasadata->valgrad ;
        cgdata->hess     = pasadata->cghess ;
        cgdata->hprod    = pasadata->cg_hprod ;
        cgdata->c        = pasadata->c ;
        cgparm->grad_tol = grad_tol ;
        if ( PrintParm == TRUE )
        {
            cg_print_parm (cgdata) ;
        }

        status = cg_descent (cgdata) ;

        pasastat->f   = cgstat->f ;
        pasastat->err = cgstat->err ;
        pasa_wrapup (status, TRUE, &Com) ;

        /* if user wants statistics, print them */
        Stats->use_cg = TRUE ;
        if ( pasaparm->PrintStat == TRUE )
        {
            cg_print_stat (cgdata) ;
        }
        return (status) ;
    }
 
    status = PASA_OK ;
    /* check if userBl or userBu need to be malloc'd and initialized */
    if ( nrow > 0 )
    {
        if ( userBl == NULL )
        {
            userBl = (PASAFLOAT *) pasa_malloc (&status, nrow, sF) ;
            Com.temp_userBl = userBl ;
            pasa_initx (userBl, -PASAINF, nrow) ;
        }
        if ( userBu == NULL )
        {
            userBu = (PASAFLOAT *) pasa_malloc (&status, nrow, sF) ;
            Com.temp_userBu = userBu ;
            pasa_initx (userBu, PASAINF, nrow) ;
        }
        if ( status == PASA_OUT_OF_MEMORY )
        {
            pasa_wrapup (status, TRUE, &Com) ;
            return (status) ;
        }
    }
    else
    {
        userBl = userBu = NULL ;
    }

#ifndef NDEBUG
    /* if debugging is used, save the user arrays in Com structure */
    Com.BL = userBl ;
    Com.BU = userBu ;
    Com.AX = userAx ;
    Com.AI = userAi ;
    Com.AP = userAp ;
    Com.LO = userlo ;
    Com.HI = userhi ;
#endif

    ni = 0 ;     /* number of strict inequalities */
    nsing = 0 ;  /* number of column singletons */

    /* initialize pasa statistics */
    pasastat->mcnf = 0 ;     /* function evaluations in main code */
    pasastat->mcng = 0 ;     /* gradient evaluations in main code */
    pasastat->gpit = 0 ;     /* number of iterations in grad_proj */
    pasastat->gpnf = 0 ;     /* function evaluations in grad_proj */
    pasastat->gpng = 0 ;     /* gradient evaluations in grad_proj */
    pasastat->agpit = 0 ;    /* number of iterations in active set grad_proj */
    pasastat->agpnf = 0 ;    /* function evaluations in active set grad_proj */
    pasastat->agpng = 0 ;    /* gradient evaluations in active set grad_proj */
    pasastat->nproject = 0 ; /* number of projections performed */
    /* Need the following parameters when statistics are printed */
    pasastat->gpmaxit = pasaparm->gpmaxit ;   /* max gradient projection iter */
    pasastat->maxsteps = pasaparm->maxsteps ; /* max attempts to satisfy line
                                                 search condition in grad_proj*/
    pasastat->err = PASAZERO ;

    if ( PrintLevel ) printf ("\n") ;
    if ( (nrow == 0) || ((userAp == NULL) && (usera == NULL)) )
    {
        Aexists = FALSE ; /* projection is simply trunction */
        Asize = 0 ;
        nrow = 0 ;
        use_napheap = FALSE ;
        use_pproj = FALSE ;
    }
    else
    {
        Aexists = TRUE ; /* either sparse matrix or dense "a" vector */
        if ( (userAp != NULL) && (usera != NULL) )
        {
            status = PASA_BOTH_A_AND_A_EXIST ;
            pasa_wrapup (status, TRUE, &Com) ;
            return (status) ;
        }
        i = (nrow == 1) && (pasaparm->use_napheap == TRUE) ;
        use_napheap = (i) ? TRUE  : FALSE ;
        use_pproj   = (i) ? FALSE : TRUE ;
        if ( userAp != NULL ) Asize = userAp [ncol] ; /* nnz in A */
        else                  Asize = ncol ;          /* dense a vector */
        if ( PrintLevel )
        {
            printf ("input matrix has %ld rows/ %ld cols/ %ld nz\n",
                    (LONG) nrow, (LONG) ncol, (LONG) Asize) ;
        }
    }
    Com.Aexists = Aexists ;
    Stats->use_napheap = Com.use_napheap = use_napheap ;
    Stats->use_pproj   = Com.use_pproj   = use_pproj ;
    Stats->use_cg      = Com.use_cg      = FALSE ;
    loExists = (userlo == NULL) ? FALSE : pasaparm->loExists;
    hiExists = (userhi == NULL) ? FALSE : pasaparm->hiExists ;
    int const user_loExists = loExists ;
    int const user_hiExists = hiExists ;

    /* The user's parameters may be changed during the operation of pasa.
       At the conclusion of the run, the user's original parameters are
       restored. Here we save a copy of parameters that may be changed
       by pasa. */
    if ( use_pproj )
    {
        Com.userparms.p_PrintStatus    = pprojparm->PrintStatus ;
        Com.userparms.p_grad_tol       = pprojparm->grad_tol ;
        Com.userparms.p_return_data    = pprojparm->return_data ;
        Com.userparms.p_use_prior_data = pprojparm->use_prior_data ;
        Com.userparms.p_loExists       = pprojparm->loExists ;
        Com.userparms.p_hiExists       = pprojparm->hiExists ;
        Com.userparms.p_getfactor      = pprojparm->getfactor ;
        Com.userparms.p_permute        = pprojparm->permute ;
        Com.userparms.p_start_guess    = pprojparm->start_guess ;
        Com.userparms.p_use_startup    = pprojparm->use_startup ;
        Com.userparms.p_LP             = pprojparm->LP ;
    }
    else if ( use_napheap )
    {
        Com.userparms.n_PrintStatus    = napparm->PrintStatus ;
        Com.userparms.n_return_data    = napparm->return_data ;
        Com.userparms.n_use_prior_data = napparm->use_prior_data ;
        Com.userparms.n_loExists       = napparm->loExists ;
        Com.userparms.n_hiExists       = napparm->hiExists ;
        Com.userparms.n_d_is_one       = napparm->d_is_one ;
    }

    nfree = ncol ;
    fixed = FALSE ;
    Afixed = 0 ; /* space in A associated with fixed variables */
    /* If both lo and hi exist, then the problem could have fixed variables.
       Check for fixed variables, and if it is found that lo/hi are
       -/+ infinity, then change the corresponding parameters
       loExists/hiExists to FALSE. */
    int boundchk = FALSE ;
    if ( (loExists == TRUE) && (hiExists == TRUE) )
    {
        boundchk = TRUE ;
        ifree = (PASAINT *) pasa_malloc (&status, ncol, sizeof(PASAINT)) ;
        Com.temp_ifree = ifree ;
        /* terminate if there is not enough memory */
        if ( status == PASA_OUT_OF_MEMORY )
        {
            pasa_wrapup (status, TRUE, &Com) ;
            return (status) ;
        }
        loExists = FALSE ;
        hiExists = FALSE ;
        nfree = 0 ; /* counts number of free variables */
        for (j = 0; j < ucol; j++)
        {
            t = userhi [j] - userlo [j] ;
            if ( t < PASAZERO )
            {
                pasastat->lobad = userlo [j] ;
                pasastat->hibad = userhi [j] ;
                pasastat->ibad  = j ;
                status = PASA_INVALID_VARIABLE_BOUNDS ;
                pasa_wrapup (status, TRUE, &Com) ;
                return (status) ;
            }
            else if ( t > PASAZERO )
            {
                if ( LP )
                {
                    k = userAp [j+1] - userAp [j] ;
                    if ( k <= 1 ) /* column singleton or zero column */
                    {
                        /* ncol is the number of free columns */
                        ncol-- ;
                        /* save the fixed variables at the end of ifree */
                        ifree [ncol] = j ;
                        if ( k == 1 )
                        {
                            /* removing this variable will remove some space
                               space from the matrix, update both the space
                               removed and the number of column singletons */
                            Afixed++ ;
                            nsing++ ; /* this variable is a column singleton */
                        }
                        /* else when k = 0, the variable can be removed from
                           the problem, but no space is removed */
                    }
                    else /* free variable and more than 1 entry in column */
                    {
                        if ( userhi [j] < PASAINF )
                        {
                            hiExists = TRUE ;
                        }
                        if ( userlo [j] > -PASAINF )
                        {
                            loExists = TRUE ;
                        }
                        ifree [nfree] = j ;
                        nfree++ ;
                    }
                }
                else /* free variable, problem not an LP */
                {
                    if ( userhi [j] < PASAINF )
                    {
                        hiExists = TRUE ;
                    }
                    if ( userlo [j] > -PASAINF )
                    {
                        loExists = TRUE ;
                    }
                    ifree [nfree] = j ;
                    nfree++ ;
                }
            }
            else /* t = 0 or nan, a fixed variable should be removed */
            {
                if ( t != t ) /* infinite bounds */
                {
                    pasastat->lobad = userlo [j] ;
                    pasastat->hibad = userhi [j] ;
                    pasastat->ibad  = j ;
                    status = PASA_INVALID_VARIABLE_BOUNDS ;
                    pasa_wrapup (status, TRUE, &Com) ;
                    return (status) ;
                }
                /* ncol is the number of free columns */
                ncol-- ;
                /* save the fixed variables at the end of ifree */
                if ( LP ) ifree [ncol] = -(j+1) ;
                else      ifree [ncol] = j ;
                /* evaluate the space in A associated with fixed variables */
                if ( Aexists == TRUE )
                {
                    Afixed += userAp [j+1] - userAp [j] ;
                }
            }
        }
    }

    /* even if bounds were not checked, an LP could have deleted columns
       due to column singletons */
    if ( LP && !boundchk )
    {
        nfree = 0 ; /* counts number of free variables */
        ifree = (PASAINT *) pasa_malloc (&status, ncol, sizeof(PASAINT)) ;
        Com.temp_ifree = ifree ;
        for (j = 0; j < ucol; j++)
        {
            if ( (k = (userAp [j+1] - userAp [j])) <= 1 )
            {
                /* ncol is the number of free columns */
                ncol-- ;
                /* save the fixed variables at the end of ifree */
                ifree [ncol] = j ;
                if ( k == 1 )
                {
                    /* count the number of column singletons */
                    nsing++ ;
                }
            }
            else
            {
                ifree [nfree] = j ;
                nfree++ ;
            }
        }
        Afixed = nsing ;
    }
    Asize -= Afixed ;

    if ( ncol < ucol )
    {
        fixed = TRUE ;
    }
    else if ( Com.temp_ifree != NULL )
    {
        pasa_free (ifree) ;
        Com.temp_ifree = NULL ;
    }

    int const singExists = (nsing > 0) ; /* TRUE if column singletons exist */

    if ( Aexists )
    {
        /* for an LP we may need to allocate some temporary variables */
        if ( singExists )
        {
            /* list of rows where the inequality is strict */
            ineqList = (PASAINT *) pasa_malloc (&status, 2*nrow+1, sI) ;

            if ( status == PASA_OUT_OF_MEMORY )
            {
                pasa_wrapup (status, TRUE, &Com) ;
                return (status) ;
            }
            /* total number of column singletons in each row */
            singcount = ineqList+nrow ;

            pasa_initi (singcount, (PASAINT) 0, nrow+1) ;
            singcount = singcount+1 ;
            /* singcount later converted to an array that points to start of
               each row with singletons. After using this pointer array to
               reorder the cost elements associated with each singleton,
               we set singcount = singcount-1 so that the pointer array again
               points to the start of each row. */
        }

        /* count the number of strict inequalities */
        for (i = 0; i < nrow; i++)
        {
            if ( (UBl = userBl [i]) < (UBu = userBu [i]) )
            {
                if ( singExists )
                {
                    ineqList [ni] = i ;
                    /* since the inequality will be converted to an equality
                       if there are column singletons, add the strict
                       inequalities into the singcount array */
                    singcount [i]++ ;
                }
                ni++ ;
            }
            else if ( (UBl > UBu) || (UBl >= PASAINF) )
            {
                pasastat->lobad = UBl ;
                pasastat->hibad = UBu ;
                pasastat->ibad  = i ;
                status = PASA_INVALID_LINEAR_CONSTRAINT_BOUNDS ;
                pasa_wrapup (status, TRUE, &Com);
                return (status) ;
            }
        }
    }

    /* for an LP, strict inequalities are converted to equalities
       when column singletons are present */
    const PASAINT nsingni = nsing + ni ;
    Com.nsing = nsing ;
    Com.ni = ni ;

    if ( PrintLevel >= 1 )
    {
        printf ("%ld free variables/ %ld fixed variables",
                (LONG) nfree, (LONG) ucol - ncol - nsing) ;
        if ( LP )
        {
            printf ("/ %ld column singletons", (LONG) nsing) ;
        }
        printf ("/ %ld total variables\n", (LONG) ucol) ;
        printf ("%ld strict inequalities/ %ld equalities\n",
                (LONG) ni, (LONG) nrow - ni) ;
        if ( (Aexists == TRUE) && Afixed )
        {
            printf ("removing fixed variables") ;
            if ( LP ) printf (" & column singletons") ;
            printf (" deletes %ld of %ld matrix elements\n",
                   (LONG) Afixed, (LONG) userAp [ucol]) ;
        }
    }

    Com.fixed = fixed ;
    Com.loExists = loExists ;
    Com.hiExists = hiExists ;
    if ( (loExists == TRUE) || (hiExists == TRUE) )
    {
        Bounds = TRUE ;
    }
    else
    {
        Bounds = FALSE ;
    }
    Com.Bounds = Bounds ;
    if ( PrintLevel >= 1 )
    {
        printf ("Aexists: %c/ loExists: %c/ hiExists: %c/ Bounds: %c/ "
                "use_napheap: %c/ use_pproj: %c\n",
                 FT[Aexists], FT[loExists], FT[hiExists], FT[Bounds],
                 FT[use_napheap], FT[use_pproj]) ;
        if ( !singExists ) printf ("\n") ;
    }

    /* if GradProjOnly is FALSE and the objective is not linear,
       then cg_descent will be used so set up the parm and stat structures */
    if ( (GradProjOnly == FALSE) && (LP == FALSE) )
    {
        Com.use_cg = Stats->use_cg = TRUE ;

        /* setup the cgdata structure */
        cgdata->value   = pasadata->value ;
        cgdata->grad    = pasadata->grad ;
        cgdata->valgrad = pasadata->valgrad ;

        /* note that cgdata->c, cgdata->hprod, and cgdata->hess are not
           used when the problem has constraints since the evaluation
           routines will use the information from pasadata */
        cgdata->c     = NULL ;
        cgdata->hprod = NULL ;
        cgdata->hess  = NULL ;

        /* save cg parameters that may be changed */
        Com.userparms.c_PrintStatus = cgparm->PrintStatus ;
        Com.userparms.c_PrintStat   = cgparm->PrintStat ;
        Com.userparms.c_PrintParm   = cgparm->PrintParm ;
        Com.userparms.c_fadjust     = cgparm->fadjust ;

        /* set the cg parameters */
        cgparm->PrintStatus = FALSE ;
        cgparm->PrintStat = FALSE ;
        cgparm->PrintParm = FALSE ;
        cgparm->fadjust = pasaparm->fadjust ;

        use_penalty = (Aexists) ? pasaparm->use_penalty : FALSE ;
        Com.use_penalty = use_penalty ;

        if ( PrintParm == TRUE )
        {
            cg_print_parm (cgdata) ;
        }

        /* initialize cg_descent statistics */
        cgstat->f = PASAINF ;   /* objective value */
        cgstat->err = PASAINF ; /* sup norm of gradient error */
        cgstat->iter  = 0 ;     /* total number of iterations */
        cgstat->nfunc = 0 ;     /* number of function evaluations */
        cgstat->ngrad = 0 ;     /* number of gradient evaluations */
        cgstat->IterSub = 0 ;   /* number of iterations in subspace */
        cgstat->NumSub =  0 ;   /* total number of subspaces */
    }

    /* If there are linear equalities/inequalities, then either napheap
       or pproj will be used. */
    if ( Aexists == TRUE )
    {
        /* if the matrix has only one row, use napheap if possible */
        if ( use_napheap == TRUE )
        {
            /* some parameters for napheap were set above, now set
               the remaining parameters */
            napparm->PrintStatus = FALSE ;
            napparm->d_is_one = TRUE ;
            napparm->return_data = TRUE ;
            napparm->loExists = loExists ;
            napparm->hiExists = hiExists ;

            if ( PrintParm )
            {
                napheap_print_parm (napdata) ;
            }
        }
        else /* use pproj */
        {
#ifndef NOPPROJ
            /* set pprojparm parameter's to match the values in pasa */
            pprojparm->PrintStatus = FALSE ;
            pprojparm->loExists = loExists ;
            pprojparm->hiExists = hiExists ;

            if ( pprojparm->cholmod == FALSE )
            {
                status = PASA_MUST_USE_CHOLMOD ;
                pasa_wrapup (status, TRUE, &Com) ;
                return (status) ;
            }

            if ( PrintParm )
            {
                pproj_print_parm (ppdata) ;
            }
#endif
        }
    }

    /* In PASA, variables associated with active constraints are removed
       from the problem and in PPROJ, we reorder unknowns to reduce fill-in.
       Consequently, when we evaluate the user's objective function, we
       need to reorder variables. Two different orderings are involved.
       If there are fixed variables or if Aexists (and PPROJ is employed),
       then we reorder using a permutation called colperm. If there are
       bounds on the variables (either lo and hi) and the active set
       methods are used (either activeGP or cg_descent), then we reorder
       with a permutation called ifree */
    Com.order_with_colperm = FALSE ;
    Com.order_with_ifree = FALSE ;
    if ( Bounds == TRUE )
    {
        Com.order_with_ifree = TRUE ; /* if location = AGP or CG */
    }
    if ( (fixed == TRUE) || (use_pproj == TRUE) )
    {
        Com.order_with_colperm = TRUE ;
        /* used if location = GP or
                  (location = AGP or CG and order_with_ifree is FALSE) */
    }

    /* Allocate PASAFLOAT work arrays.
       Note that the value of ncol provided by the user has been reduced
       to take into account any fixed variables. ucol denotes the
       original version of ncol specified by the user's input argument.
       pasaparm->M space is needed for storing the M values of the objective
       function at the M most recent iterates.  6*ncol space needed for
       the following arrays. The userx and userg arrays are due the
       the following: When polyhedral constraints are present, the
       rows and columns of A are reordered to help improve the sparsity
       in the linear algebra. Throughout the code, we work in this
       permuted coordinate system. Hence, when the user's function or
       gradient is evaluated, we need to permute to the user's coordinate
       system. Also, in the active set method, variables can be fixed
       at bounds. Currently, these fixed variables are removed from the
       problem. When the user's function or gradient is evaluated, we only
       copy the unfixed variable values into the user's coordinates.

       xnew = x + alpha*d
       gnew = gradient evaluated at xnew
       g    = gradient evaluated at x
       d    = search direction
       userx= x in the user's coordinate system
       userg= g in the user's coordinate system

       For a quadratic objective, we also need space to store:

       Qd   = Hessian times d

       The space allocation for CG is the same as the space allocation
       in the cg_descent code except that 4*ncol space is shared with
       the main pasa code.  The routine pproj for projecting
       a point onto a polyhedron handles its own memory internally.  */

    /* figure out space for float arrays and allocate a block of memory */
    if ( xWork == NULL )
    {
        i = 0 ;

        /* Com->d, userx, userg */
        i += ncol + 2*ucol ;

        /* for linear cost problems, need extra copy of the linear term */
        if ( LP == TRUE )
        {
            /* store a copy of c in Com.c since it will be scaled */
            i += ncol ;

            /* allocate space for ColScale, offset, and RowScale */
            i += 2*ncol + nrow ;

            if ( singExists )
            {
                /* space for sx (matrix element for singleton)
                             sc (cost element)
                            slo (lower bound)
                            shi (upper bound) */
                i += 4*nsingni ;

                /* singlo and singhi are sorted versions of slo and shi */
                i += 2*nsingni ;

                /* singc is the sorted version of sc */
                i += nsingni ;
            }
        }
        else /* LP is FALSE */
        {
            /* Com.xnew, Com.gnew, Com.g, nonmonotone line search memory */
            i += 3*ncol + pasaparm->M ;
        }
        if ( use_napheap == TRUE )
        {
            /* 3*ncol for xwork array and ncol for nap_a */
            i += 4*ncol ;

            /* save a copy of a if bounds are present and an
               active set method will be used */
            if ( (Bounds == TRUE) && (GradProjOnly == FALSE) )
            {
                i += ncol ; /* nap_acopy */
            }

            /* when using napheap with fixed variables, need to save
               a copy of a before removing the fixed variables */
            if ( fixed == TRUE )
            {
                i += ucol ; /* nap_auser */
            }
        }

        /* if lambda == NULL, then allocate space for lambda array */
        if ( lambda == NULL )
        {
            i += nrow ;
        }

        /* if fixed variables present or use_pproj is used, then save a copy
             of lo and hi (due to compression for fixed variables, the
             computation of Prob->lo or hi, and the scaling for an LP)*/
        if ( (fixed  == TRUE) || LP )
        {
            if ( loExists == TRUE )
            {
                i += ncol ;
            }
            if ( hiExists == TRUE )
            {
                i += ncol ;
            }

            /* Copies of bl and bu (of size nrow) are needed to account
               for modifications of the right side due to the fixed
               variables. Later, these variables are used to stored copies
               in Com.bl and Com.bu of the reordered right sides from
               Prob->bl and Prob->bu of pproj. Note that Prob->bl and
               Prob->bu have size ni+1 <= nrow +1.  Also, need a copy
               of Prob->b from pproj */
            if ( use_pproj ) /* the inequalities bl <= Ax <= bu exist */
            {
                i += 2*(nrow+1) + nrow ;
            }
            /* when there are fixed variables and pproj is used, we need to
               compress Ax, for an LP we scale Ax even if no fixed variables */
            if ( ((fixed == TRUE) && use_pproj) || LP )
            {
                i += Asize ;
            }
        }
        else if ( use_pproj )
        {
            /* allocate space for Com.bl, Com.bu, and Com.b since these
               are used in the computation of Prob->bl, Prob->bu, and Prob->b */
            i += nrow + 2*(ni+1) ;
        }

        /* Allocate the space needed for CG. If GradProjOnly is TRUE or
           the cost is linear, then CG is not performed and this space is
           not needed. */
        if ( (GradProjOnly == FALSE) && (LP == FALSE) )
        {
            /* in CG, memory is the minimum of ncol and LBFGSmemory */
            mem = PASAMIN (cgparm->LBFGSmemory, ncol) ;

            /* if penalty term is used, then allocate space for it */
            if ( use_penalty == TRUE )
            {
                /* cgcom-> lambda_pen, gpen, AAd, in CG */
                i += 2*ncol + nrow ;
            }

            if ( Aexists == TRUE )
            {
                /* need 3*ncol for projected gradients at x and x + alpha*d,
                   and for the search direction before the final projection */
                i += 3*ncol ;
            }

            /* if L-BFGS is potentially used, then allocate space for it */
            if ( cgparm->LBFGS >= 1 )
            {
                i += 2*mem*(ncol+1) ;
            }

            /* if limited memory CG used, need additional space */
            if ( cgparm->LBFGS == 3 )
            {
                i += (mem+2)*ncol + (3*mem+9)*mem + 5 ;
            }
        }

        if ( Aexists ) /* A exists */
        {
            /* active set method */
            if ( (GradProjOnly == FALSE) && (LP == FALSE) )
            {
                /* Com->copylo, Com->copyhi used in gradproj initialization */
                if ( loExists == TRUE )
                {
                    i += ncol ;
                }
                if ( hiExists == TRUE )
                {
                    i += ncol ;
                }
                if ( ni > 0 )
                {
                    /* Com->Axk, Adk are used in CG */
                    i += 2*(ni+1) ;
                }
                if ( use_pproj )
                {
                    /* variables used in gradproj initialization */
                    i += nrow + 2*(ni+1) ; /* Copy->b, bl, bu */
                    i += 2*Asize ;         /* Copy->ATx, Ax */
                }
            }
        }
        /* bound constraints only, if active set method, allocate copy arrays */
        else if ( GradProjOnly == FALSE )
        {
            /* Com->copylo, Com->copyhi */
            if ( loExists )
            {
                i += ncol ;
            }
            if ( hiExists )
            {
                i += ncol ;
            }
        }

        if ( QP == TRUE )
        {
            /* we need space to store the total gradient in user coordinate,
               as well as space for the Hessian times search direction */
            i += 2*ncol ; /* Com->Qd, gtot */
        }
        work = (PASAFLOAT *) pasa_malloc (&status, i, sF) ;
        Com.work = work ; /* work in Com structure for later free */
    }
    else
    {
        work = xWork ;
        Com.work = NULL ;
    }

    /* terminate if there is not enough memory */
    if ( status == PASA_OUT_OF_MEMORY )
    {
        pasa_wrapup (status, TRUE, &Com) ;
        return (status) ;
    }

    /* Allocate space for integer arrays. */

    if ( iWork == NULL )
    {
        i = 0 ;

        if ( LP )
        {
            /* row si and column sj numbers for column singletons */
            i += 2*nsingni ;

            /* singperm (permutation from sorted singc to sc) */
            i += nsingni ;

            /* row_sing [[row_sing[i], row_sing[i+1]) =singc indices for row i*/
            i += nrow + 1 ;
        }

        if ( use_napheap == TRUE )
        {
            /* iwork array of napheap */
            i += 4*ncol + 2 ;
        }
        /* If fixed variables are present, then save space for Ai and Ap
           if they exist. If not fixed variables, utilize userAi and userAp */
        if ( use_pproj && (fixed == TRUE) )
        {
            /* space for Ap and Ai */
            i += ncol + 1 + Asize ;
        }

        /* in the following cases, we need the permutation colperm
           between the internal ordering of variables and the user ordering */
        if ( (fixed == TRUE) || (use_pproj == TRUE) )
        {
            i += ncol ;
        }

        if ( (GradProjOnly == FALSE) && (LP == FALSE) ) /* active set method */
        {
            /* Com->ifree, bound_cols */
            if ( Bounds == TRUE )
            {
                i += 2*ncol ;
            }
            /* bound_rows */
            i += ni ;

            if ( use_pproj == TRUE )
            {
                /* Copy->ineq_row, Copy->sol_to_blk, Copy->ATp,
                   Com->row_to_ineq, Com->invperm, Copy->Ap,
                   Copy->Ai, Copy->ATi */
                i += 2*(ni + 2) + ucol + ncol + 2*(nrow + 1 + Asize) ;
            }
        }

        iwork = (PASAINT *) pasa_malloc (&status, i, sI) ;
        Com.iwork = iwork ; /* iwork in Com structure for later free */
    }
    else
    {
        iwork = iWork ;
        Com.iwork = NULL ;
    }

    /* terminate if there is not enough memory */
    if ( status == PASA_OUT_OF_MEMORY )
    {
        pasa_wrapup (status, TRUE, &Com) ;
        return (status) ;
    }

    /* -------- REST OF COMMON BLOCK ----------*/
    Com.value   = pasadata->value ;   /* objective function */
    Com.grad    = pasadata->grad ;    /* gradient of objective function */
    Com.valgrad = pasadata->valgrad ; /* combined function and gradient*/
    Com.hprod   = pasadata->hprod ;   /* if objective quadratic, routine to
                                         evaluate Hessian-vector product */
    Com.hess    = pasadata->hess ;    /* evaluate Hessian at a given point */
    Com.grad_tol = grad_tol ;         /* bound on KKT error */
    Com.GPtol = pasaparm->GPtol ;     /* error tolerance for projected grad. */
    Com.nrow = nrow ;    /* number of rows in A */
    Com.ncol = ncol ;    /* number of component in x (cols in A if A exists) */
    Com.userc = userc ;  /* user's linear term in the objective */
    /* initialize factor for switching between GPA and PPROJ*/
    Com.switchfactor = pasaparm->switchfactor ;
    /* line search could change from Armijo to approximate Armijo, so store
       the parameter value in Com where it can be changed */
    Com.approxstep = pasaparm->approxstep ;
    Com.nf = ncol ;
    Com.nr = 0 ;  /* no bound rows by default */
    Com.nc = 0 ;  /* no bound cols by default */
    Com.location = PASA_BASE_CODE ; /* other locations: PASA_GRAD_PROJ
                                                        PASA_ACTIVE_BP
                                                        PASA_CG_DESCENT */
    Com.unitstep = FALSE ;  /* used in activeGP */
    Com.factor_ok = FALSE ; /* used in activeGP */
    Com.first = TRUE ;      /* used in project, denotes first iteration */

    /* initialize error and function value to infinity */
    Com.E = PASAINF ;
    Com.fnew = PASAINF ;

    /* ======== FLOAT WORKSPACE ===========================================*/
    Com.d     = work ;  work += ncol ;
    Com.userx = work ;  work += ucol ; /* note: ucol not ncol */
    Com.userg = work ;  work += ucol ; /* note: ucol not ncol */

    /* set up the structure to store the Hessian */
    Hess.x = Com.userx ;
    Hess.Hp = NULL ;
    Hess.Hi = NULL ;
    Hess.Hx = NULL ;
    Hess.ncol = ucol ;
    /* for linear cost problems, need extra copy of the linear term */
    if ( LP )
    {
        /* store a copy of c in Com.c since it will be scaled */
        Com.c = work ;  work += ncol ;

        Com.ColScale = work ;  work += ncol ;
        Com.RowScale = work ;  work += nrow ;
        Com.offset   = work ;  work += ncol ;

        /* if column singleton exist, allocate for slo, shi, sx, sc */
        if ( singExists )
        {
            sx     = Com.sx     = work ;  work += nsingni ;
            sc     = Com.sc     = work ;  work += nsingni ;
            slo    = Com.slo    = work ;  work += nsingni ;
            shi    = Com.shi    = work ;  work += nsingni ;
            singlo = Com.singlo = work ;  work += nsingni ;
            singhi = Com.singhi = work ;  work += nsingni ;
            singc  = Com.singc  = work ;  work += nsingni ;
        }
    }
    else /* the problem is not an LP */
    {
        /* xnew, Com->g, gnew, nonmonotone line search memory */
        Com.xnew  = work ;  work += ncol ;
        Com.gnew  = work ;  work += ncol ;
        Com.g     = work ;  work += ncol ;
        Com.fmem  = work ;  work += pasaparm->M ;
    }

    if ( use_napheap == TRUE )
    {
        Com.napxwork = work ;  work += 3*ncol ;
        Com.nap_a    = work ;  work +=   ncol ;
        /* save a copy of a if bounds are present and an
           active set method will be used */
        if ( (Bounds == TRUE) && (GradProjOnly == FALSE) )
        {
            Com.nap_acopy  = work ;  work += ncol ;
        }
        else
        {
            Com.nap_acopy  = NULL ;
        }
        if ( fixed == TRUE )
        {
            Com.nap_auser  = work ;  work += ucol ;
        }
        else
        {
            Com.nap_auser  = Com.nap_acopy ;
        }
    }
    else
    {
        Com.napxwork = NULL ;
        Com.nap_a    = NULL ;
        Com.nap_acopy = NULL ;
    }

    if ( lambda == NULL )
    {
        lambda = work ;  work += nrow ;
    }
    Com.lambda = lambda ;

    /* initialize lo, hi, Bu, Bl, Ap, Ai, Ax to the user values */

    lo = (loExists == TRUE) ? userlo : NULL ;
    hi = (hiExists == TRUE) ? userhi : NULL ;
    Bu = userBu ;
    Bl = userBl ;
    Ax = userAx ;
    Ap = userAp ;
    Ai = userAi ;

    /* if fixed variables present or A exists, then save a copy of lo and hi */
    if ( (fixed == TRUE) || LP )
    {
        if ( loExists == TRUE )
        {
            lo = work ;  work += ncol ;
        }
        else
        {
            lo = NULL ;
        }
        if ( hiExists == TRUE )
        {
            hi = work ;  work += ncol ;
        }
        else
        {
            hi = NULL ;
        }

        /* Copies of bl and bu (of size nrow) are needed to account
           for modifications of the right side due to the fixed
           variables. Later, these variables are used to stored copies
           of the reordered right sides from Prob->bl and Prob->bu of
           pproj. Note that Prob->bl and Prob->bu have size ni+1 <= nrow +1.
           Also, need a copy of Prob->b from pproj */
        if ( use_pproj )
        {
            /* wasteful since only need ni+1 later for bl/bu, which is
               typically less than nrow */
            b  = work ;  work += nrow ;
            Bl = work ;  work += (nrow+1) ;
            Bu = work ;  work += (nrow+1) ;
        }
        /* when there are fixed variables and pproj is used, we need to
           compress Ax, for an LP we scale Ax even if no fixed variables */
        if ( ((fixed == TRUE) && use_pproj) || LP )
        {
            Ax = work ;  work += Asize ;
        }
    }
    else if ( use_pproj )
    {
        /* allocate space for Com.bl, Com.bu, and Com.b since these
           are used in the computation of Prob->bl, Prob->bu, and Prob->b */
        b  = work ;  work += nrow ;
        Bl = work ;  work += ni + 1 ;
        Bu = work ;  work += ni + 1 ;
    }

    /* if an active set algorithm used and the objective is not linear,
       then set up cg memory */
    if ( (GradProjOnly == FALSE) && (LP == FALSE) )
    {
        /* This is where cg memory starts. */
        Com.cgwork = cgwork = work ;

        /* if use_penalty is TRUE, allocate space for the multiplier used
           in penalty function */
        if ( use_penalty == TRUE )
        {
            Com.lambda_pen = work ;  work += nrow ;
            Com.gpen       = work ;  work += ncol ;
            Com.cgwork = cgwork = work ;
            /* jump over space for AAd */
            work += ncol ;
        }

        if ( Aexists == TRUE )
        {
            /* jump over space for projected gradients at x and x + alpha*d,
               and for the search direction before the final projection */
            work += 3*ncol ;
        }

        /* if L-BFGS is potentially used, then jump over its space */
        if ( cgparm->LBFGS >= 1 )
        {
            work += 2*mem*(ncol+1) ;
        }

        /* if limited memory CG used, then jump over its space */
        if ( cgparm->LBFGS == 3 )
        {
            work += (mem+2)*ncol + (3*mem+9)*mem + 5 ;
        }
    }

    /* Com.lo and hi are set to the value computed above; however, if lo
       and hi did not get new value above, then Com.lo = userlo and
       Com.hi = userhi */
    Com.lo = lo ;
    Com.hi = hi ;

    /* copylo and and copy hi are initialized to the value given above */
    Com.copylo = lo ;
    Com.copyhi = hi ;

    Com.Ax = Ax ; /* nonzero numerical entries in A */
    Com.b = b ;
    Com.bl = Bl ; /* left side of linear inequalities */
    Com.bu = Bu ; /* right side of linear inequalities */

    /* if A exists, then allocate space for copy arrays and arrays used in CG */
    if ( Aexists == TRUE )
    {
        if ( (GradProjOnly == FALSE) && (LP == FALSE) )
        {
            if ( loExists == TRUE )
            {
                Com.copylo = work ;  work += ncol ;
            }
            if ( hiExists == TRUE )
            {
                Com.copyhi = work ;  work += ncol ;
            }
            if ( ni > 0 )
            {
                Com.Axk = work ;  work += ni + 1 ;
                Com.Adk = work ;  work += ni + 1 ;
            }
            if ( use_pproj == TRUE )
            {
                Copy.b   = work ;  work += nrow ;
                Copy.bl  = work ;  work += ni + 1 ;
                Copy.bu  = work ;  work += ni + 1 ;
                Copy.ATx = work ;  work += Asize ;
                Copy.Ax  = work ;  work += Asize ;
            }
        }
    }
    /* else bound constraints only, if active set method, allocate copy arrays*/
    else if ( GradProjOnly == FALSE )
    {
        if ( loExists )
        {
            Com.copylo = work ;  work += ncol ;
        }
        if ( hiExists )
        {
            Com.copyhi = work ;  work += ncol ;
        }
    }

    if ( QP == TRUE )
    {
        /* Com->Qd (Hessian times direction) */
        Com.Qd   = work ;  work += ncol ;
        /* Com->gtot total gradient in user's coordinates with fixed variables
                removed.  gtot differs from userg in that userg is gradient
                at the current x, which may not be accepted, while
                gtot is the gradient at an accepted iterate x. We only
                update gtot when the iterate is accepted.  */
        Com.gtot = work ;  work += ncol ;
    }

    /* ======== INTEGER WORKSPACE =========================================*/

    if ( singExists )
    {
        si       = Com.si       = iwork ;  iwork += nsingni ;
        sj       = Com.sj       = iwork ;  iwork += nsingni ;
        singperm = Com.singperm = iwork ;  iwork += nsingni ;
        row_sing = Com.row_sing = iwork ;  iwork += nrow + 1 ;
    }

    if ( use_napheap )
    {
        Com.napiwork = iwork ;  iwork += 4*ncol + 2 ;
    }
    else
    {
        Com.napiwork = NULL ;
    }

    if ( use_pproj && (fixed == TRUE) )
    {
        Ap = iwork ;  iwork += ncol + 1 ;
        Ai = iwork ;  iwork += Asize ;
    }
    if ( ((fixed == TRUE) || (use_pproj == TRUE)) && !LP )
    {
        Com.colperm = iwork ;  iwork += ncol ;
    }
    else
    {
        Com.colperm = NULL ;
    }

    Com.ifree = NULL ;
    if ( (GradProjOnly == FALSE) && (LP == FALSE) ) /* active set method */
    {
        /* ifree, bound_cols, bound_rows */
        if ( Bounds == TRUE )
        {
            Com.ifree      = iwork ;  iwork += ncol ;
            Com.bound_cols = iwork ;  iwork += ncol ;
        }
        Com.bound_rows = iwork ;  iwork += ni ; /* ni could vanish */
        if ( use_pproj )
        {
            /* Copy->ineq_row, Copy->ATp, Com->row_to_ineq, Com->invperm,
                   Copy->Ap, Copy->Ai, Copy->ATi */
            Copy.ineq_row   = iwork ;  iwork += ni + 2 ;

            /* use int for consistency with pproj (waste when iwork is long) */
            Copy.sol_to_blk = (int *) iwork ;  iwork += ni + 2 ;

            Copy.ATp        = iwork ;  iwork += nrow + 1 ;
            Com.row_to_ineq = iwork ;  iwork += nrow ;
            Com.invperm     = iwork ;  iwork += ucol ;
            Copy.Ap         = iwork ;  iwork += ncol + 1 ;
            Copy.ATi        = iwork ;  iwork += Asize ;
            Copy.Ai         = iwork ;  iwork += Asize ;
        }
    }

    /* Ap and Ai are either userAp and userAi if no fixed variables,
       or they were allocated space above if there are fixed variables
       and the matrix is compressed */
    Com.Ap = Ap ; /* column pointers for A */
    Com.Ai = Ai ; /* row indices for A */

    /* =========== ALLOCATION OF MEMORY IS COMPLETE =========== */

    if ( use_pproj )
    {
        pprojparm->grad_tol = pasaparm->GPtol ;
        ppdata->lambda      = lambda ;
        ppdata->Parm        = pprojparm ;
        ppdata->Stat        = pprojstat ;
        ppdata->nrow        = nrow ;
        ppdata->ncol        = ncol ;
        ppdata->ni          = ni ;
        ppdata->nsing       = (singExists) ? nsingni : 0 ;
        ppdata->Ap          = Ap ;
        ppdata->Ai          = Ai ;
        ppdata->Ax          = Ax ;
        ppdata->lo          = lo ;
        ppdata->hi          = hi ;
        if ( (fixed == TRUE) || LP )
        {
            ppdata->bl       = Bl ;
            ppdata->bu       = Bu ;
        }
        else
        {
            ppdata->bl       = userBl ;
            ppdata->bu       = userBu ;
        }
        if ( singExists )
        {
            ppdata->row_sing = row_sing ;
            ppdata->singlo   = singlo ;
            ppdata->singhi   = singhi ;
            ppdata->singc    = singc ;
        }
        else
        {
            ppdata->row_sing = NULL ;
            ppdata->singlo   = NULL ;
            ppdata->singhi   = NULL ;
            ppdata->singc    = NULL ;
         }
        /* still to assign: x, y (these input are chosen in different ways
                                  throughout the code) */
    }

    ns = 0 ; /* singleton number */
    /* remove any fixed variables from the problem */

    if ( fixed == TRUE )
    {
        /* store the unfixed parts of userlo, userhi, and x in lo, hi, and x */
        for (j = 0; j < ncol; j++)
        {
            i = ifree [j] ;
            x [j] = x [i] ; /* bound variables stored in userx */
            if ( loExists ) lo [j] = userlo [i] ;
            if ( hiExists ) hi [j] = userhi [i] ;
            if ( LP )  Com.c [j] = userc [i] ;
        }

        /* if A exists, place the fixed variables in userx, and adjust Bl, Bu,
           and A to account for the fixed variables */
        if ( use_pproj )
        {
            PASAINT ColNew, ColOld, LocNew, LocOld ;
            pasa_copyx (Bl, userBl, nrow) ;
            pasa_copyx (Bu, userBu, nrow) ;
            Ap [0] = 0 ;
            ColOld  = 0 ; /* column in old A */
            ColNew  = 0 ; /* column in new A */
            LocNew  = 0 ; /* location in new Ai or Ax */
            LocOld  = 0 ; /* location in old Ai or Ax */

            for (i = ucol-1; i >= ncol; i--)
            {
                j = ifree [i] ; /* index in user's coordinates */
                if ( LP )
                {
                    if ( j >= 0 ) /* either column singleton or zero column */
                    {
                        /* Although column singletons were put in the fixed
                           variable list, they are not fixed.  Similar to
                           fixed variables, the columns with singletons will
                           be removed from the problem (not discarded but
                           placed in a separate structure) */
                        PASAINT Apj = userAp [j] ;
                        k = userAp [j+1] - Apj ;
                        if ( k == 1 )
                        {
                            if ( user_loExists ) slo [ns] = userlo [j] ;
                            else                 slo [ns] = -PASAINF ;
                            if ( user_hiExists ) shi [ns] = userhi [j] ;
                            else                 shi [ns] = PASAINF ;
                            Com.sc [ns] = userc [j] ;    /* cost */
                            const PASAINT row = userAi [Apj] ;
                            si [ns] = row ;          /* row of singleton */
                            singcount [row]++ ;      /* count # in row */
                            sj [ns] = j ;            /* col of singleton */
                            sx [ns] = userAx [Apj] ; /* numerical entry */
                            ns++ ;
                            t = PASAZERO ; /* nothing to subtract from b */
                        }
                        else /* k = 0, the column is zero, determine value */
                        {
                            if ( userc [j] > PASAZERO )
                            {
                                if ( loExists ) t = userlo [j] ;
                                else            t = -PASAINF ;
                            }
                            else if ( userc [j] < PASAZERO )
                            {
                                if ( hiExists ) t = userhi [j] ;
                                else            t = PASAINF ;
                            }
                            else /* c [j] = 0, take closest number to 0 */
                            {
                                if ( userlo [j] > PASAZERO )
                                {
                                    t = userlo [j] ;
                                }
                                else
                                {
                                    if ( userhi [j] < PASAZERO )
                                    {
                                        t = userhi [j] ;
                                    }
                                    else /* 0 \in [userlo, userh] */
                                    {
                                        t = PASAZERO ;
                                    }
                                }
                            }
                        }
                    }
                    else /* variable is bound but not singleton or zero column*/
                    if ( j < 0 )
                    {
                        j = -(j+1) ;
                        t = userlo [j] ; /* = userhi [j] */
                    }
                }
                else /* variable is bound */
                {
                    t = userlo [j] ; /* = userhi [j] */
                }
                Com.userx [j] = t ;
                /* copy column m up to column j-1 from userA into A */
                p = LocOld ;
                LocOld = userAp [j] ;
                pasa_copyx (Ax+LocNew, userAx+p, LocOld-p) ;
                pasa_copyi (Ai+LocNew, userAi+p, LocOld-p) ;
                for (l = ColOld; l < j; l++)
                {
                    LocOld = userAp [l+1] ;
                    LocNew += LocOld - p ;
                    ColNew++ ;
                    Ap [ColNew] = LocNew ;
                    p = LocOld ;
                }
                ColOld = j + 1 ;

                /* subtract column j times fixed value t from right and
                   left side of inequality if t nonzero */
                LocOld = userAp [ColOld] ;
                if ( t )
                {
                    for (; p < LocOld; p++)
                    {
                        l = userAi [p] ;
                        s = t*userAx [p] ;
                        Bl [l] -= s ;
                        Bu [l] -= s ;
                    }
                }
            }

            /* copy from end of last fixed column to the end of A */
            p = LocOld ;
            LocOld = userAp [ucol] ;
            pasa_copyx (Ax+LocNew, userAx+p, LocOld-p) ;
            pasa_copyi (Ai+LocNew, userAi+p, LocOld-p) ;
            for (l = ColOld; l < ucol; l++)
            {
                LocOld = userAp [l+1] ;
                LocNew += LocOld - p ;
                ColNew++ ;
                Ap [ColNew] = LocNew ;
                p = LocOld ;
            }
        }
        else /* no Ap is present */
        {
            if ( use_napheap ) /* napheap "a" is present */
            {
                for (i = ucol-1; i >= ncol; i--)
                {
                    j = ifree [i] ;
                    Com.userx [j] = userlo [j];
                    t = userlo [j]*usera [j] ;
                    Bl [0] -= t ;
                    Bu [0] -= t ;
                }
            }
            else /* no matrix: Ap and "a" do not exist */
            {
                for (i = ucol-1; i >= ncol; i--)
                {
                    j = ifree [i] ;
                    Com.userx [j] = userlo [j] ;
                }
            }
        }
    }
    else if ( LP ) /* no fixed variables present and an LP */
    {
        /* save copy of c since it will be scaled (ucol = ncol) */
        pasa_copyx (Com.c, userc, ucol) ;

        /* save copy of the equation bounds since they will be scaled */
        pasa_copyx (Bl, userBl, nrow) ;
        pasa_copyx (Bu, userBu, nrow) ;
        pasa_copyx (Ax, userAx, Asize) ; /* A is initially scaled below */
        if ( loExists ) pasa_copyx (lo, userlo, ncol) ;
        if ( hiExists ) pasa_copyx (hi, userhi, ncol) ;
    }

    /* save a if napheap is used */
    if ( use_napheap == TRUE )
    {
        /* the following pointers in napdata do not change in the algorithm */
        napdata->a     = Com.nap_a ;
        napdata->lo    = lo ;
        napdata->hi    = hi ;
        napdata->xWork = Com.napxwork ;
        napdata->iWork = Com.napiwork ;
        /* By default, the multiplier is in Com.lambda. If the multiplier
           is not supposed to be used for the starting guess, then set it
           to -infinity. Before napheap is called, it sets napdata->lambda =
           -Com.lambda, and napheap interprets an infinite value as something
           to be ignored. */
        if ( pasaparm->use_lambda == FALSE )
        {
            Com.lambda [0] = -PASAINF ;
        }
 
        pasa_initx (Com.nap_a, PASAZERO, ncol) ;
        t = PASAZERO ;
        /* extract the constraint from either A matrix or "a" vector */
        if ( usera == NULL ) /* use A matrix */
        {
            p = 0 ;
            for (j = 0; j < ncol; j++)
            {
                q = Ap [j+1] ;
                if ( p < q )
                {
                    PASAFLOAT const aj = Ax [p] ;
                    Com.nap_a [j] = aj ;
                    t += aj*aj ;
                    p = q ;
                }
            }
        }
        else /* use a vector */
        {
            for (j = 0; j < ncol; j++)
            {
                PASAFLOAT const aj = usera [ifree [j]] ;
                Com.nap_a [j] = aj ;
                t += aj*aj ;
            }
        }
        Com.nap_a2 = Com.nap_a2save = Com.nap_a2full = t ;
        if ( Com.nap_acopy != NULL )
        {
            pasa_copyx (Com.nap_acopy, Com.nap_a, ncol) ;
        }
        Com.nap_bl = Com.nap_blcopy = *Bl ;
        Com.nap_bu = Com.nap_bucopy = *Bu ;

        /* if there are fixed variables, also save a copy of a before the
           removal of the fixed variables */
        if ( fixed == TRUE )
        {
            pasa_initx (Com.nap_auser, PASAZERO, ucol) ;
            p = 0 ;
            for (j = 0; j < ucol; j++)
            {
                q = userAp [j+1] ;
                if ( p < q )
                {
                    Com.nap_auser [j] = userAx [p] ;
                    p = q ;
                }
            }
        }
    }

    /* If the problem is an LP, then do the following:

       1. First multiply each row by a factor to make the largest element
          in the row around one (exclude all column singletons).
       2. Multiply each column by a factor to make the Euclidean norm one.
       3. If the bound [lo [j], hi [j]] does not contain 0, then make
          make a translation in x [j] so that 0 lies in the transformed
          bound interval.
       4. Multiply the right side bl and bu by a scalar so that the
          right side vector has absolute largest element 1.
       5. Multiply the cost vector c by a scalar so that the absolute
          largest element is 1. */
    if ( LP )
    {
        if ( singExists )
        {
            /* augment the singleton data with all strict inequalities */
            for (k = 0; k < ni; k++)
            {
                i = ineqList [k] ;
                slo [ns] = Bl [i] ;
                shi [ns] = Bu [i] ;
                Bl [i]  = PASAZERO ;
                sc [ns] = PASAZERO ;   /* cost */
                si [ns] = i ;          /* row of singleton */
                sj [ns] = -1 ;         /* no column */
                sx [ns] = -PASAONE ;   /* numerical entry */
                ns++ ;
            }
            ppdata->bu = Bl ; /* = ppdata->bl */
            /* strict inequalities converted to equalities when column
               singletons exist */
            ppdata->ni = 0 ;
        }

        PASAFLOAT *RowScale = Com.RowScale ;
        pasa_initx (RowScale, PASAZERO, nrow) ;

        /* Find the largest element in each row of the matrix.
           Note that any column singleton were removed in the fixed
           variable code above. */
        for (p = 0; p < Asize; p++)
        {
            i = Ai [p] ;
            PASAFLOAT const v = fabs (Ax [p]) ;
            if ( v > RowScale [i] ) RowScale [i] = v ;
        }
        /* only scale by powers of 2 */
        for (i = 0; i < nrow; i++)
        {
            if ( (t = RowScale [i]) != PASAZERO )
            {
                /* square root decreases change in row */
                t = frexp (sqrt (t), &exponent) ;
                t = ldexp (1, -exponent) ;
                Bl [i] *= t ;
                if ( !singExists ) Bu [i] *= t ;
            }
            else
            {
                t = PASAONE ;
            }
            RowScale [i] = t ;
        }

        /* scale matrix rows and adjust the bound such that 0 \in [lo, hi] */
        PASAFLOAT *offset = Com.offset ;
        c = Com.c ;
        p = 0 ;
        PASAFLOAT normc = PASAZERO ;
        for (j = 0; j < ncol; j++)
        {
            if      ( loExists && (lo [j] > PASAZERO) ) t = lo [j] ;
            else if ( hiExists && (hi [j] < PASAZERO) ) t = hi [j] ;
            else                                        t = PASAZERO ;
            offset [j] = t ;
            q = Ap [j+1] ;
            p0 = p ;
            s = PASAZERO ;
            if ( singExists )
            {
                for (; p < q; p++)
                {
                    i = Ai [p] ;
                    r = RowScale [i]*Ax [p] ;
                    Ax [p] = r ;
                    Bl [i] -= r*t ;
                    s += r*r ; /* sum of squares of elements in the column */
                }
            }
            else
            {
                for (; p < q; p++)
                {
                    i = Ai [p] ;
                    r = RowScale [i]*Ax [p] ;
                    Ax [p] = r ;
                    Bl [i] -= r*t ;
                    Bu [i] -= r*t ;
                    s += r*r ; /* sum of squares of elements in the column */
                }
            }
            s = frexp (sqrt (s), &exponent) ;
            s = ldexp (1, -exponent) ;
            for (p = p0; p < q; p++)
            {
                Ax [p] *= s ;
            }
            u = PASAONE/s ;
            if ( hiExists ) hi [j] = u*(hi [j] - t) ;
            if ( loExists ) lo [j] = u*(lo [j] - t) ;
            Com.ColScale [j] = s ;
            c [j] *= s ;
            if ( fabs (c [j]) > normc ) normc = fabs (c [j]) ;
            x [j] *= u ;
        }

        /* if there are column singletons, then create the associated
           data structures */
        PASAFLOAT normb ;
        normb = PASAZERO ;
        if ( singExists )
        {
            /* make singcount point to start of storage for singletons in row */
            k = 0 ;
            nr = 0 ;
            rowList = ineqList ; /* do not need ineqList, use it for rowList */
            for (i = 0; i < nrow; i++)
            {
                l = k ;
                if ( singcount [i] )
                {
                    k += singcount [i] ;
                    rowList [nr] = i ;
                    nr++ ;
                }
                singcount [i] = l ;
            }

            /* allocate space for local variables */
            PASAINT *itemp = (PASAINT *) pasa_malloc (&status, 3*nsingni, sI) ;
            if ( status == PASA_OUT_OF_MEMORY )
            {
                pasa_wrapup (status, TRUE, &Com) ;
                return (status) ;
            }
                     iwork = itemp ;  itemp += nsingni ;
            PASAINT *tempk = itemp ;  itemp += nsingni ;
            PASAINT *order = itemp ;  itemp += nsingni ;

            PASAFLOAT *tempc = (PASAFLOAT *) pasa_malloc (&status, nsingni,sF);
            if ( status == PASA_OUT_OF_MEMORY )
            {
                pasa_free (iwork) ; /* also frees tempk and order */
                pasa_wrapup (status, TRUE, &Com) ;
                return (status) ;
            }
            for (k = 0; k < ns; k++)
            {
                const PASAINT row = Com.si [k] ;
                l = singcount [row]++ ;
                const PASAFLOAT ax = RowScale [row]*sx [k] ;
                sx [k] = ax ;
                tempc [l] = sc [k]/ax ; /* tempc contains c vector by rows */
                tempk [l] = k ;         /* index of tempc [l] in sc array */
            }
            /* Since singcount [i] is now the end of each row, we set
               singcount = singcount-1 to switch back to start of each row
               Arrange the c vector so that the entries are increasing for
               each row of the matrix. Output the corresponding arrays of
               singleton lower and upper bound, along with the permutation
               that maps back to the original data stored in Com. */
            l = -1 ; /* start of the elements in c is l + 1 */
            PASAINT lastrow = -1 ;
            singcount = singcount-1 ;
            PASAINT rownum ;
            for (rownum = 0; rownum < nr; rownum++)
            {
                const PASAINT row = rowList [rownum] ;
                for (i = lastrow+1; i <= row; i++)
                {
                    row_sing [i] = l + 1 ;
                }
                lastrow = row ;
                PASAINT     start = singcount [row] ;
                PASAINT   *kstart = tempk+start ; /* point from tempc to sc */
                PASAFLOAT *cstart = tempc+start ; /* start of c values for row*/
                /* num_in_row = number of singletons in row */
                const PASAINT num_in_row = singcount [row+1] - start ;

                pproj_xminsort (order, cstart, iwork, num_in_row) ;
                PASAFLOAT last_c_value = PASAINF ;
                for (i = 0; i < num_in_row; i++)
                {
                    /* index associated with the i-th largest singleton */
                    PASAINT const orderi = order [i] ;
                    k = kstart [orderi] ;
                    PASAFLOAT const newcost = cstart [orderi] ;

                    /* perform shift to achieve 0 \in [lok, hik] */
                    const PASAFLOAT lok = slo [k] ;
                    const PASAFLOAT hik = shi [k] ;

                    /* t = shift */
                    if      ( lok > PASAZERO ) t = lok ;
                    else if ( hik < PASAZERO ) t = hik ;
                    else                       t = PASAZERO ;

                    /* ax = matrix element * RowScale for singleton */
                    const PASAFLOAT ax = sx [k] ;

                    /* adjust Bl due to shift in x */
                    Bl [row] -= t*ax ;

                    if ( newcost == last_c_value ) /* c value repeats */
                    {
                        /* flag the current column if the c value repeats
                           since all the repeated values of c are compressed
                           into a single entry, as well as lo and hi */
                        if ( !repeat )
                        {
                            singperm [l] = -(singperm [l]+1) ;
                        }
                        repeat = TRUE ;
                        /* point last si to new k value */
                        Com.si [kold] = k ;
                        kold = k ;
                        Com.si [k] = EMPTY ;
                        /* The bounds are added to the running totals.
                           Note that in pproj, xj is replaced by -xj.  */
                        if ( ax > PASAZERO ) /* sign change flips bounds */
                        {
                            singlo [l] += (t - hik)*ax ;
                            singhi [l] += (t - lok)*ax ;
                        }
                        else
                        {
                            singlo [l] += (t - lok)*ax ;
                            singhi [l] += (t - hik)*ax ;
                        }
                    }
                    else
                    {
                        repeat = FALSE ; /* new distinct c value */
                        last_c_value = newcost ;
                        l++ ; /* store a new c value and bounds */
                        singperm [l] = k ;
                        kold = k ;
                        /* Note that in pproj, xj is replaced by -xj.  */
                        if ( ax > PASAZERO ) /* scaling flips bounds */
                        {
                            singlo [l] = (t - hik)*ax ;
                            singhi [l] = (t - lok)*ax ;
                        }
                        else
                        {
                            singlo [l] = (t - lok)*ax ;
                            singhi [l] = (t - hik)*ax ;
                        }
                        singc [l] = newcost ;
                        if ( fabs (newcost) > normc ) normc = fabs (newcost) ;
                    }
                }
            }
            /* the last one column singleton is in location l so the
               number of column singletons is l + 1 */
            l++ ;
            for (i = lastrow+1; i <= nrow; i++) row_sing [i] = l ;
            ppdata->nsing = l ;
            if ( PrintLevel )
            {
                printf ("%ld column singletons and strict inequalities "
                        "after compression\n\n", (LONG) l) ;
            }
            pasa_free (iwork) ; /* also frees tempk and order */
            pasa_free (tempc) ;
            pasa_free (ineqList) ; /* also free singcount */

            for (i = 0; i < nrow; i++)
            {
                const PASAFLOAT Bli = Bl [i] ;
                if ( fabs (Bli) > normb )
                {
                    normb = fabs (Bli) ;
                    if ( Bli >= PASAINF )
                    {
                        pasastat->lobad = PASAZERO ;
                        pasastat->hibad = PASAZERO ;
                        pasastat->ibad  = i ;
                        status = PASA_INVALID_MATRIX_ELEMENT ;
                        pasa_wrapup (status, TRUE, &Com) ;
                        return (status) ;
                    }
                }
            }
        }
        else /* no column singletons, compute normb = max min right side */
        {
            for (i = 0; i < nrow; i++)
            {
                PASAFLOAT const fabsBl = fabs (Bl [i]) ;
                PASAFLOAT const fabsBu = fabs (Bu [i]) ;
                if ( fabsBl <= fabsBu )
                {
                    if ( fabsBl > normb ) normb = fabsBl ;
                }
                else
                {
                    if ( fabsBu > normb ) normb = fabsBu ;
                }
            }
        }
        if ( normb == PASAZERO ) normb = PASAONE ;
        t = frexp (normb, &exponent) ;
        Com.scale_x = ldexp (1, -exponent) ;

        if ( normc == PASAZERO ) normc = PASAONE ;
        t = frexp (normc, &exponent) ;
        Com.scale_l = ldexp (1, -exponent) ;
        if ( PrintLevel >= 1 )
        {
            printf ("initial scale_x: %e scale_l: %e\n",
                     Com.scale_x, Com.scale_l) ;
        }
#if 0
pproj_printX (singlo, ppdata->nsing, "singlo") ;
pproj_printX (singhi, ppdata->nsing, "singhi") ;
pproj_printX (singc, ppdata->nsing, "singc") ;
pproj_printX (Bl, nrow, "b_pasa") ;
pproj_printX (c, ncol, "c_pasa") ;
pproj_printX (lo, ncol, "lo_pasa") ;
#endif
        /* problem solved by gradprojLP so both pasa and pproj statistics
           will be reported */
        Stats->use_pasa = TRUE ;

        status = pasa_gradprojLP (&Com) ;

        if ( status == PPROJ_OUT_OF_MEMORY ) pasa_wrapup (status, TRUE, &Com) ;
        else                                 pasa_wrapup (status, FALSE, &Com) ;

        return (status) ;
    }

    /* For quadratic objectives, we need to convert between the user
       ordering of variables, and the ordering used in pproj. When bound
       constraints exist, then the ordering is stored in ifree. If there
       are no bound constraints, but either pproj is used or
       there are fixed variables, then the ordering is based on colperm.
       Otherwise, no reordering is needed. */

    if ( QP == TRUE )
    {
        Com.order = NULL ;
        if ( Com.order_with_ifree == TRUE )
        {
            Com.order = Com.ifree ;
        }
        else if ( Com.order_with_colperm == TRUE )
        {
            Com.order = Com.colperm ;
        }
    }

#ifndef NDEBUG
    if ( use_pproj ) Com.ppdataDEBUG = ppdata ;
    else             Com.ppdataDEBUG = NULL ;
#endif

    /* If there are linear constraints, do not insist on computing the
       factorization when generating a feasible point since the initial
       point may be feasible. If only a projection is being performed,
       then do not touch the user's parameters */
    Com.initial_projection = TRUE ;
    if ( use_pproj && !PROJ )
    {
        pprojparm->getfactor = FALSE ;
        pprojparm->return_data = TRUE ;
        pprojparm->use_prior_data = FALSE ;
    }

    /* project d (starting guess) onto polyhedron */

    /* Project the starting guess on the polyhedron. If the starting guess
       is feasible, then this terminates quickly. */

    if ( PrintLevel > 0 )
    {
        printf ("Project starting point on polyhedron\n") ;
    }

    if ( PROJ ) /* projection problem, solve it and return to user */
    {
        status = pasa_project (x, pasadata->y, &Com) ;

        /* when fixed variables present, copy free variables into userx
           and then copy userx to the solution vector x */
        if ( fixed == TRUE )
        {
            for (j = 0; j < ncol; j++)
            {
                Com.userx [ifree [j]] = x [j] ;
            }
            pasa_copyx (x, Com.userx, ucol) ;
        }

        /* pproj objective value not returned */
        pasastat->f = PASAINF ;
        pasastat->err = pprojstat->errdual ;
        pasa_wrapup (status, TRUE, &Com) ;

        /* print the statistics if requested */
        Stats->use_pproj = TRUE ;
        if ( pasaparm->PrintStat == TRUE )
        {
            pasa_print_stats (pasadata) ;
        }

        /* free memory in pasa */
        if ( Com.work != NULL )
        {
            pasa_free (Com.work) ;
        }
        if ( Com.iwork != NULL )
        {
            pasa_free (Com.iwork) ;
        }
        return (status) ;
    }
    else /* project starting guess on polyhedron */
    {
        status = pasa_project (Com.d, x, &Com) ; /* projection in Com.d */
    }

    /* if an error occurred in pasa_project, then return */
    if ( status > 1 )
    {
        pasa_wrapup (status, TRUE, &Com) ;
        return (status) ;
    }

    /* not unconstrained or a knapsack problem or a projection problem */
    Stats->use_pasa = TRUE ;

    /* set the linear constraint status for napheap if it is used */
    if ( use_napheap == TRUE )
    {
        if ( *Bl == *Bu )
        {
            Com.nap_constraint = 2 ;
        }
        else if ( *(Com.lambda) > PASAZERO )   /* active inequality */
        {
            Com.nap_constraint = -1 ;          /* lower bound */
        }
        else if ( *(Com.lambda) == PASAZERO )  /* inactive inequality */
        {
            Com.nap_constraint = 0 ;
        }
        else /* lambda < 0 */                  /* active inequality */
        {
            Com.nap_constraint = 1 ;           /* upper bound */
        }
    }

    /* copy unfixed components (free) components of projection into userx */
    if ( fixed == TRUE )
    {
        for (j = 0; j < ncol; j++)
        {
            Com.userx [ifree [j]] = Com.d [j] ;
        }
    }
    else
    {
        pasa_copyx (Com.userx, Com.d, ncol) ;
    }

    /* evaluate function and gradient at userx */
    t = PASAZERO ;
    pasa_evaluate (t, &t, &Com, "fg") ;

    if ( PrintLevel >= 1 )
    {
        printf ("starting function value: %25.15e\n", Com.f) ;
    }

    /* if starting function value is nan or infinite, then return */
    if ( (Com.f >= PASAINF) || (Com.f <= -PASAINF) || (Com.f != Com.f) )
    {
        status = PASA_STARTING_FUNCTION_VALUE_INFINITE_OR_NAN ;
        pasa_wrapup (status, TRUE, &Com) ;
        return (status) ;
    }

    if ( fixed == TRUE )
    {
        for (j = 0; j < ncol; j++)
        {
            Com.g [j] = Com.userg [ifree [j]] ;
        }
    }
    else
    {
        pasa_copyx (Com.g, Com.userg, ncol) ;
    }

    /* Store starting gradient in user's coordinates when the objective is
       quadratic. This will be updated internally in the code. */
    if ( QP == TRUE )
    {
        pasa_copyx (Com.gtot, Com.g, ncol) ;
    }

    /* When A exists, pproj is used to project onto the feasible set.
       The ordering of x in pproj is generally different from the user's
       ordering. Hence, the code employs pproj's ordering; when
       the user's function or gradient is evaluated, we need to convert
       to the user's ordering. Below, we convert x and g (the gradient)
       to pproj's ordering, and store data from pproj that is used to
       setup for the projection. Also, for an active set method, we need
       to store copies of pproj's data since it is needed during the
       initialization of the active set algorithm. As constraints become
       active or as variables become bound, this data is compressed. */
    if ( use_pproj == TRUE ) /* A exists and pproj is used */
    {
        /* in pasa_project, the priordata structure of ppdata was stored in
           Com.ppcom */
        Prob = Com.ppcom->Prob ;

        /* when pproj is called by pasa, the default starting guess is
           based on the bound structure for the solution to a prior problem */
        pprojparm->start_guess = 2 ;

        /* Since we employ the factorization in the error estimation,
           we do not allow pproj to terminate when the error tolerance is
           satisfied unless the matrix has been factored */
        pprojparm->getfactor = TRUE ;

        /* Throughout the code, Com.x is ordered according to pproj's
           ordering of variables; consequently, do not permute inside pproj */
        pprojparm->permute = FALSE ;
        /* use pproj permuted version of A */
        Com.Ap = Prob->Ap ;
        Com.Ai = Prob->Ai ;
        Com.Ax = Prob->Ax ;
        colperm = Prob->colperm ;

        /* copy the project in Com.d to x */
        pasa_convert_to_pproj (x, Com.d, colperm, ncol) ;
        /* grab some temporary work space from pproj */
        temp = Com.ppcom->Work->arrayd ;
        pasa_copyx (temp, Com.g, ncol) ;
        pasa_convert_to_pproj (Com.g, temp, colperm, ncol) ;
        pasa_copyx (Com.b, Prob->b, nrow) ;
        /* We delay the allocation of Com.bl and Com.bu until here since
           we set Com.bl = Bl and Com.bu = Bu before calling project */
        Prob->bl [0] = PASAZERO ;
        Prob->bu [0] = PASAZERO ;
        if ( ni > 0 )
        {
            pasa_copyx (Com.bl, Prob->bl, ni+1) ;
            pasa_copyx (Com.bu, Prob->bu, ni+1) ;
        }
        if ( loExists == TRUE )
        {
            pasa_copyx (Com.lo, Prob->lo, ncol) ;
        }
        if ( hiExists == TRUE )
        {
            pasa_copyx (Com.hi, Prob->hi, ncol) ;
        }
        if ( GradProjOnly == FALSE )
        {
            /* For an active method, also store a copy of the problem data in
               pproj format since it is needed during the initialization of the
               active set algorithm. */
            PPwork *Work ;
            Work = Com.ppcom->Work ;
            /* integer */
            Copy.ncol = ncol ;
            pasa_copyi (Copy.Ai,         Prob->Ai,         Asize    ) ;
            pasa_copyx (Copy.Ax,         Prob->Ax,         Asize    ) ;
            pasa_copyi (Copy.Ap,         Prob->Ap,         ncol+1   ) ;
            pasa_copyi (Copy.ATp,        Work->ATp,        nrow+1   ) ;
            pasa_copyi (Copy.ATi,        Work->ATi,        Asize    ) ;
            pasa_copyi (Copy.ineq_row,   Prob->ineq_row,   ni+2     ) ;
            pasa_copyi_int (Copy.sol_to_blk, Work->sol_to_blk, ni+2 ) ;
            /* we cannot allocate col_start until now since we do not know
               how many blocks are in the multilevel decomposition until
               after the initial projection */
            Copy.col_start =
                (PASAINT *) pasa_malloc (&status, (Work->blks+1), sI);
            /* terminate if there is not enough memory */
            if ( status == PASA_OUT_OF_MEMORY )
            {
                pasa_wrapup (status, FALSE, &Com) ;
                return (status) ;
            }

            pasa_copyi (Copy.col_start, Work->col_start, Work->blks+1) ;

            /* float */
            pasa_copyx (Copy.ATx,      Work->ATx,      Asize) ;
            pasa_copyx (Copy.b,        Prob->b,        nrow ) ;
            pasa_copyx (Copy.bl,       Prob->bl,       ni+1 ) ;
            pasa_copyx (Copy.bu,       Prob->bu,       ni+1 ) ;
            if ( loExists == TRUE )
            {
                pasa_copyx (Com.copylo,Prob->lo,       ncol ) ;
            }
            if ( hiExists == TRUE )
            {
                pasa_copyx (Com.copyhi,Prob->hi,       ncol ) ;
            }
            for (i = 0; i < nrow; i++)
            {
                j = abs (Work->ir [i]) ;
                if ( j > ni )
                {
                    j -= ni ;
                }
                Com.row_to_ineq [i] = j ;
            }
        }
        if ( fixed == TRUE ) /* fixed variables exist */
        {
            for (j = 0; j < ncol; j++)
            {
                /* colperm [j] = compressed index associated with pproj index j
                   ifree   [j] = user index associated with compressed index j*/
                Com.colperm [j] = ifree [colperm [j]] ;
                /* colperm [j] = user index associated with pproj index j */
            }
        }
        else /* no fixed variables */
        {
            pasa_copyi (Com.colperm, colperm, ncol) ;
        }

        /* for an active set method, we need the inverse permutation */
        if ( GradProjOnly == FALSE )
        {
            for (j = 0; j < ncol; j++)
            {
                Com.invperm [Com.colperm [j]] = j ;
            }
        }
    }
    else /* pproj not used */
    {
        /* put the projection in x */
        pasa_copyx (x, Com.d, ncol) ;
        if ( fixed == TRUE ) /* fixed variables exist */
        {
            pasa_copyi (Com.colperm, ifree, ncol) ;
        }
        if ( GradProjOnly == FALSE ) /* only bound constraints + active set */
        {
            pasa_copyx (Com.copylo, lo, ncol) ;
            pasa_copyx (Com.copyhi, hi, ncol) ;
        }
    }
#ifndef NDEBUG
    /* When pasa is used in debugging mode, it computes a projection twice
       to confirm the correctness of the first computation. Since the
       input arguments to pproj on the second call to it are different,
       we have another ppdata structure in the debugging mode. */
    Com.ppdataDEBUG      = &ppdataDEBUG ;
    ppdataDEBUG.lambda   = lambda ;
    ppdataDEBUG.Parm     = pprojparm ;
    ppdataDEBUG.nrow     = nrow ;
    ppdataDEBUG.nsing    = nsing ;
    /* ncol, ni, nsing, lo, hi, Bl, Bu change as variables become fixed */
#endif

    /* done using ifree */
    if ( fixed == TRUE )
    {
        pasa_free (ifree) ;
        Com.temp_ifree  = NULL ;
    }

    /* Perform a unit step along the negative gradient to estimate the
       global error. When A does not exist, the projection operator is
       simply truncation, and the gradproj_step routine also estimates
       the local error.  If the matrix has not been factored, then
       we need to estimate the local error after the global error since
       the factor is used in the local error computation. toggle specifies
       the routine that will be executed -- more explanation below. */
    if ( use_pproj == TRUE )
    {
        /* Henceforth, there is no need to malloc arrays or compute and
           ordering in pproj. */
        pprojparm->use_prior_data = TRUE ;

        /* since the initial projection may not provide much bound info
           (especially if the initial guess was feasible), we start off
           by running phase1 in pproj and do not exploit the bound
           structure of the initial point */
        Com.initial_projection = TRUE ;
    }
    toggle = PASA_GRAD_PROJ ;       /* default */
    if ( Aexists == FALSE )
    {
        if ( PrintLevel >= 1 )
        {
            printf ("part 0\n") ;
        }
        status = pasa_gradproj_step (PASAONE, &Com);
    }
    else if ( (use_napheap == TRUE) || (Com.ppcom->Work->fac == TRUE) )
    {
        if ( PrintLevel >= 1 )
        {
            printf ("part 1\n") ;
        }

        /* Estimate local error before global error. The local error
           is gotten by projecting the gradient into the null space
           associated with the current active constraints. Note that
           null_project will not alter g */
        pasa_null_project (NULL, Com.g, NULL, TRUE, &Com) ;

        /* Estimate the global error by projecting x - g on the polyhedron.
           The multiplier associated with this projection is not stored
           in Com->lambda; we retain the multiplier associated with
           the original point before the step along negative gradient. */
        status = pasa_gradproj_step (PASAONE, &Com);
    }
    else /* do local error after global error */
    {
        if ( PrintLevel >= 1 )
        {
            printf ("part 2\n") ;
        }
        status = pasa_gradproj_step (PASAONE, &Com);
        /* Estimate the local error by projecting gradient into null space
           associated with current active constraints. The local error
           estimate is erroneous since it corresponds to the active
           constraints at the unit step projected point computed in
           gradproj_step */
        pasa_null_project (NULL, Com.g, NULL, TRUE, &Com) ;
    }
    if ( status > 1 ) /* an error occurred */
    {
        pasa_wrapup (status, FALSE, &Com) ;
        return (status) ;
    }

    if ( Com.E <= grad_tol ) /* convergence tolerance satisfied */
    {
        status = PASA_ERROR_TOLERANCE_SATISFIED ;
        pasa_wrapup (status, FALSE, &Com) ;
        return (status) ;
    }

    /* compute tolerance for switching between grad_proj and active set method*/
    t = pasaparm->switchfactor * Com.E ;
    Com.testtol = PASAMAX (t, grad_tol) ;

    /* if an active set method will be used, determine whether to start
       in GRAD_PROJ or in ACTIVE_GP */
    if ( GradProjOnly == FALSE )
    {
        /* Com.e = local error, Com.E = global error.
           The switch between the gradient projection algorithm and
           the active set algorithm is controlled by the variable toggle.
           toggle = PASA_GRAD_PROJ => use gradproj,
           toggle = PASA_ACTIVE_GP => use active set algorithm gradient project,
           toggle = PASA_CG_DESCENT => use CG
           e  >= switchfactor*E => use active set method (ACTIVE_GP/CG_DESCENT),
           e  <  switchfactor*E => use gradient projection method (GRAD_PROJ) */
        toggle = (Com.e >= Com.testtol) ? PASA_ACTIVE_GP : PASA_GRAD_PROJ ;
    }

    /* If the problem is unconstrained, then use cg_descent unless
       GradProjOnly is TRUE */
    if ( (Bounds == FALSE) && (Aexists == FALSE) )
    {
        Com.testtol = grad_tol ;
        if ( GradProjOnly == FALSE )
        {
            toggle = PASA_CG_DESCENT ;
            pasa_update_bb (&Com) ;
        }
    }
    if ( PrintLevel >= 1 )
    {
        printf ("Global error: %e Local error: %e testtol: %e\n",
                 Com.E, Com.e, Com.testtol) ;
    }

    while ( 1 )
    {
        /* ==================================================================
           If xk is the current iteration, then we compute pk, the
           projection of xk - bbk*gk onto the polyhedron, where bbk is
           the BB cyclic BB parameter at step k.  A line search is
           performed on the line segment [xk, pk] to generate xk+1.
           ================================================================== */
        if ( toggle == PASA_GRAD_PROJ )
        {
            status = pasa_grad_proj (&Com) ;
        }
        /* ==================================================================
           The active set algorithm is implemented using two different
           algorithms. If xk is the current iterate, then the algorithms are

           A. The gradient projection algorithm, but the bound variables
              at x_k, are kept fixed at their bounds, while the inequalities
              Bl <= Ax <= Bu that are active at xk are kept active when
              projecting the gradient into the polyhedron.

           B. The conjugate gradient algorithm but with all gradients
              replaced by gradients projected into the current active bounds
              and inequalities.

              We stop the active set gradient projection algorithm when the
              number of iterations since there is an addition to the active
              constraints exceeds pasaparm->terminate_agp. We stop the projected
              conjugate gradient algorithm whenever a new variable reaches
              a bound or a new inequality constraint becomes active.
           ================================================================== */
        else if ( toggle == PASA_ACTIVE_GP )
        {
            /* active set gradient projection algorithm */
            status = pasa_activeGP (&Com) ;
        }
        else /* ( toggle == PASA_CG_DESCENT ) conjugate gradient algorithm */
        {
            cgparm->grad_tol = Com.testtol ;
            cgdata->x    = Com.x ;
            cgdata->n    = Com.nf ;
            cgdata->Work = cgwork ;
            status = pasa_cg_descent (cgdata, &Com);
        }
        if ( status < 0 )
        {
            toggle = status ;
        }
        else /* the problem was solved or an error occurred */
        {
            break ;
        }
    }
    pasa_wrapup (status, FALSE, &Com) ;
    return (status) ;
}

/* ==========================================================================
   === pasa_setup ===========================================================
   ==========================================================================
    Generate a pointer to a PASAdata structure with default values for all
    parameters, and NULL values for all pasa inputs. Return NULL pointer if
    there is not enough memory.
   ========================================================================== */
PASAdata * pasa_setup (void)
{
    int status ;
    /* Initialize PASAdata structure */
    PASAdata *Data ;

    status = PASA_OK ;
    /* Allocate memory for parameters and statistics, initialize parameters */
    Data = (PASAdata  *) pasa_malloc (&status, 1, sizeof (PASAdata)) ;
    if ( status == PASA_OUT_OF_MEMORY )
    {
        Data = NULL ;
        return (Data) ;
    }
    Data->Parms = (PASAparms *) pasa_malloc (&status, 1, sizeof (PASAparms)) ;
    Data->Stats = (PASAstats *) pasa_malloc (&status, 1, sizeof (PASAstats)) ;
    Data->cgdata = cg_setup () ;
    Data->napdata = napheap_setup () ;
    if ( (status == PASA_OUT_OF_MEMORY) || (Data->cgdata == NULL)
                                        || (Data->napdata == NULL) )
    {
        Data = NULL ;
        return (Data) ;
    }
    Data->Stats->use_pasa    = FALSE ;
    Data->Stats->use_cg      = FALSE ;
    Data->Stats->use_pproj   = FALSE ;
    Data->Stats->use_napheap = FALSE ;

    /* pasa Parm and Stat structures */
    Data->Parms->pasa = (PASAparm *) pasa_malloc (&status, 1, sizeof(PASAparm));
    Data->Stats->pasa = (PASAstat *) pasa_malloc (&status, 1, sizeof(PASAstat));
    if ( status == PASA_OUT_OF_MEMORY )
    {
        Data = NULL ;
        return (Data) ;
    }
    /* set pasa Parm to default values */
    pasa_default (Data->Parms->pasa) ;

    /* store Parm and Stat structures of cg and napheap in Parms and Stats */
    Data->Parms->cg      = Data->cgdata->Parm ;
    Data->Stats->cg      = Data->cgdata->Stat ;
    Data->Parms->napheap = Data->napdata->Parm ;
    Data->Stats->napheap = Data->napdata->Stat ;

#ifndef NOPPROJ
    /* PPROJ could be used */
    Data->ppdata = pproj_setup () ;
    if ( Data->ppdata == NULL )
    {
        Data = NULL ;
        return (Data) ;
    }
    Data->Parms->pproj = Data->ppdata->Parm ;
    Data->Stats->pproj = Data->ppdata->Stat ;

#else
    /* PPROJ not used */
    Data->Parms->pproj = NULL ;
    Data->Stats->pproj = NULL ;
    Data->ppdata       = NULL ;
#endif

    /* set the remaining values in the PASAdata structure to EMPTY (= -1)
       or NULL */
    Data->x = NULL ;

    Data->lambda = NULL ;


    /* number of rows in A not specified by default (pasa will set nrow to be
       the maximum entry in Ai if a sparse matrix is present, while nrow is
       0 for unconstrained and bound constrained optimization, and it is
       1 for a napsack constraint). */
    Data->nrow = EMPTY ;

    /* number of components of x (number of cols in A); a value for
       Data->ncol must be provided by the user */
    Data->ncol = EMPTY ;

    /* size ncol+1, column pointers */
    Data->Ap = NULL ;

    /* size Ap [ncol], row indices for A, increasing order in each column */
    Data->Ai = NULL ;

    /* size Ap [ncol], numerical entries of A */
    Data->Ax = NULL ;

    /* size nrow, lower bounds for b above*/
    Data->bl = NULL ;

    /* size nrow, upper bounds for b above*/
    Data->bu = NULL ;

    /* size ncol, lower bounds for x */
    Data->lo = NULL ;

    /* size ncol, upper bounds for x */
    Data->hi = NULL ;

    /* size ncol, point projected onto the polyhedron by PPROJ */
    Data->y = NULL ;

    /* size ncol, the linear term for a quadratic or linear objective is c'x,
       except in NAPHEAP where the linear term is -c'x. */
    Data->c = NULL ;

    /* size ncol, linear constraint vector in NAPHEAP constraint
      *bl <= a'*x <= *bu. Note that Data->bl and Data->bu are arrays in
      general, but for a napsack problem, the arrays have only one element. */
    Data->a = NULL ;

    /* size ncol, diagonal of quadratic term in NAPHEAP */
    Data->d = NULL ;

    /* For a quadratic objective, hprod evaluates the Hessian times a vector.
       There are two different routines. For an unconstrained optimization
       problem, the routine is cg_hprod (p, x, n) where the problem dimension
       n and x are given, while the routine should generate p = H*x,
       where H is the Hessian of the objective. When constraints are
       present, the routine is hprod (p, d, F, m, n) where d and F have
       m components, and p = H(:, F)*d in MATLAB notation. That is,
       p = sum_{j in F} H(:, j)*d(j) */
       
    Data->hprod    = NULL ;
    Data->cg_hprod = NULL ;

    /* evaluate the function f at x, value (f, x, n) */
    Data->value = NULL ;

    /* evaluate the gradient at x, grad  (g, x, n) */
    Data->grad = NULL ;

    /* evaluate the function f and gradient g at x, valgrad (f, g, x, n) */
    /* NULL => use value & grad */
    Data->valgrad = NULL ;

    /* evaluate Hessian */
    Data->hess = NULL ;

    /* NULL => allocate real work space. */
    Data->xWork = NULL ;

    /* NULL  => allocate integer work space. */
    Data->iWork = NULL ;

    /* None of the routines require a starting guess for the multiplier lambda.
       If the user does not provide a pointer to the lambda array, then PASA
       allocates memory for lambda and returns the multiplier in lambda.
       pasa_terminate will free any allocated memory. The lambda_created pointer
       is only used internally by PASA when it creates or frees memory for
       lambda.

       Routines such as NAPHEAP, PPROJ, and the LP do not require
       a starting guess. If the user does not provide a pointer to x,
       then PASA allocates memory for the solution x. pasa_terminate
       will free any allocated memory. The x_created pointer is only
       used internally by PASA when it creates or frees  memory for x */
    Data->x_created = NULL ;

    Data->lambda_created = NULL ;

    /* Return pointer to PASAdata structure */
    return (Data) ;
}

/* ==========================================================================
   === pasa_terminate =======================================================
   ==========================================================================
    Free allocated memory associated with PASAdata structure
   ========================================================================== */
void pasa_terminate
(
    PASAdata **DataHandle
)
{
    PASAdata *Data ;
    Data = *DataHandle ;
    /* Free memory allocated in pasa_setup */
    pasa_free (Data->Parms->pasa) ;
    pasa_free (Data->Stats->pasa) ;
    if ( Data->lambda_created != NULL )
    {
        if ( Data->lambda_created == Data->lambda )
        {
            pasa_free (Data->lambda) ;
            Data->lambda = Data->lambda_created = NULL ;
        }
        else /* user created lambda */
        {
            pasa_free (Data->lambda_created) ;
            Data->lambda_created = NULL ;
        }
    }
    if ( Data->x_created != NULL )
    {
        if ( Data->x_created == Data->x )
        {
            pasa_free (Data->x) ;
            Data->x = Data->x_created = NULL ;
        }
        else /* user created x */
        {
            pasa_free (Data->x_created) ;
            Data->x_created = NULL ;
        }
    }

    cg_terminate (&(Data->cgdata)) ;
    napheap_terminate (&(Data->napdata)) ;
#ifndef NOPPROJ
    pproj_terminate (&(Data->ppdata)) ;
#endif
    pasa_free (Data->Parms) ;
    pasa_free (Data->Stats) ;

    /* Free Data struct */
    pasa_free (Data) ;
}

/* ==========================================================================
   === pasa_autodetect ======================================================
   ==========================================================================
    Analyze the user's pasadata structure and attempt to classify the
    problem into one of the following 7 classes:
        UNC     - unconstrained optimization:
                      min f(x) s.t. x in R^n
        BNC     - purely bound constrained optimization:
                      min f(x) s.t. lo <= x <= hi
        LP      - linear program:
                      min c'*x s.t. lo <= x <= hi, bl <= A*x <= bu
        QP      - quadratic program:
                      min 0.5*x'*Q*x + c'*x s.t. lo <= x <= hi, bl <= A*x <= bu
        NL      - nonlinear program:
                      min f(x) s.t. lo <= x <= hi, bl <= A*x <= bu
        NAPSACK - separable quadratic with napsack constraints:
                      min 0.5*x*'D*x + c'*x s.t. lo <= x <= hi, bl <= a'*x <= bu
        PROJ    - polyhedral constrained projection:
                      min 0.5*||x - y||^2 s.t. lo <= x <= hi, bl <= A*x <= bu
   ========================================================================== */
int pasa_autodetect
(
    PASAdata *pasadata
)
{
    /* If y was provided, then the problem must be a projection. */
    if ( pasadata->y != NULL )
    {
        return (PASA_PROJ) ;
    }
    /* if cg_hprod or hprod is given, then the problem is assumed to be a QP */
    else if ( (pasadata->hprod != NULL) || (pasadata->cg_hprod != NULL) )
    {
        return (PASA_QP) ;
    }
    /* If (a and d) or (a and c) or (d and c) were provided,
       then the problem structure is napsack */
    else if ( ((pasadata->a != NULL) && (pasadata->d != NULL)) ||
              ((pasadata->a != NULL) && (pasadata->c != NULL)) ||
              ((pasadata->d != NULL) && (pasadata->c != NULL)) )
    {
        return (PASA_NAPSACK) ;
    }
    /* check problem constraints */
    else if ( (pasadata->bl == NULL) && (pasadata->bu == NULL) )
    {
        /* problem does not have polyhedral constraints */
        if ( (pasadata->lo != NULL) || (pasadata->hi != NULL) ) 
        {   /* problem has bound constraints */
            return (PASA_BNC) ;
        }
        else
        {   /* problem is unconstrained */
            return (PASA_UNC) ;
        }
    }
    else if ( (pasadata->Ap != NULL) || (pasadata->a != NULL) )
    {   /* problem has polyhedral constraints */
        if (pasadata->c != NULL)
        {
            /* problem is LP */
            return (PASA_LP) ;
        }
        else if ( pasadata->value != NULL )
        {
            /* problem has a nonlinear objective */
            return (PASA_NL) ;
        }
        else /* missing objective */
        {
            return (PASA_MISSING_OBJECTIVE) ;
        }
    }
    else
    {
        return (PASA_RHS_GIVEN_BUT_MATRIX_MISSING) ;
    }
}

/* =========================================================================
   ============================== pasa_malloc ==============================
   ========================================================================= */
void * pasa_malloc
(
    int *status,
    PASAINT   n,
    int    size
)
{
    void *p ;
    if ( n > 0 )
    {
#ifdef MATLAB_MEX_FILE
        p = mxMalloc (n*size) ;
#else
        p = malloc (n*size) ;
#endif
    }
    else
    {
        return (NULL) ;
    }
    if ( p == NULL )
    {
        *status = PASA_OUT_OF_MEMORY ;
    }
    return (p) ;
}

/* =========================================================================
   ============================== pasa_free ================================
   ========================================================================= */
void pasa_free
(
    void * p
)
{

#ifdef MATLAB_MEX_FILE
    mxFree (p) ;
#else
    free (p) ;
#endif

}

/* ========================================================================== */
/* ====== pasa_error ======================================================== */
/* ========================================================================== */
/* when -g compiler option is used, prints line number of error
   ========================================================================== */
void pasa_error
(
    int status,
    const char *file,
    int line,
    const char *message
)
{
    if (status < 0)
    {
        printf ("file: %s line: %d status: %d %s\n",
                 file, line, status, message) ;
#ifdef MATLAB_MEX_FILE
        mexErrMsgTxt (message) ;
#else
        ASSERT (0) ;
        abort ( ) ;
#endif
    }
}

/* ==========================================================================
   === pasa_wrapup ==========================================================
   ==========================================================================
    Final messages and free memory before returning to user
   ========================================================================== */
void pasa_wrapup
(
    int     status, /* termination status */
    int fastreturn, /* T => return after printing status and freeing temp mem */
    PASAcom   *Com  /* common data for PASA */
)
{
    int location ;
    PASAparm *pasaparm ;
    PASAstat *pasastat ;

    pasaparm = Com->pasaparm ;
    pasastat = Com->pasastat ;
    pasastat->status = status ;

    /* free any temporary variables that were created */
    if ( Com->temp_userBl != NULL ) pasa_free (Com->temp_userBl) ;
    if ( Com->temp_userBu != NULL ) pasa_free (Com->temp_userBu) ;
    if ( Com->temp_ifree  != NULL ) pasa_free (Com->temp_ifree) ;

    /* print the run status */
    if ( pasaparm->PrintStatus && fastreturn )
    {
        if ( status != PASA_ERROR_TOLERANCE_SATISFIED )
        {
            pasastat->err = PASAINF ;
            pasastat->f = PASAINF ;
        }
        pasa_print_status (Com->pasadata) ;
        printf ("\n") ;
    }

    if ( fastreturn ) return ;

    /* store final f and error in pasa statistics structure */
    pasastat->err = Com->E ;
    location = Com->location ;
    if ( (location == PASA_BASE_CODE) || (location == PASA_CG_DESCENT) ||
         (location == PASA_GRADPROJ_LP) )
    {
        pasastat->f = Com->f ;
    }
    else
    {
        pasastat->f = Com->fnew ;
    }
    if ( Com->LP )
    {
        /* add any final adjustment to the linear cost that was
           specified by the user in the parm structure */
        pasastat->f += pasaparm->fadjust ;
    }
    if ( Com->QP )
    {
        /* add any final adjustment to the quadratic cost that was
           specified by the user in the parm structure */
        pasastat->f += pasaparm->fadjust ;
    }

    /* print the run status */
    if ( pasaparm->PrintStatus && !fastreturn )
    {
        pasa_print_status (Com->pasadata) ;
        printf ("\n") ;
    }

    /* print the statistics */
    if ( (pasaparm->PrintStat == TRUE) )
    {
        pasa_print_stats (Com->pasadata) ;
    }

    /* The optimal x is stored in userx with the following exceptions:
       1. If QP is TRUE, then we need to map the current iterate
          into userx and then copy the composite back to x
       2. If GradProjOnly is TRUE, there is no A, and there are no fixed
          variables, then x contains the optimal solution. */
    if ( Com->QP )
    {
        if ( (location == PASA_ACTIVE_GP) || (location == PASA_CG_DESCENT) )
        {
            if ( Com->order != NULL )
            {
                pasa_convert_to_user (Com->userx, Com->x, Com->order, Com->nf) ;
                pasa_copyx (Com->x, Com->userx, Com->ucol) ;
            }
        }
        else /* location = base code or grad_proj */
        {
            if ( Com->colperm != NULL )
            {
                pasa_convert_to_user (Com->userx, Com->x, Com->colperm,Com->nf);
                pasa_copyx (Com->x, Com->userx, Com->ucol) ;
            }
        }
    }
    else /* not a quadratic objective */
    {
        if ( (Com->Aexists == TRUE) || (Com->fixed == TRUE) ||
             (Com->nf < Com->ucol) )
        {
             /* userx contains x in user's coordinates */
             pasa_copyx (Com->x, Com->userx, Com->ucol) ;
        }
        /* otherwise x contains the solution in user coordinates */
    }

    /* free memory */
    if ( (Com->use_pproj == TRUE) && (pasaparm->GradProjOnly == FALSE) )
    {
        if ( Com->Copy->col_start != NULL )
        {
            pasa_free (Com->Copy->col_start) ;
        }
    }
    if ( Com->work != NULL )
    {
        pasa_free (Com->work) ;
    }
    if ( Com->iwork != NULL )
    {
        pasa_free (Com->iwork) ;
    }

    /* restore pproj parameter values possibly modified in the main pasa code */
    if ( Com->use_pproj )
    {
        PPparm *pprojparm = Com->pprojparm ;
        pprojparm->grad_tol       = Com->userparms.p_grad_tol ;
        pprojparm->PrintStatus    = Com->userparms.p_PrintStatus ;
        pprojparm->return_data    = Com->userparms.p_return_data ;
        pprojparm->use_prior_data = Com->userparms.p_use_prior_data ;
        pprojparm->loExists       = Com->userparms.p_loExists ;
        pprojparm->hiExists       = Com->userparms.p_hiExists ;
        pprojparm->getfactor      = Com->userparms.p_getfactor ;
        pprojparm->permute        = Com->userparms.p_permute ;
        pprojparm->start_guess    = Com->userparms.p_start_guess ;
        pprojparm->use_startup    = Com->userparms.p_use_startup ;
        pprojparm->LP             = Com->userparms.p_LP ;
    }
    else if ( Com->use_napheap )
    {
        NAPparm *napparm = Com->napparm ;
        napparm->PrintStatus    = Com->userparms.n_PrintStatus ;
        napparm->return_data    = Com->userparms.n_return_data ;
        napparm->use_prior_data = Com->userparms.n_use_prior_data ;
        napparm->loExists       = Com->userparms.n_loExists ;
        napparm->hiExists       = Com->userparms.n_hiExists ;
        napparm->d_is_one       = Com->userparms.n_d_is_one ;
    }
    else if ( Com->use_cg )
    {
        CGparm *cgparm = Com->cgparm ;
        cgparm->PrintStatus     = Com->userparms.c_PrintStatus ;
        cgparm->PrintStat       = Com->userparms.c_PrintStat ;
        cgparm->PrintParm       = Com->userparms.c_PrintParm ;
        cgparm->fadjust         = Com->userparms.c_fadjust ;
    }

#ifndef NOPPROJ
    /* if polyhedral constraints present, free associated pproj memory */
    if ( Com->use_pproj == TRUE )
    {
        /* free memory unless pproj terminated before malloc's performed */
        if ( (status != PPROJ_OUT_OF_MEMORY) &&
             (status != PPROJ_MISSING_PRIOR_DATA) &&
             (status != PPROJ_START_GUESS_NEEDS_PRIOR_DATA) &&
             (status != PPROJ_INVALID_LINEAR_CONSTRAINT_BOUNDS) &&
             (status != PPROJ_BOTH_NI_AND_NSING_POSITIVE) &&
             (status != PPROJ_NSING_START_GUESS_PROB) )
        {
            pproj_freeAll (&(Com->ppcom)) ; /* free the pproj memory */
            Com->pasadata->ppdata->priordata = NULL ;
        }
    }
#endif
}

/* ==========================================================================
   === pasa_grad_proj =======================================================
   ==========================================================================
    Perform the gradient projection algorithm until the termination condition
    local error e >= theta * global error E is satisfied. If xk is the
    current iteration, then we compute pk the projection of xk - bbk*gk
    onto the polyhedron.  A line search is performed on the
    line segment [xk, pk] to generate xk+1. The value of bbk is obtained
    from a cyclic version of the BB algorithm. This means that we reuse
    the BB approximation bbk*I to the Hessian for one or more iterations
    before updating its value. The cycle length is the number of times
    that the bb parameter is reused.
   ========================================================================== */
int pasa_grad_proj /* return:
                       PASA_ACTIVE_GP
                       PASA_ERROR_TOLERANCE_SATISFIED
                       PASA_POLYHEDRON_INFEASIBLE
                       PASA_INVALID_LINEAR_CONSTRAINT_BOUNDS
                       PASA_ITERATIONS_EXCEED_MAXITS_IN_GRAD_PROJ
                       PASA_LINE_SEARCH_STEPS_EXCEED_MAXSTEPS_IN_GRAD_PROJ
                       PASA_OUT_OF_MEMORY_IN_PPROJ
                       PASA_SSOR_ASCENT_FAILURE_IN_PPROJ
                       PASA_SEARCH_DIRECTION_NOT_DESCENT_DIRECTION_IN_GRAD_PROJ
                       PASA_SSOR_ITERATIONS_EXCEED_MAX_ITS_IN_PPROJ */
(
    PASAcom *Com  /* common data for PASA */
)
{
    int its_since_test, na, PrintLevel, status, test_increment ;
    PASAINT i, j, k, n, nf, row, *colperm, *ifree ;
    PASAFLOAT alpha, muAd, delta, deltaf, dQd, fR, fRpert,
              gtd, gtdnew, maxsteps, pert, s, safe0, safe1,
              stepdecay, t, *d, *x, *g, *gnew ;
    PASAparm *Parm ;      /* parameters for PASA */
    PASAstat *Stat ;      /* statistics for PASA */
    CGstat *cgstat ;      /* statistics for CG_DESCENT */
    PASAcopy *Copy ;      /* copy of pproj's prob structure */
    PPprob   *Prob ;      /* problem in PPROJ */
    PPwork   *Work ;      /* work structure in PPROJ */

    /* if the code comes from CG and an estimate for the bb parameter
       was obtained, then we use it */
    if ( Com->location == PASA_CG_DESCENT )
    {
        if ( Com->cg_bb_est > PASAZERO )
        {
            Com->bbk = Com->cg_bb_est ;
        }
    }
    Com->location = PASA_GRAD_PROJ ;
    Parm = Com->pasaparm ; /* parameters */
    PrintLevel = Parm->PrintLevel ;
    if ( PrintLevel >= 1 )
    {
        printf ("START grad_proj, testtol: %e\n", Com->testtol) ;
    }
    const int Aexists = Com->Aexists ;
    const int fixed = Com->fixed ;
    const int GradProjOnly = Parm->GradProjOnly ;
    const int QP = Com->QP ;
    const int use_napheap = Com->use_napheap ;
    const int use_pproj = Com->use_pproj ;

    if ( use_pproj == TRUE )
    {
        Prob = Com->ppcom->Prob ;
    }
    n = Com->ncol ;
    x = Com->x ;
    g = Com->g ;
    Stat = Com->pasastat ; /* pasa statistics */
    cgstat = Com->cgstat ;
    nf = Com->nf ;
    Com->nf = n ;      /* all variables treated as free */
    ifree = Com->ifree ;
    colperm = Com->colperm ;

    /* if returning from the active set algorithm, then restore problem data */
    if ( Stat->agpit + cgstat->iter > 0 )
    {
        if ( PrintLevel >= 1 )
        {
            printf ("%ld free variables and %ld bound variables\n",
                   (LONG) nf, (LONG) (n-nf)) ;
        }
        Copy = Com->Copy ;
        /* when pproj is employed, matrices and bounds are restored */
        if ( use_pproj == TRUE )
        {
            int *temp ;
            PASAINT nnz, nc, nr, ni, nrow, p, q,
                    *Ap, *ATp, *ATi, *Ai, *Anz, *bound_cols, *bound_rows,
                    *invperm, *ir, *row_to_ineq, *ineq_row,
                    *col_beg, *col_end ;
            PASAFLOAT *Ax, *ATx ;
            /* extract number of bound variables (number of cols) */
            nc = Com->nc ;
            /* extract number of bound inequalities (number of rows) */
            nr = Com->nr ;
            if ( PrintLevel >= 1 )
            {
                printf ("%ld free and %ld bound inequalities\n",
                       (LONG) Prob->ni, (LONG) nr) ;
            }
            ASSERT (Prob->ni + nr == Com->ni) ;
            Work = Com->ppcom->Work ;
            nrow = Com->nrow ;
            pasa_copyi (Prob->Ap,             Copy->Ap,         n+1   ) ;
            pasa_copyi (Work->ATp,            Copy->ATp,        nrow+1) ;
            pasa_copyi (Work->AFTp,           Copy->ATp,        nrow+1) ;
            nnz = Prob->Ap [n] ;
            pasa_copyi (Work->ATi,            Copy->ATi,        nnz ) ;
            ni =                              Com->ni ;
            pasa_copyi (Prob->ineq_row,       Copy->ineq_row,   ni+2) ;
            pasa_copyi_int (Work->sol_to_blk, Copy->sol_to_blk, ni+2) ;
            pasa_copyx (Work->ATx,            Copy->ATx,        nnz ) ;
            pasa_copyx (Com->b,               Copy->b,          nrow) ;
            pasa_copyx (Com->bl,              Copy->bl,         ni+1) ;
            pasa_copyx (Com->bu,              Copy->bu,         ni+1) ;
            pasa_copyx (Com->lo,              Com->copylo,      n   ) ;
            pasa_copyx (Com->hi,              Com->copyhi,      n   ) ;
            pasa_copyi (Work->col_start,      Copy->col_start,  Work->blks+1) ;
            Work->A->ncol = n ;
            Work->A->nzmax = nnz ;

            if ( Com->Bounds == TRUE )
            {
                /* set Work->ib & Work->ir according to current factorization */
                invperm = Com->invperm ;

                ASSERT (nc == n - nf) ;

                /* integer work space, will use n ints */
                temp = (int *) Work->arrayi ;
                bound_cols = Com->bound_cols ;

                /* If a column was fixed in the compressed matrix, then
                   we need to fix it in the original matrix
                       ifree [j]   = variable in the original matrix associated
                                     with column j of compressed matrix
                       invperm [j] = column in pproj associated with column j
                                     in original matrix
                    First set ib for the bound variables, store in temp array.*/
                for (k = 0; k < nc; k++)
                {
                    j = bound_cols [k] ; /* index in original matrix */
                    if ( j < 0 )
                    {
                        j = -(j+1) ;
                        i = invperm [j] ;/* corresponding index in pproj */
                        temp [i] = -1 ;
                    }
                    else
                    {
                        i = invperm [j] ;
                        temp [i] = 1 ;
                    }
                }

                /* Then set ib for the current free variables. */
                for (j = 0; j < nf; j++) /* nf = # free columns from cg or agp*/
                {
                    temp [invperm [ifree [j]]] = Work->ib [j] ;
                }

                /* Copy temp into Work->ib */
                pasa_copyi_int (Work->ib, temp, n) ;
            }

            /* set ir greater than  ni for all dropped inequalities */
            ineq_row = Prob->ineq_row ;
            ir = Work->ir ;
            for (i = 1; i <= ni; i++)
            {
                row = ineq_row [i] ;
                if ( ir [row] > Prob->ni ) /* row is dropped */
                {
                    ir [row] = ni + 1 ;
                }
            }
            Prob->ni = ni ;
            bound_rows = Com->bound_rows ;
            row_to_ineq = Com->row_to_ineq ;

            /* in gradproj, all inequalities treated as inactive */
            Com->nr = 0 ;
            for (k = 0; k < nr; k++)
            {
                i = bound_rows [k] ;
                if ( i < 0 ) /* lower bind */
                {
                    row = -(i+1) ;
                    /* store negative number */
                    ir [row] = -row_to_ineq [row] ;
/*printf ("row %i is active at lower bound\n", row) ;*/
                }
                else         /* upper bind */
                {
                    row = i - 1 ;
                    /* store positive number */
                    ir [row] = row_to_ineq [row] ;
/* printf ("row %i is active at upper bound\n", row) ;*/
                }
            }
            Prob->ncol = n ;

            /* If ni > 0 construct A using AT and the current active rows.
               Otherwise, simply copy A. */
            Ap = Prob->Ap ;
            Ai = Prob->Ai ;
            Anz= Prob->Anz ;
            Ax = Prob->Ax ;

            if ( ni == 0 )
            {
                pasa_copyi (Ai, Copy->Ai, nnz) ;
                pasa_copyx (Ax, Copy->Ax, nnz) ;
                for (j = 0; j < n; j++)
                {
                    Anz [j] = Ap [j+1] - Ap [j] ;
                }
            }
            else
            {
                ATp = Work->ATp ;
                ATi = Work->ATi ;
                ATx = Work->ATx ;

                /* done using temp, now use arrayi for col_end and col_beg,
                   2n PASAINTs needed */
                col_end = Work->arrayi ;
                pasa_copyi (col_end, Ap+1, n) ;
                col_beg = col_end + n ;
                pasa_copyi (col_beg, Ap, n) ;

                p = 0 ;
                for (i = 0; i < nrow; i++)
                {
                    q = ATp [i+1] ;
                    if ( ir [i] <= ni ) /* row is active */
                    {
                        for (; p < q; p++)
                        {
                            j = ATi [p] ;
                            Ai [col_beg [j]] = i ;
                            Ax [col_beg [j]] = ATx [p] ;
                            col_beg [j]++ ;
                        }
                    }
                    else               /* row is dropped */
                    {
                        for (; p < q; p++)
                        {
                            j = ATi [p] ;
                            col_end [j]-- ;
                            Ai [col_end [j]] = i ;
                            Ax [col_end [j]] = ATx [p] ;
                        }
                    }
                }
                for (j = 0; j < n; j++)
                {
                    Anz [j] = col_beg [j] - Ap [j] ;
                }
            }
        }
        else if ( use_napheap == TRUE )
        {
            pasa_copyx (Com->nap_a, Com->nap_acopy, n) ;
            Com->nap_bl = Com->nap_blcopy ;
            Com->nap_bu = Com->nap_bucopy ;
            Com->nap_a2 = Com->nap_a2save = Com->nap_a2full ;
        }

        /* Extract the full x-vector and g-vector from the user's version.
           For a quadratic cost, we get g from gtot; otherwise, we get it
           from userg. For a quadratic cost, we first insert the free
           components of x in userx. For either quadratic or nonquadratic cost,
           we then map userx to x. */
        if ( QP == TRUE )
        {
            /* copy total gradient from user's coordinates to pasa coordinates*/
            if ( (fixed == TRUE) || (use_pproj == TRUE) )
            {
                pasa_convert_to_pproj (g, Com->gtot, colperm, n) ;
            }
            else
            {
                pasa_copyx (g, Com->gtot, n) ;
            }
            if ( Com->Bounds == TRUE )
            {
                for (j = 0; j < nf; j++)
                {
                    Com->userx [ifree [j]] = Com->x [j] ;
                }
            }
        }
        else /* not QP */
        {
            if ( (fixed == TRUE) || (use_pproj == TRUE) )
            {
                pasa_convert_to_pproj (g, Com->userg, colperm, n) ;
            }
            else
            {
                pasa_copyx (g, Com->userg, n) ;
            }
        }
        if ( (fixed == TRUE) || (use_pproj == TRUE) )
        {
            pasa_convert_to_pproj (x, Com->userx, colperm, n) ;
        }
        else
        {
            pasa_copyx (x, Com->userx, n) ;
        }

        /* Uncompress the bounds, all variables are free.
           These copies only occur when the variables exist. */
        pasa_copyx (Com->lo, Com->copylo, n) ;
        pasa_copyx (Com->hi, Com->copyhi, n) ;
    }

    /* in gradproj, all variables are treated as free */
    Com->nc = 0 ;

    /* Evaluate starting guess for bb parameter Com->bbk */
    Com->first = TRUE ; /* first iteration */
    status = pasa_update_bb (Com) ;
    if ( status == PASA_ERROR_TOLERANCE_SATISFIED )
    {
        return (PASA_ERROR_TOLERANCE_SATISFIED) ;
    }

    /* Initialize reference value to be the current function value */
    fR = pasa_update_reference (Com) ;

    /* subsequent calls to update_bb and update_reference are handled
       differently */
    Com->first = FALSE ;

    gnew = Com->gnew ;
    d = Com->d ;
    delta = Parm->Armijo_delta ;

    /* A nonmonotone Armijo line search is performed. An acceptable stepsize
       alpha satisfies the condition

               fnew <= fR + alpha*deltaf,  deltaf = delta*gk'*dk

       where dk = pk - xk and pk = projection (xk - bbk*gk) onto
       the polyhedron.  Here bbk is the BB parameter and alpha has
       the starting value 1.  If the acceptance condition is not satisfied,
       then we continue to decrease alpha until the acceptance condition
       is satisfied. Due to numerical errors that arise when xnew is near x,
       we may replace the Armijo acceptance condition by the following
       approximation, which can be evaluated more accurately than the
       original version of the acceptance condition when xnew is near x:

                   gnew'*dk <= 2(fR-f)/alpha + deltag,

        where deltag = (2*delta - 1)*gk'*dk.  This is an accurate
        approximation to the Armijo condition when xnew is close to
        x, but when xnew is far from x, it could yield a poor
        approximation. Hence, we also require fnew <= fRpert,
        which is slightly larger than fR. */
    delta = Parm->Armijo_delta ;
    maxsteps = Parm->maxsteps ; /* max number of Armijo backsteps */

    /* The reduction in alpha is done by different methods in different
       situations. One method is to multiply alpha by the factor
       armijo_decay. Another method is to approximate a local minimum of
       the function in the search direction using either a quadratic
       interpolation step or a secant step, with safe guards to ensure
       that the new alpha lies in the interval [safe0, safe1]*alpha */
    stepdecay = Parm->stepdecay ;
    safe0 = Parm->safe0 ;
    safe1 = Parm->safe1 ;

    if ( PrintLevel >= 1 )
    {
        printf ("GP starting function value: %e\n", Com->f) ;
    }
    its_since_test = 0 ; /* counts number of iterations since testing error */
    test_increment = 0 ; /* number of iteration until next test */
    if ( GradProjOnly == TRUE )
    {
        test_increment = Parm->testit ; /* less frequent initial test */
    }
    na = 0 ;

    while ( Stat->gpit < Parm->gpmaxit )
    {
        if ( PrintLevel >= 1 )
        {
            printf ("Ref value: %e  BB step: %e\n", Com->fr, Com->bbk) ;
        }
        Stat->gpit++ ;

        /* Project xk - bbk*gk onto the polyhedron.  The search direction
           dk = pk - xk is stored in Com->d. If A does not exist, then we also
           compute global and local errors. */
        status = pasa_gradproj_step (Com->bbk, Com) ;
        if ( status > 1 ) /* error occurred */
        {
            return (status) ;
        }

        if ( Aexists == FALSE ) /* bound constraints only */
        {
            if ( Com->E <= Com->grad_tol )
            {
                return (PASA_ERROR_TOLERANCE_SATISFIED) ;
            }
            if ( (GradProjOnly == FALSE) && (Com->e > Com->testtol) )
            {
                return (PASA_ACTIVE_GP) ;
            }
        }
        else /* Aexists is TRUE */
        {
            if ( Com->E <= Com->grad_tol )
            {
                return (PASA_ERROR_TOLERANCE_SATISFIED) ;
            }
        }

        /* to deactivate Lagrangian-based line search, change "if 1" to "if 0"*/
        muAd = PASAZERO ;
#if 1
        if ( use_pproj == TRUE )
        {
            PASAINT p, q, *Ai, *Ap ;
            PASAFLOAT u, v, *lambda, *Ax ;
            if ( Aexists == TRUE )
            {
                Ai = Prob->Ai ;
                Ax = Prob->Ax ;
                Ap = Prob->Ap ;
                lambda = Com->lambda ;
                v = PASAZERO ; /* = (lambda'*A)*d */
                for (j = 0; j < n; j++)
                {
                    if ( d [j] != PASAZERO )
                    {
                        u = PASAZERO ; /* = (lambda'*A)_j */
                        q = Ap [j+1] ;
                        for (p = Ap [j]; p < q; p++)
                        {
                            u += lambda [Ai [p]]*Ax [p] ;
                        }
                        v += u*d [j] ;
                    }
                }
                muAd = v/Com->bbk ;
            }
        }
        else if ( use_napheap == TRUE )
        {
            muAd = *(Com->lambda)*pasa_dot (d, Com->nap_a, n) ;
        }
#endif
        gtd = Com->gtd ;
        gtd -= muAd ;
        if ( muAd < PASAZERO )
        {
            muAd = PASAZERO ;
        }
        /* If gtd > 0, then current search direction not descent direction */
        if ( gtd > PASAZERO )
        {
            if ( PrintLevel >= 1 )
            {
                printf ("gtd = %e no descent\n", gtd) ;
            }
            return (PASA_SEARCH_DIRECTION_NOT_DESCENT_DIRECTION_IN_GRAD_PROJ) ;
        }

        /* initial stepsize in line search */
        alpha = PASAONE ;

        /* For a quadratic objective, we use the approximate Armijo line
           search. If the unit step is accepted, then we take it.
           Also, if d'Qd <= 0 (nonconvex) or -g'd/d'Qd >= 1 (exact min > 1),
           then we use a unit step.  Otherwise, we take alpha = -g'd/d'Qd,
           the exact minimizer in the search direction. */
        if ( QP == TRUE )
        {
            pasa_evaluate (PASAZERO, &alpha, Com, "fg") ;
            gtdnew = pasa_dot (gnew, d, n) ;
            if ( Com->order_with_colperm == TRUE )
            {
                dQd = PASAZERO ;
                /* dQd = pasa_dot (d, Com->Qd, n) but, Qd is in user's coor */
                for (j = 0; j < n; j++)
                {
                    dQd += d [j]*Com->Qd [colperm [j]] ;
                }
            }
            else
            {
                dQd = pasa_dot (d, Com->Qd, n) ;
            }
            if ( gtdnew > 2*(fR-Com->f) + (2.*delta - 1.)*gtd )
            {   /* unit step not accepted initially, check to see if the
                   exact minimizer lies on (0, 1] */
                if ( dQd > PASAZERO )
                {
                    alpha = PASAMIN (PASAONE, -Com->gtd/dQd) ;
                    /* If -Com->gtd <= 0, then d not a descent direction
                       although numerically it was ok above. In this case,
                       our best guess is alpha = 1. */
                    if ( alpha < PASAZERO )
                    {
                        alpha = PASAONE ;
                    }
                }
                /* else nonconvex, take the default unit step */
            }
            if ( alpha < PASAONE ) /* alpha minimizes quadratic */
            {
                /* update cost */
                Com->fnew = Com->f - 0.5*(Com->gtd)*(Com->gtd)/dQd ;

                /* update xnew */
                pasa_step (Com->xnew, Com->x, d, alpha, n) ;

                /* update gradient */
                pasa_step (Com->gtot, Com->gtot, Com->Qd, alpha, n) ;

                if ( (use_pproj == TRUE) || (fixed == TRUE) )
                {
                    /* copy gtot into gnew, adjust for user's coordinates  */
                    pasa_convert_to_pproj (gnew, Com->gtot, colperm, n) ;
                }
                else
                {
                    pasa_copyx (gnew, Com->gtot, n) ;
                }
            }
            else /* alpha = 1 */
            {
                /* update cost and gtot (xnew and gnew updated in
                   pasa_evaluate) */
                Com->fnew = Com->f + 0.5*dQd + Com->gtd ;

                /* update total gradient */
                pasa_step (Com->gtot, Com->gtot, Com->Qd, alpha, n) ;
            }
        }
        /* approximate Armijo stopping condition in line search? */
        else if ( Com->approxstep == TRUE )
        {
            if ( PrintLevel >= 1 )
            {
                printf ("approx armijo step in grad proj\n") ;
            }
            /* stopping condition in the approximate Armijo line search is

                   gnew'*d <= 2(fR-f)/alpha + deltaf,

               where deltaf = (2*delta - 1)*gk'*dk. This is an accurate
               approximation to the Armijo condition when xnew is close to
               x, but when xnew is far from x, it could yield a poor
               approximation. Hence, we also require fnew <= fRpert,
               which is slightly larger than fR.

               If PertRule = 1, then fRpert = fR + Parm->eps*|fR|,
               if PertRule = 0, then fRpert = fR + Parm->eps */

            if ( Parm->PertRule == 1 )
            {
                pert = Parm->pert_eps*fabs(fR) ;
            }
            else
            {
                pert = Parm->pert_eps ;
            }
            fRpert = fR + pert ;
            deltaf = (2.*delta - 1.)*gtd ;
            s = 2.*(fR - Com->f) + deltaf ; /* 2*(fR-f)/alpha + deltaf */
            for (na = 0; na < maxsteps; na++)
            {
                /* Evaluate func and grad at xnew = x + alpha*d. */
                status = pasa_evaluate (PASAZERO, &alpha, Com, "fg") ;
                if ( status == PASA_FUNCTION_NAN_OR_INF )
                {
                    return (status) ;
                }

                if ( PrintLevel >= 1 )
                {
                    printf ("    na: %i deltaf: %e fR: %e fnew: %e fold: %e\n",
                             na, deltaf, fR, Com->fnew, Com->f) ;
                }
                gtdnew = pasa_dot (gnew, d, n) ;
                {
                    if ( (gtdnew != gtdnew) || (gtdnew <= -PASAINF) )
                    {
                        gtdnew = PASAINF ;
                    }
                }
                if ( (gtdnew <= s) && (Com->fnew - alpha*muAd <= fRpert) )
                {
                    break ;
                }
                if ( gtdnew > PASAZERO ) /* newalpha computed by secant method*/
                {
                    t = gtd/(gtd - gtdnew) ;
                    if ( (t >= safe0) && (t <= safe1) )
                    {
                        alpha *= t ;
                    }
                    else
                    {
                        alpha *= stepdecay ;
                    }
                }
                else
                {
                    alpha *= stepdecay ;
                }
                /* change fR to f + fpert after the first iteration */
                s = 2.*pert/alpha + deltaf ;
                fRpert = Com->f + pert ;
            }
            /* Check whether to switch back to ordinary Armijo line search */
            if ( fabs(Com->f - Com->fnew) >= Parm->ArmijoSwitchFactor*fabs(fR) )
            {
                if ( Parm->PrintLevel >= 1 )
                {
                    printf ("-- change to ordinary Armijo line search\n") ;
                }
                Com->approxstep = FALSE ;
            }
        }
        else /* Ordinary Armijo line search */
        {
            if ( PrintLevel >= 1 )
            {
                printf ("ordinary armijo step\n") ;
            }
            /* stopping condition in the line search is
                   fnew <= fR + alpha*deltaf, where deltaf = delta*gk'*dk */
            deltaf = delta*gtd ;
            for (na = 0; na < maxsteps; na++)
            {
                /* evaluate objective at xnew = x + alpha*d */
                status = pasa_evaluate (PASAZERO, &alpha, Com, "f") ;
                if ( status == PASA_FUNCTION_NAN_OR_INF )
                {
                    return (status) ;
                }
                if ( PrintLevel >= 1 )
                {
                    printf ("    na: %i deltaf: %e fR: %e fnew: %e fold: %e\n",
                             na, deltaf, fR, Com->fnew, Com->f) ;
                }
                if ( Com->fnew <= fR + alpha*(deltaf+muAd) )
                {
                    break ;
                }
                fR = Com->f ; /* change fR to f after the first iter */
                /* Perform safeguarded quadratic interpolation step */
                t = 2.*(Com->fnew - alpha*(muAd+gtd) - Com->f) ;
                if ( t > PASAZERO )
                {
                    t = -gtd*alpha/t ;
                    /* if the minimizer of the quadratic outside the
                       interval [safe0, safe1]*alpha, then multiply alpha
                       by the decay factor */
                    if ( (t >= safe0) && (t <= safe1) )
                    {
                        alpha *= t ;
                    }
                    else
                    {
                       alpha *= stepdecay ;
                    }
                }
                else /* quadratic not convex, multiply alpha by decay factor */
                {
                    alpha *= stepdecay ;
                }
            }
            /* Check whether to switch to approximate Armijo line search */
            if ( fabs(fR-Com->fnew) < Parm->ArmijoSwitchFactor*fabs(fR) )
            {
                if ( PrintLevel >= 1 )
                {
                    printf ("-- change to approximate Armijo line search\n") ;
                }
                Com->approxstep = TRUE ;
            }
            /* evaluate grad at xnew computed above */
            t = -PASAONE ;
            pasa_evaluate (PASAZERO, &t, Com, "g") ;
        }
        if ( PrintLevel >= 1 )
        {
            printf ("GP iter: %ld na: %i fnew: %25.15e fold: %25.15e "
                    "alpha: %e\n",
                    (LONG) Stat->gpit, na, Com->fnew, Com->f, alpha) ;
        }
        if ( na == maxsteps )
        {
            return (PASA_LINE_SEARCH_STEPS_EXCEED_MAXSTEPS_IN_GRAD_PROJ) ;
        }
        Com->alpha = alpha ;
        if ( alpha == PASAONE )
        {
            Com->unitstep = TRUE ;
            /* if napheap is used, save the multiplier */
            if ( use_napheap == TRUE )
            {
                *(Com->lambda) = Com->nap_lambda ;
            }
        }
        else
        {
            Com->unitstep = FALSE ;
        }

        /* update the status of the linear constraint if napheap is used */
        if ( (use_napheap == TRUE) && (Com->nap_constraint < 2) )
        {
            if ( Com->unitstep == TRUE )
            {
                if ( Com->nap_lambda == PASAZERO )
                {
                    Com->nap_constraint = 0 ; /* strict inequality */
                }
                else if ( Com->nap_lambda > PASAZERO )
                {
                    Com->nap_constraint = -1 ;
                }
                else /* nap_lambda < 0 */
                {
                    Com->nap_constraint = 1 ;
                }
            }
            else /* unitstep is FALSE */
            {
                if ( Com->nap_lambda == PASAZERO )
                {
                    Com->nap_constraint = 0 ;
                }
                else if ( Com->nap_lambda < PASAZERO )
                {
                    /* if prior > 0, then no change, otherwise set to 0 */
                    if ( Com->nap_constraint <= 0 )
                    {
                        Com->nap_constraint = 0 ;
                    }
                }
                else /* nap_lambda > 0 */
                {
                    /* if prior < 0, then no change, otherwise set to 0 */
                    if ( Com->nap_constraint >= 0 )
                    {
                        Com->nap_constraint = 0 ;
                    }
                }
            }
        }

        /* update the BB parameter if necessary */
        status = pasa_update_bb (Com) ;

        pasa_copyx (Com->x, Com->xnew, n) ;
        pasa_copyx (Com->g, Com->gnew, n) ;
#ifndef NDEBUG
        if ( na > 0 ) /* test error decay */
        {
            if ( Com->f != PASAZERO ) /* test relative error */
            {
                if ( (Com->fnew - Com->f)/fabs (Com->f) > Parm->debugtol )
                {
                    printf ("old cost %25.15e < new cost %25.15e\n",
                              Com->f, Com->fnew) ;
                    pasa_error (-1, __FILE__, __LINE__, "stop") ;
                }
            }
        }
#endif
        Com->f = Com->fnew ;

        /* test the error if the criteria are satisfied */
        its_since_test++ ;
        if ( PrintLevel > 1 )
        {
            printf ("its_since_test: %i test_increment: %i Com->E: %e "
                    "Com->estE: %e Com->grad_tol: %e\n", its_since_test,
                     test_increment, Com->E, Com->estE, Com->grad_tol) ;
        }
        if ( (Aexists == TRUE) && ((its_since_test >= test_increment)
                                    || (Com->E <= Com->grad_tol)
                                    || (Com->estE <= Com->grad_tol)) )
        {
           /* If a unit step was taken, then estimate local error first.
              In this case, the current matrix corresponds to the current
              iterate so both the local and global error estimate are at
              the current iterate if we estimate the local error first.
              Estimate the local error Com->e by projecting gradient into
              the null space associated with current active constraints.
              If na > 0, this computation may underestimate Com->e since
              since there could be more constraints active in the
              current factorization than at the current iterate due to
              the backtracking. That is, when there is a backtrack,
              there is no change in the active constraints, while the
              factorization at the unit step may contain additional
              active constraints. Hence, when na > 0, we underestimate
              Com->e. */
            pasa_null_project (NULL, g, NULL, TRUE, Com) ;

            /* factor_ok is TRUE by default. It changes to FALSE if
               the global error is estimated, in which case the
               factorization could change; it may no longer correspond
               to the factorization associated with the current iterate. */
            Com->factor_ok = TRUE ;

            /* If the local error is sufficiently large relative to the
               previous estimate of the global error, then switch to
               the active set algorithm, no need to compute global error. */
            if ( (GradProjOnly == FALSE) && (Com->e > Com->testtol) )
            {
                return (PASA_ACTIVE_GP) ;
            }

            /* Estimate the global error Com->E by taking a unit step
               along the negative gradient. */
            status = pasa_gradproj_step (PASAONE, Com) ;
            if ( status > 1 ) /* an error occurred */
            {
                return (status) ;
            }

            if ( use_pproj == TRUE )
            {
                /* If the pproj did not stop in hotchol, then the
                   factorization could have changed. Hence, the factorization
                   associated with the global error estimate could be
                   different from the factorization at the current point. */
                if ( Com->ppcom->Work->stop_in_hotchol == FALSE )
                {
                    Com->factor_ok = FALSE ;
                }
            }

            if ( Com->E <= Com->grad_tol )
            {
                return (PASA_ERROR_TOLERANCE_SATISFIED) ;
            }
            /* Note: if na > 0, Com->e may be smaller than it should be
                     which delays the algorithm switch */
            if ( (GradProjOnly == FALSE) && (Com->e > Com->testtol) )
            {
                return (PASA_ACTIVE_GP) ;
            }
            its_since_test = 0 ;
            test_increment++ ;
            test_increment = PASAMIN (test_increment, Parm->testit) ;
        }

        /* update the reference value */
        fR = pasa_update_reference (Com) ;
    }

    return (PASA_ITERATIONS_EXCEED_MAXITS_IN_GRAD_PROJ) ;
}

/* ==========================================================================
   === pasa_activeGP ========================================================
   ==========================================================================
    Perform an active set version of the gradient projection algorithm
    until either "local error e < switchfactor*global error E",
    or the number of iterations since there is an addition to the number
    of active constraints exceeds Parm->terminate_agp. In the active
    set gradient projection algorithm, the variables at bounds at the
    current iterate xk, are kept fixed at their bounds in the next iteration,
    while the inequalities Bl <= Ax <= Bu that are active at xk are kept
    active when projecting onto the polyhedron.
   ========================================================================== */
int pasa_activeGP /* return:
                       PASA_GRAD_PROJ
                       PASA_CG_DESCENT
                       PASA_ERROR_TOLERANCE_SATISFIED
                       PASA_POLYHEDRON_INFEASIBLE
                       PASA_INVALID_LINEAR_CONSTRAINT_BOUNDS
                       PASA_LINE_SEARCH_STEPS_EXCEED_MAXSTEPS_IN_ACTIVEGP
                       PASA_OUT_OF_MEMORY_IN_PPROJ
                       PASA_SSOR_ASCENT_FAILURE_IN_PPROJ
                       PASA_SEARCH_DIRECTION_NOT_DESCENT_DIRECTION_IN_ACTIVEGP
                       PASA_SSOR_ITERATIONS_EXCEED_MAX_ITS_IN_PPROJ */
(
    PASAcom *Com  /* common data for PASA */
)
{
    int Aexists, na, PrintLevel, ProblemCompressed,
        status, use_pproj, use_napheap ;
    PASAFLOAT alpha, muAd, delta, deltaf, dQd, fR, fRpert,
              gtd, gtdnew, maxsteps, pert, s, safe0, safe1, stepdecay, t,
             *Ax, *d, *gnew, *x ;
    PASAINT i, iter_nochange, j, k, l, nf, p, q, row,
           *Ap, *Ai, *ifree, *order ;
    PASAparm *Parm ;
    PASAstat *Stat ;
    PPprob *Prob ;

    Parm = Com->pasaparm ;  /* parameters for PASA */
    PrintLevel = Parm->PrintLevel ;
    if ( PrintLevel >= 1 )
    {
        printf ("START activeGP, testtol: %e\n", Com->testtol) ;
    }

    /* We want to compress the problem by removing the bound variables and by
       changing the active inequality constraints to equality constraints.
       If the current factorization corresponds to the current iterate,
       then we can directly compress the problem. However, if a prior
       stepsize in grad_proj was not a unit step, or if the global error
       was estimated at the end of grad_proj, then there may be a mismatch
       between the active constraints for the current iterate and the
       current factorization.  (One way to force the factorization
       to match the current iterate is to project the current iterate
       onto the feasible set. This does not change the iterate, but the
       factorization will be updated during the projection process to match
       the active constraints of the current iterate) */
    Aexists = Com->Aexists ;
    const PASAINT nfull = Com->ncol ;
    ifree = Com->ifree ;
    order = Com->order ;
    use_pproj = Com->use_pproj ;
    use_napheap = Com->use_napheap ;

    /* depending on where we are coming from, the startup process is slightly
       different */
    if ( Com->location == PASA_CG_DESCENT ) /* coming from CG (new active) */
    {
        if ( PrintLevel >= 1 )
        {
            printf ("coming from CG_DESCENT\n") ;
        }
        ProblemCompressed = TRUE ;

        /* if the code comes from CG and an estimate for the bb parameter
           was obtained, then we use it */
        if ( Com->cg_bb_est > PASAZERO )
        {
            Com->bbk = Com->cg_bb_est ;
        }
    }
    else /* coming from either grad_proj or base code */
    {
        /* if bounds exist, then set ifree */
        if ( Com->Bounds == TRUE )
        {
            if ( (Com->fixed == TRUE) || (use_pproj == TRUE) )
            {
                /* colperm stores the indices in the user's coordinate system
                   of the unfixed variables */
                pasa_copyi (ifree, Com->colperm, nfull) ;
            }
            else
            {
                /* all the variables are free initially */
                for (j = 0; j < nfull; j++)
                {
                    ifree [j] = j ;
                }
            }
        }
        ProblemCompressed = FALSE ;
        if ( use_pproj == TRUE )
        {
            /* if we came from grad_proj, the prior iteration was
               a unit step (that is, na = 0), and the global error was
               estimated (Com->factor_ok == TRUE), then compress the
               problem. Note that we set unitstep = FALSE in the base code. */
            if ( (Com->unitstep == TRUE) && (Com->factor_ok == TRUE) )
            {
                /* In this case, the active constraints for the current iterate
                   match the factorization.  We can directly compress
                   the problem.*/
                pasa_compress_prob (FALSE, Com) ;
                ProblemCompressed = TRUE ;
            }
        }
        else /* only bound constraints or use_napheap is true */
        {
            /* if use_napheap is FALSE (i.e. bound constraints),
               or coming from grad_proj, then compress the problem,
               otherwise, no compression */
            if ( (use_napheap == FALSE) || (Com->location == PASA_GRAD_PROJ) )
            {
                pasa_compress_prob (FALSE, Com) ;
                ProblemCompressed = TRUE ;
            }
        }
    }

    /* update Com->location to the active set gradient projection algorithm */
    Com->location = PASA_ACTIVE_GP ;

    if ( use_pproj == TRUE )
    {
        /* these variables are used below when there are linear constraints */
        Prob = Com->ppcom->Prob ;
        Ap = Prob->Ap ;
        Ai = Prob->Ai ;
        Ax = Prob->Ax ;
    }
    x = Com->x ;
    d = Com->d ;
    Stat = Com->pasastat ; /* statistics for PASA */
    gnew = Com->gnew ;
    nf = Com->nf ;

    /* Evaluate starting guess for bb parameter Com->bbk */
    Com->first = TRUE ; /* first iteration */
    status = pasa_update_bb (Com) ;
    if ( status == PASA_ERROR_TOLERANCE_SATISFIED )
    {
        return (PASA_ERROR_TOLERANCE_SATISFIED) ;
    }

    /* Initialize reference value to be the current function value */
    fR = pasa_update_reference (Com) ;

    /* subsequent calls to update_bb and update_reference are handled
       differently */
    Com->first = FALSE ;

    /* A nonmonotone Armijo line search is performed. An acceptable stepsize
       alpha satisfies the condition

               fnew <= fR + alpha*deltaf,  deltaf = delta*gk'*dk

       where dk = pk - xk and pk = projection (xk - bbk*gk) onto
       the polyhedron.  Here bbk is the BB parameter and alpha has
       the starting value 1.  If the acceptance condition is not satisfied,
       then we continue to decrease alpha until the acceptance condition
       is satisfied. Due to numerical errors that arise when xnew is near x,
       we may replace the Armijo acceptance condition by the following
       approximation, which can be evaluated more accurately than the
       original version of the acceptance condition when xnew is near x:

                   gnew'*dk <= 2(fR-f)/alpha + deltag,

        where deltag = (2*delta - 1)*gk'*dk.  This is an accurate
        approximation to the Armijo condition when xnew is close to
        x, but when xnew is far from x, it could yield a poor
        approximation. Hence, we also require fnew <= fRpert,
        which is slightly larger than fR. */
    delta = Parm->Armijo_delta ;
    maxsteps = Parm->maxsteps ; /* max number of Armijo backsteps */

    /* The reduction in alpha is done by different methods in different
       situations. One method is to multiply alpha by the factor
       armijo_decay. Another method is to approximate a local minimum of
       the function in the search direction using either a quadratic
       interpolation step or a secant step, with safe guards to ensure
       that the new alpha lies in the interval [safe0, safe1]*alpha */
    stepdecay = Parm->stepdecay ;
    safe0 = Parm->safe0 ;
    safe1 = Parm->safe1 ;

    if ( PrintLevel >= 1 )
    {
        printf ("active set GP Starting function value: %e\n", Com->f) ;
    }
    na = 0 ;
    iter_nochange = 0 ;

    int const QP = Com->QP ;

    while ( iter_nochange < Parm->terminate_agp )
    {
        if ( PrintLevel >= 1 )
        {
            printf ("active GP Ref value: %e  BB step: %e\n",
                    Com->fr, Com->bbk) ;
        }

        /* Project xk - bbk*gk onto the polyhedron. The search direction
           dk = pk - xk is stored in Com->d. If A does not exist (only
           bound constraints are present), then we also compute global
           and local errors. */

        status = pasa_gradproj_step (Com->bbk, Com) ;
        if ( status > 1 )
        {
            return (status) ;
        }

        if ( Aexists == FALSE ) /* only bound constraints present */
        {
            if ( Com->E <= Com->grad_tol )
            {
                return (PASA_ERROR_TOLERANCE_SATISFIED) ;
            }
            if ( Com->e <= Com->testtol )
            {
                return (PASA_GRAD_PROJ) ;
            }
        }
        Stat->agpit++ ;

        /* to deactivate Lagrangian-based line search, change "if 1" to "if 0"*/
        muAd = PASAZERO ;
#if 1
        if ( use_pproj == TRUE )
        {
            PASAFLOAT u, v, *lambda ;
            if ( Aexists == TRUE )
            {
                lambda = Com->lambda ;
                v = PASAZERO ; /* = (lambda'*A)*d */
                for (j = 0; j < nf; j++)
                {
                    if ( d [j] != PASAZERO )
                    {
                        u = PASAZERO ; /* = (lambda'*A)_j */
                        q = Ap [j+1] ;
                        for (p = Ap [j]; p < q; p++)
                        {
                            u += lambda [Ai [p]]*Ax [p] ;
                        }
                        v += u*d [j] ;
                    }
                }
                muAd = v/Com->bbk ;
            }
        }
        else if ( use_napheap == TRUE )
        {
            muAd = *(Com->lambda)*pasa_dot (d, Com->nap_a, nf) ;
        }
#endif
        gtd = Com->gtd ;
        gtd -= muAd ;
        if ( muAd < PASAZERO )
        {
            muAd = PASAZERO ;
        }
        /* If gtd > 0, then current search direction not descent direction */
        if ( gtd > PASAZERO )
        {
            if ( PrintLevel >= 1 )
            {
                printf ("gtd = %e no descent\n", gtd) ;
            }
            return (PASA_SEARCH_DIRECTION_NOT_DESCENT_DIRECTION_IN_ACTIVEGP) ;
        }

        /* initial stepsize in line search (backtrack along search direction) */
        alpha = PASAONE ;

        /* For a quadratic objective, we use the approximate Armijo line
           search. If the unit step is accepted, then we take it.
           Also, if d'Qd <= 0 (nonconvex) or -g'd/d'Qd >= 1 (exact min > 1),
           then we use a unit step.  Otherwise, we take alpha = -g'd/d'Qd,
           the exact minimizer in the search direction. */
        if ( QP )
        {
            /* evaluate Qd = Q*d */
            pasa_evaluate (PASAZERO, &alpha, Com, "fg") ;
            /* only first nf components of d are nonzero */
            gtdnew = pasa_dot (gnew, d, nf) ;
            if ( order != NULL )
            {
                dQd = PASAZERO ;
                /* dQd = pasa_dot (d, Com->Qd, nf) but Qd is in user's coor */
                for (j = 0; j < nf; j++)
                {
                    dQd += d [j]*Com->Qd [order [j]] ;
                }
            }
            else /* no bounds, no linear constraints, no fixed variables */
            {
                dQd = pasa_dot (d, Com->Qd, nf) ;
            }
            if ( gtdnew > 2*(fR-Com->f) + (2.*delta - 1.)*gtd )
            {   /* unit step not accepted initially, check to see if the
                   exact minimizer lies on (0, 1] */
                if ( dQd > PASAZERO )
                {
                    alpha = PASAMIN (PASAONE, -Com->gtd/dQd) ;
                    /* If -Com->gtd <= 0, then d not a descent direction
                       although numerically it was ok above. In this case,
                       our best guess is alpha = 1. */
                    if ( alpha <= PASAZERO )
                    {
                        alpha = PASAONE ;
                    }
                }
                /* else nonconvex, take the default unit step */
            }
            if ( alpha < PASAONE ) /* alpha minimizes quadratic */
            {
                /* update cost */
                Com->fnew = Com->f - 0.5*(Com->gtd)*(Com->gtd)/dQd ;

                /* update xnew */
                pasa_step (Com->xnew, Com->x, d, alpha, nf) ;

                /* update gradient */
                pasa_step (Com->gtot, Com->gtot, Com->Qd, alpha, nfull) ;

                /* copy relevant part of gtot into gnew */
                if ( order != NULL )
                {
                    pasa_convert_to_pproj (gnew, Com->gtot, order, nf) ;
                }
                else /* TODO: gtot can be eliminated when no constraints */
                {
                    pasa_copyx (gnew, Com->gtot, nf) ;
                }
            }
            else /* alpha = 1 */
            {
                /* update cost and gtot (xnew and gnew updated in
                   pasa_evaluate) */
                Com->fnew = Com->f + 0.5*dQd + Com->gtd ;

                /* update total gradient */
                pasa_step (Com->gtot, Com->gtot, Com->Qd, alpha, nfull) ;
            }
        }
        /* approximate Armijo stopping condition in line search? */
        else if ( Com->approxstep == TRUE )
        {
            if ( PrintLevel >= 1 )
            {
                printf ("approx armijo step in grad proj\n") ;
            }
            /* stopping condition in the approximate Armijo line search is

                   gnew'*d <= 2(fR-f)/alpha + deltaf,

               where deltaf = (2*delta - 1)*gk'*dk. This is an accurate
               approximation to the Armijo condition when xnew is close to
               x, but when xnew is far from x, it could yield a poor
               approximation. Hence, we also require fnew <= fRpert,
               which is slightly larger than fR

               If PertRule = 1, then fRpert = fR + Parm->eps*|fR|,
               if PertRule = 0, then fRpert = fR + Parm->eps */
            if ( Parm->PertRule == 1 )
            {
                pert = Parm->pert_eps*fabs(fR) ;
            }
            else
            {
                pert = Parm->pert_eps ;
            }
            fRpert = fR + pert ;
            deltaf = (2.*delta - 1.)*gtd ;
            s = 2.*(fR - Com->f) + deltaf ; /* 2*(fR-f)/alpha + deltaf */
            for (na = 0; na < maxsteps; na++)
            {
                /* Evaluate func and grad at xnew = x + alpha*d. */
                status = pasa_evaluate (PASAZERO, &alpha, Com, "fg") ;
                if ( status == PASA_FUNCTION_NAN_OR_INF )
                {
                    return (status) ;
                }
                if ( PrintLevel >= 1 )
                {
                    printf ("    na: %i deltaf: %e fR: %e fnew: %e fold: %e\n",
                             na, deltaf, fR, Com->fnew, Com->f) ;
                }
                gtdnew = pasa_dot (gnew, d, nf) ;
                if ( (gtdnew != gtdnew) || (gtdnew <= -PASAINF) )
                {
                    gtdnew = PASAINF ;
                }
                if ( (gtdnew <= s) && (Com->fnew - alpha*muAd <= fRpert) )
                {
                    break ;
                }
                if ( gtdnew > PASAZERO ) /* newalpha computed by secant method*/
                {
                    t = gtd/(gtd - gtdnew) ;
                    if ( (t >= safe0) && (t <= safe1) )
                    {
                        alpha *= t ;
                    }
                    else
                    {
                        alpha *= stepdecay ;
                    }
                }
                else
                {
                    alpha *= stepdecay ;
                }
                /* change fR to f + fpert after the first iteration */
                s = 2.*pert/alpha + deltaf ;
                fRpert = Com->f + pert ;
            }
            /* Check whether to switch back to ordinary Armijo line search */
            if ( fabs(Com->f - Com->fnew) >= Parm->ArmijoSwitchFactor*fabs(fR) )
            {
                if ( Parm->PrintLevel >= 1 )
                {
                    printf ("-- change to ordinary Armijo line search\n") ;
                }
                Com->approxstep = FALSE ;
            }
        }
        else /* Ordinary Armijo line search */
        {
            if ( PrintLevel >= 1 )
            {
                printf ("ordinary armijo step\n") ;
            }
            /* stopping condition in the line search is
                   fnew <= fR + alpha*deltaf, where deltaf = delta*gk'*dk */
            deltaf = delta*gtd ;
            for (na = 0; na < maxsteps; na++)
            {
                /* evaluate objective at xnew = x + alpha*d */
                status = pasa_evaluate (PASAZERO, &alpha, Com, "f") ;
                if ( status == PASA_FUNCTION_NAN_OR_INF )
                {
                    return (status) ;
                }
                if ( PrintLevel >= 1 )
                {
                    printf ("    na: %i deltaf: %e fR: %e fnew: %e fold: %e\n",
                             na, deltaf, fR, Com->fnew, Com->f) ;
                }
                if ( Com->fnew <= fR + alpha*(deltaf+muAd) )
                {
                    break ;
                }
                fR = Com->f ; /* change fR to f after the first iter */
                /* Perform safeguarded quadratic interpolation step */
                t = 2.*(Com->fnew - alpha*(muAd+gtd) - Com->f) ;
                if ( t > PASAZERO )
                {
                    t = -gtd*alpha/t ;
                    /* if the minimizer of the quadratic outside the
                       interval [safe0, safe1]*alpha, then multiply alpha
                       by the decay factor */
                    if ( (t >= safe0) && (t <= safe1) )
                    {
                        alpha *= t ;
                    }
                    else
                    {
                       alpha *= stepdecay ;
                    }
                }
                else /* quadratic not convex, multiply alpha by decay factor */
                {
                    t = -gtd*alpha/t ;
                    alpha *= stepdecay ;
                }
            }
            /* Check whether to switch to approximate Armijo line search */
            if ( fabs(fR-Com->fnew) < Parm->ArmijoSwitchFactor*fabs(fR) )
            {
                if ( PrintLevel >= 1 )
                {
                    printf ("-- change to approximate Armijo line search\n") ;
                }
                Com->approxstep = TRUE ;
            }
            /* evaluate grad at xnew computed above */
            t = -PASAONE ;
            pasa_evaluate (PASAZERO, &t, Com, "g") ;
        }
        if ( PrintLevel >= 1 )
        {
            printf ("AG iter: %ld na: %i fnew: %25.15e fold: %25.15e "
                    "alpha: %e Com->E: %e\n",
                    (LONG) Stat->agpit, na, Com->fnew, Com->f, alpha, Com->E) ;
        }
        if ( na == maxsteps )
        {
            return (PASA_LINE_SEARCH_STEPS_EXCEED_MAXSTEPS_IN_ACTIVEGP) ;
        }
        Com->alpha = alpha ;
        iter_nochange++ ;

        /* update the BB parameter if necessary */
        status = pasa_update_bb (Com) ;
        if ( status == PASA_ERROR_TOLERANCE_SATISFIED )
        {
            return (status) ;
        }

        pasa_copyx (Com->x, Com->xnew, nf) ;
        pasa_copyx (Com->g, Com->gnew, nf) ;

#ifndef NDEBUG
        if ( na > 0 ) /* test error decay */
        {
            if ( Com->f != PASAZERO ) /* test relative error */
            {
                if ( (Com->fnew - Com->f)/fabs (Com->f) > Parm->debugtol )
                {
                    printf ("old cost %25.15e < new cost %25.15e\n",
                              Com->f, Com->fnew) ;
                    pasa_error (-1, __FILE__, __LINE__, "stop") ;
                }
            }
        }
        Com->f = Com->fnew ;
        pasa_check_feas (Com) ;
#endif
        Com->f = Com->fnew ;

        /* update the reference value */
        fR = pasa_update_reference (Com) ;

        if ( alpha < PASAONE ) /* no change in active set */
        {
            /* continue iteration if the problem was previously compressed */
            if ( ProblemCompressed == TRUE )
            {
                continue ;
            }

            /* If the problem has not yet been compressed, then we endeavor
               to compress it. However, we need to be careful since the
               factorization at the projected point corresponding to alpha = 1
               may not be the same as the factorization corresponding to
               a step alpha < 1. If any bounds became active, then we can
               compress those columns, and remove the associated bound
               variables from the problem. */
            else /* problem not compressed */
            {
                if ( use_napheap == TRUE )
                {
                    status = pasa_compress_prob (TRUE, Com) ;
                }
                else
                {
                    status = pasa_bound_compress (Com) ;
                    if ( status == PASA_TRY_PROJECT2 )
                    {
                        status = pasa_project2 (d, x, Com) ;
                        if ( status >= 0 ) /* an error occurred */
                        {
                            return (status) ;
                        }
                        /* there were no errors, so compress the problem */
                        status = pasa_compress_prob (TRUE, Com) ;
                    }
                }
            }
        }
        else /* alpha = 1, compress the problem */
        {
            /* when napheap is used and constraint inactive, update status */
            if ( (use_napheap == TRUE) && (Com->nap_constraint == 0) )
            {
                /* save the lambda computed in napheap */
                *(Com->lambda) = Com->nap_lambda ;
                if ( Com->nap_lambda > PASAZERO )
                {
                    Com->nap_constraint = -1 ;
                }
                else if ( Com->nap_lambda < PASAZERO )
                {
                    Com->nap_constraint = 1 ;
                }
            }
            status = pasa_compress_prob (TRUE, Com) ;
        }

        if ( (status == PASA_GRAD_PROJ) ||
             (status == PASA_ERROR_TOLERANCE_SATISFIED) )
        {
            return (status) ;
        }

        ProblemCompressed = TRUE ;

        /* if new active constraints found, then reset iter_nochange = 0 */
        if ( status == PASA_NEW_ACTIVE )
        {
            iter_nochange = 0 ;
        }
        nf = Com->nf ;
    }
    /* If alpha < 1, then no addition to the active set. The effect
       on the factorization associated with any increase in the active
       set at step alpha needs to be neutralized. */
    if ( (alpha < PASAONE) && (use_pproj == TRUE) )
    {
        int    *ib ;
        PASAINT col, ncol, ncoladd, nrowdel, ni, *ineq_row,
               *AFTp, *AFTnz,*AFTi, *Anz, *Cp, *Cnz, *F, *ir,
               *RLinkDn, *RLinkUp, *RowmodFlag, *RowmodList,
               *ColmodFlag, *ColmodList ;
        PASAFLOAT *AFTx, *lambda, *shift_l ;
        PPwork *Work ;

        Work = Com->ppcom->Work ;
        ncol = Prob->ncol ;
        ni = Prob->ni ;
        ineq_row = Prob->ineq_row ;
        Anz = Prob->Anz ;
        Ap = Prob->Ap ;
        Ax = Prob->Ax ;
        AFTp = Work->AFTp ;
        AFTi = Work->AFTi ;
        AFTnz = Work->AFTnz ;
        AFTx = Work->AFTx ;
        Cp = Work->Cp ;
        Cnz = Work->Cnz ;

        Work = Com->ppcom->Work ;
        ib = Work->ib ;
        ir = Work->ir ;
        lambda = Work->lambda ;
        RowmodFlag = Work->RowmodFlag ;
        RowmodList = Work->RowmodList ;
        ColmodFlag = Work->ColmodFlag ;
        ColmodList = Work->ColmodList ;
        /* list of free variables */
        F = Work->F ;
        /* number of free variables */
        nf = Work->nf ;
        /* Links point to active rows */
        RLinkUp = Work->RLinkUp ;
        RLinkDn = Work->RLinkDn ;
        lambda = Work->lambda ;
        shift_l = Work->shift_l ;
        ASSERT (Work->ncoladd == 0) ;
        ASSERT (Work->ncoldel == 0) ;
        ASSERT (Work->nrowadd == 0) ;
        ASSERT (Work->nrowdel == 0) ;
        ncoladd = 0 ;
        nrowdel = 0 ;
        if ( Com->Bounds == TRUE )
        {
            for (j = 0; j < ncol; j++)
            {
                /* if column was dropped, then it needs to be added */
                if ( ib [j] != 0 )
                {
                    /* add the column since the constraint is free at x [j] */
                    ASSERT (ColmodFlag [j] == EMPTY) ;
                    ib [j] = 0 ;
                    F [nf++] = j ;
                    ColmodList [ncoladd] = j ;
                    ColmodFlag [j] = ncoladd ;
                    ncoladd++ ;
                    q = Ap [j+1] ;
                    for (p = Ap [j]; p < q; p++)
                    {
                        i = Ai [p] ;
                        if ( ir [i] <= ni )
                        {
                            l = AFTp [i] + AFTnz [i]++ ;
                            AFTx [l] = Ax [p] ;
                            AFTi [l] = j ;
                        }
                    }
                }
            }
        }
        for (k = 1; k <= ni; k++)
        {
            /* check if previously dropped inequality is now active */
            row = ineq_row [k] ;
            if ( ir [row] <= ni ) /* row active, drop it */
            {
                ASSERT (RowmodFlag [row] == EMPTY) ;
#ifndef NDEBUG
                /* since we do not resymbol (remove any zeros that arise
                   in the factorization when we delete rows, we initialize
                   the components of lambda_pen, if it exists, to 0 to avoid
                   valgrind errors that would otherwise arise when the zeros
                   in the factorization are multiplied by undefined variables */
                if ( Com->use_penalty == TRUE )
                {
                    Com->lambda_pen [row] = PASAZERO ;
                }
#endif
                RowmodList [nrowdel] = row ;
                RowmodFlag [row] = nrowdel ;
                nrowdel++ ;
                /* remove row from the active row links */
                i = RLinkDn [row] ;
                j = RLinkUp [i] = RLinkUp [row] ;
                RLinkDn [j] = i ;
            }
            ir [row] = k + ni ;
            if ( Work->shiftl_is_zero )
            {
               lambda [row] = PPZERO ;
            }
            else
            {
               lambda [row] = -shift_l [row] ;
            }
        }
        Work->nrowdel = nrowdel ;
        Work->ncoladd = ncoladd ;
        Work->nf = nf ;
        if ( PrintLevel > 0 )
        {
            printf ("exit activeGP, restore factorization:\n") ;
            printf ("nrowdel: %ld ncoladd: %ld\n",
                   (LONG) nrowdel, (LONG) ncoladd) ;
            if ( PrintLevel > 1 )
            {
                printf ("\nadd columns:\n") ;
                for (i = 0; i < ncoladd; i++)
                {
                    printf ("%ld\n", (LONG) ColmodList [i]) ;
                }
                printf ("\ndelete rows:\n") ;
                for (i = 0; i < nrowdel; i++)
                {
                    printf ("%ld\n", (LONG) RowmodList [i]) ;
                }
            }
        }
#ifndef NOPPROJ
        if  ( nrowdel > 0 )
        {
            Com->ppcom->Parm = Com->pprojparm ;
            pproj_modrow (Com->ppcom, 0, TRUE, FALSE, -1, NULL, NULL,NULL,NULL);
            pproj_updateAnz (Com->ppcom, 0) ;
        }
        if ( ncoladd > 0 )
        {
            l = 0 ;
            for (k = 0; k < ncoladd; k++)
            {
                j = ColmodList [k] ;
                if ( Anz [j] > 0 )
                {
                    Cp [l] = Ap [j] ;
                    Cnz [l] = Anz [j] ;
                    l++ ;
                }
                else /* update the list of addition columns by removing j */
                {
                    ncoladd-- ;                         /* one less update */
                    col = ColmodList [ncoladd] ;        /* last col in list*/
                    ColmodList [k] = col ;              /* replace j by col*/
                    ColmodFlag [col] = k ;              /* new col location */
                    Work->ncoladd = ncoladd ;           /* store it in W */
                    k-- ;                               /* adjust k */
                    ColmodFlag [j] = EMPTY ;            /* col j is done */
                }
            }
            Com->ppcom->Parm = Com->pprojparm ;
            pproj_modcol (Com->ppcom, 0, 0, +1, NULL, Cp, Cnz, NULL, NULL, l) ;
        }
#endif
    }
    /* if the convergence tolerance not satisfied and no new constraints
       are activated, then go to cg_descent */
    return (PASA_CG_DESCENT) ;
}

/* ==========================================================================
   === pasa_gradprojLP ======================================================
   ==========================================================================
    If gradprojLP is called fromm activeLP, then perform one iteration
    of the gradient projection algorithm with a unit step and return to
    pasa_activeLP. Otherwise, perform the gradient projection algorithm
    until the objective value does not improve strictly.
   ========================================================================== */
int pasa_gradprojLP /* return:
                       PASA_ACTIVE_LP
                       PASA_SSOR_ASCENT_FAILURE_IN_PPROJ
                       PASA_SSOR_ITERATIONS_EXCEED_MAX_ITS_IN_PPROJ */
(
    PASAcom *Com  /* common data for PASA */
)
{
    int exponent, status, blks, blk ;
    PASAINT Annz, ATnz, col, iter, i, j, k, l, m, nf, Ll, Ul, Rl,
            nactive, nrowadd, ncoladd, nrowdel, ncoldel, prevblkL, prevblkU,
            p, p0, Q0, Q1, shii, nbrk ;
    PASAFLOAT err, berr, cerr, cj, oj, lmax, xmax, fd, fn, sd, st, s, t,
              scale_l, scale_x, scale_l_inc, scale_x_inc,
              epsilon, epsilon_new, epsilon_inc, *singc ;
    PPwork   *Work ;      /* work structure in PPROJ */
    PPprob   *Prob ;      /* problem structure in PPROJ */
    PASAparm *pasaparm ;  /* parameter structure for PASA */
    PPparm   *pprojparm ; /* parameter structure for PPROJ */
    PPcom    *ppcom ;     /* com structure for pproj */

    pprojparm = Com->pprojparm ;
    pasaparm  = Com->pasaparm ;

    int const PrintLevel = pasaparm->PrintLevel ;
    if ( PrintLevel >= 1 )
    {
        printf ("START gradprojLP\n") ;
    }

    /* set the new location */
    Com->location = PASA_GRADPROJ_LP ;

    /* extract data from Com */
    PPdata               *ppdata = Com->pasadata->ppdata ; /* PPROJ input data*/
    int       const     loExists = Com->loExists ;
    int       const     hiExists = Com->hiExists ;
    PASAINT   const         ncol = Com->ncol ;
    PASAINT   const         nrow = Com->nrow ;
    PASAFLOAT const     grad_tol = Com->grad_tol ;
    PASAFLOAT                 *x = Com->x ;
    PASAFLOAT                 *c = Com->c ;
    PASAFLOAT                 *b = Com->b ;
    PASAFLOAT                *lo = Com->lo ;
    PASAFLOAT                *hi = Com->hi ;
    PASAFLOAT                *bl = Com->bl ;
    PASAFLOAT                *bu = Com->bu ;
    PASAFLOAT            *lambda = Com->lambda ;

    /* column singletons */
    PASAINT   const           ni = ppdata->ni ;
    PASAINT   const        nsing = ppdata->nsing ;
    PASAINT   const      nsingni = nsing + ni ;
    PASAINT   const *userRowSing = ppdata->row_sing ;
    PASAINT   const     nsingni1 = nsingni + 1 ;
    PASAINT   const     nsingni2 = nsingni + 2 ;
    PASAINT   const         ntot = nsingni1 + ncol ;
    PASAINT   const      gpmaxit = pasaparm->gpmaxit ;

    epsilon = pasaparm->epsilon ; /* proximal parameter */
    if ( epsilon == PASAZERO )
    {
        if      ( nrow <  100 )  epsilon = ldexp (1, -6) ;
        else if ( nrow < 2500 )  epsilon = ldexp (1, -3) ;
        else                     epsilon = PASAONE ;
    }

    PASAFLOAT const epsilon_grow  = pasaparm->EpsilonGrow ;
    PASAFLOAT const epsilon_decay = pasaparm->EpsilonDecay ;

    err = PASAINF ;
    iter = 0 ;
    scale_x = Com->scale_x ;
    scale_l = Com->scale_l ;

    /* scale c by scale_l */
    if ( scale_l != PASAONE )
    {
        pasa_scale (c, c, scale_l, ncol) ;
        if ( nsing )
        {
            pasa_scale (ppdata->singc, ppdata->singc, scale_l, nsing) ;
        }
        if ( pprojparm->start_guess == 1 )
        {
            pasa_scale (lambda, lambda, scale_l, nrow) ;
        }
    }

    /* scale the polyhedron by scale_x*epsilon */
    t = scale_x*epsilon ;
    if ( t != PASAONE )
    {
        pasa_scale (bl, bl, t, nrow) ;
        if ( ni ) /* if no strict inequalities, bl = bu */
        {
            pasa_scale (bu, bu, t, nrow) ;
        }
        if ( loExists )
        {
            pasa_scale (lo, lo, t, ncol) ;
        }
        if ( hiExists )
        {
            pasa_scale (hi, hi, t, ncol) ;
        }
        if ( nsing )
        {
            pasa_scale (ppdata->singlo, ppdata->singlo, t, nsing) ;
            pasa_scale (ppdata->singhi, ppdata->singhi, t, nsing) ;
        }
    }

    PASAFLOAT *y = Com->userg ;
    pasa_scale (y, c, -PASAONE, ncol) ; /* starting x = 0 */

    ppdata->y = y ;
    ppdata->x = x ;

    /* set some pproj parameters */
    pprojparm->getfactor        = FALSE ;
    pprojparm->use_prior_data   = FALSE ; /* after iteration 1, change to TRUE*/
    pprojparm->return_data      = TRUE ;
    pprojparm->permute          = FALSE ;
    pprojparm->use_startup      = TRUE ;
    pprojparm->LP               = TRUE ;
    /* for the initial projection, the start guess is based on the pasaparm
       parameter use_lambda */
    if ( pasaparm->use_lambda == TRUE )
    {
        pprojparm->start_guess = 1 ;
    }
    else
    {
        pprojparm->start_guess = 0 ;
    }

    /* call pproj, reorder equation and variables, perform phase1 */
    status = pproj (ppdata) ;
    Com->pasastat->nproject++ ;
    ppcom = Com->ppcom = ppdata->priordata ;

    /* solution is stored in ppdata->x = Com->x */

    if ( (status != PPROJ_SOLUTION_FOUND) &&
         (status != PPROJ_ERROR_DECAY_STAGNATES) ) /* error occurred */
    {
        Com->f = PASAINF ;
        return (status) ;
    }


    pprojparm->start_guess    = 3 ;      /* use input lambda for start guess */
    pprojparm->use_prior_data = TRUE ;
    pprojparm->use_startup    = FALSE ;  /* go straight to pproj_dasa */

    /* henceforth, store pproj solution in ppdata->x = Com->d = d */
    PASAFLOAT *dx = ppdata->x = Com->d ;

    /* switch storage format to that of pproj since a permutation was done */
    Prob = ppcom->Prob ;

    /* -c was stored in y, so switch sign to get reordered c */
    pasa_scale (c, Prob->y, -PASAONE, ncol) ;

                     b = Prob->b ;
    if ( loExists ) lo = Prob->lo ;
    if ( hiExists ) hi = Prob->hi ;

    singc  = Prob->singc ;
    bl = (nsing) ? Prob->singlo : Prob->bl ;
    bu = (nsing) ? Prob->singhi : Prob->bu ;

    PASAINT   const         *Ap = Prob->Ap ;
    PASAINT   const         *Ai = Prob->Ai ;
    PASAINT   const        *Anz = Prob->Anz ;
    PASAFLOAT const         *Ax = Prob->Ax ;
    PASAINT   const   *ineq_row = Prob->ineq_row ;
    PASAINT   const   *row_sing = Prob->row_sing ;
    PASAINT   const  *row_sing1 = row_sing+1 ;

    Work = ppcom->Work ;
    PASAINT                 *ir = Work->ir ;
    int                     *ib = Work->ib ;
    PASAINT                  *F = Work->F ;
    PASAINT                 *ns = Work->ns ;
    PASAINT   const        *ATp = Work->ATp ;
    PASAINT   const        *ATi = Work->ATi ;
    PPFLOAT   const        *ATx = Work->ATx ;

    PASAFLOAT           *arrayd = Work->arrayd ;
    PASAFLOAT          *dlambda = arrayd ;  arrayd += nrow ;
    /* g is also used for absAx */
    PASAFLOAT                *g = arrayd ;  arrayd += nrow ;
    PASAFLOAT                *r = arrayd ;  arrayd += nrow ;
    PASAFLOAT               *pA = arrayd ;  arrayd += ncol ;
    PASAFLOAT         *Br_value = arrayd ;  arrayd += ntot ;

    PASAFLOAT               *pC = Com->userg ; /* = ppdata->y */
    PASAINT                *slo = Work->slo ;
    PASAINT                *shi = Work->shi ;

    PASAINT             *arrayi = Work->arrayi ;
    PASAINT               *Heap = arrayi ;  arrayi += ntot + 1 ;

    /* AFT is the transpose of the AF matrix, the submatrix of A associated
       with the free variables */
    PASAINT   const       *AFTp = Work->AFTp ;
    PASAINT              *AFTnz = Work->AFTnz ;
    PASAINT               *AFTi = Work->AFTi ;
    PASAFLOAT             *AFTx = Work->AFTx ;

    PASAINT            *RLinkUp = Work->RLinkUp ;
    PASAINT            *RLinkDn = Work->RLinkDn ;
          /* numbering of strict inequalities (also referred to
             as column singletons) starts at 1 instead of 0 */
    int       const *sol_to_blk = Work->sol_to_blk ;
    PASAINT            *lLinkUp = Work->SLinkUp ;
    PASAINT            *lLinkDn = Work->SLinkDn ;
    PASAINT            *uLinkUp = Work->SLinkUp ;
    PASAINT            *uLinkDn = Work->SLinkDn ;
    PASAINT             *lstart = Work->lstart ;
    PASAINT             *ustart = Work->ustart ;
    PASAINT         *RowmodFlag = Work->RowmodFlag ;
    PASAINT         *RowmodList = Work->RowmodList ;
    PASAINT         *ColmodFlag = Work->ColmodFlag ;
    PASAINT         *ColmodList = Work->ColmodList ;
    PASAFLOAT          *shift_l = Work->shift_l ;
    PASAFLOAT const       sigma = Work->sigma ;

    PASAFLOAT               *Wb = Work->b ;
    PASAFLOAT               *Wc = Work->c ;
    PASAFLOAT               *WD = Work->D ;
    PASAFLOAT              *Wlo = Work->lo ;
    PASAFLOAT              *Whi = Work->hi ;
#ifndef NDEBUG
    /* if the debug mode is used, then create an array for the right side */
    PASAFLOAT *checkb, *checklo, *checkhi ;
    checkb = ppcom->Check->b =
                 (PASAFLOAT *) pasa_malloc (&status, nrow, sizeof (PASAFLOAT)) ;
    if ( loExists ) checklo = ppcom->Check->lo =
                 (PASAFLOAT *) pasa_malloc (&status, ncol, sizeof (PASAFLOAT)) ;
    if ( hiExists ) checkhi = ppcom->Check->hi =
                 (PASAFLOAT *) pasa_malloc (&status, ncol, sizeof (PASAFLOAT)) ;
    if ( status == PASA_OUT_OF_MEMORY ) return (status) ;
#endif

    while ( (err > grad_tol) && (iter < gpmaxit) )
    {
        iter++ ;
        /* If iter > 1, then call pproj. Otherwise, pproj was called above. */
        if ( iter > 1 )
        {
            status = pproj (ppdata) ;
            Com->pasastat->nproject++ ;

            /* this is normally done in pproj_invert, but since we skip
               the inversion, we do it here */
            if ( pprojparm->cholmod == TRUE )
            {
                pproj_updateAnz (ppcom, 2) ;
                /*also sets AFTnz = 0 for dead rows*/
            }

            /* update x with the solution in d = ppdata->x */
            pasa_step (x, x, dx, PASAONE, ncol) ;

            if ( (status != PPROJ_SOLUTION_FOUND) &&
                 (status != PPROJ_ERROR_DECAY_STAGNATES) ) /* error occurred */
            {
                return (status) ;
            }
        }

        /* Instead of projecting xk - step*ck onto the polyhedron as in
           the usual gradient projection step, the polyhedron is scaled
           by epsilon = 1/step. Also, a scale factor scale_x is applied
           to the polyhedron. This factor is chosen in an effort to achieve
           a solution whose sup-norm is one. The variables scale_x and
           epsilon contain the cummulative scale values, however, in each
           iteration, we only apply the increments to scale_x and
           epsilon determined in the previous iteration. */

        if ( PrintLevel >= 1 )
        {
            printf ("iter: %ld step: %e scale_x: %e scale_l: %e\n",
                   (LONG) iter, 1./epsilon, scale_x, scale_l) ;
        }
#ifndef NDEBUG
        pasa_errLP (epsilon, Com) ;
#endif

        /* Determine the scale factor for lambda and x and see whether the
           stopping criterion is satisfied. Both lmax and xmax should be
           on the order of one if the scaling is working. */
        lmax = Work->norm_l ;
        if ( lmax == PASAZERO ) lmax = PASAONE ;

        xmax = pasa_sup_norm (x, ncol)/epsilon ;
        if ( xmax == PASAZERO ) xmax = PASAONE ;
        /* Work->absAxk = xmax ;*/

        cerr = Work->cerr/lmax ;
        berr = Work->errdual ;
        t = berr/(xmax*epsilon) ;
        err = PASAMAX (cerr, t) ;
        if ( PrintLevel >= 1 )
        {
            printf ("err: %e cerr: %e berr: %e xmax: %e lmax: %e\n",
                    err, cerr, berr, xmax, lmax) ;
        }
        if ( (err <= grad_tol) || (iter >= gpmaxit) ) break ;

        /* Determine the adjustments needed for the x and lambda scaling */
        t = frexp (lmax, &exponent) ;        /* exponent for lambda */
        scale_l_inc = ldexp (1, -exponent) ; /* lambda normalization factor */
        scale_l *= scale_l_inc ;             /* cummulative lambda scale */

        t = frexp (xmax, &exponent) ;        /* exponent for x */
        scale_x_inc = ldexp (1, -exponent) ; /* x normalization factor */
        scale_x *= scale_x_inc ;             /* cummulative x scale */

        if ( PrintLevel )
        {
            printf ("scale_x_inc: %e scale_x: %e scale_l_inc: %e scale_l: %e\n",
                     scale_x_inc,    scale_x,    scale_l_inc,    scale_l) ;
        }

        /* Determine the adjustments to epsilon. If cerr is small
           enough relative to either grad_tol or berr, then increase epsilon;
           otherwise, epsilon decreases. After determining these adjustments,
           we evaluate the change that these adjustments to epsilon and the
           scaling of x and lambda have on the solution to the dual problem,
           assuming that the constraints active at the current lambda remain
           active after the change in epsilon and the update to x. Since this
           assumption may not be valid, a line search is performed in this
           search direction using the new value for epsilon.
           After the line search, x and lambda are scaled by the scales
           computed above. */
        if ( cerr <= grad_tol*pasaparm->cerr_decay )
        {
            epsilon_inc = epsilon_grow ;
        }
        else /* epsilon decays */
        {
            epsilon_inc = epsilon_decay ;
        }
        /* The rationale for the factor scale_x/scale_l below is the
           following: We want to both rescale the problem and multiply
           epsilon by some factor. If we multiply epsilon by this scale
           ratio, then the minimizer of the proximal function with this
           new epsilon is the same as the minimizer of the rescaled
           function with the new epsilon. */
        epsilon_inc *= (scale_x_inc/scale_l_inc) ;
        epsilon_new = epsilon*epsilon_inc ;
        if ( PrintLevel )
        {
            printf ("epsilon: %e epsilon_inc: %e epsilon_new %e\n",
            epsilon, epsilon_inc, epsilon_new) ;
        }

        /* If cholmod is being used and the matrix has been factored,
           then evaluate the adjustment in lambda due to changing epsilon to
           epsilon_new and due to the update of x (x = x + d done above).
           Without the scaling of variable used in lpdasa, the change in
           the right side is given by:

              (eps_new - eps_old)b + AF(eps_old yF_old - eps_new yF_new)
                                   + AB(eps_old xB_old - eps_new xB_new)

           After the first iteration, yF_new = xF/eps_old and yF_old =
           (xF - dF)/eps_old due to the scaling by eps. We assume that
           xB_old = xB_new = code's xB/epsilon.  In the first iteration,
           y_old = 0, yF_new = xF/eps_old, and again
           xB_old = xB_new = code's xB/epsilon.  Also note that b
           in this formula for the right side is the code's b/eps_old.
           Define deps = (eps_new - eps_old)/eps_old. After substituting
           in terms of the code's variables, the right side change after
           the first iteration is

              deps*b - AF*(deps*xF_new + dF) - deps*AB*xB_new

           For the first iteration, let us define rel_eps = eps_new/eps_old.
           The right side change is

              deps*b - rel_eps*AF*xF_new - deps*AB*xB_new) */

#ifndef NDEBUG
        if ( Work->fac == FALSE )
        {
            printf ("warning: Work->fac is FALSE after pproj\n") ;
        }
#endif
        PASAINT const cholmod = pprojparm->cholmod ;
        if ( cholmod )
        {
            nrowadd = Work->nrowadd ;
            ncoladd = Work->ncoladd ;
            nrowdel = Work->nrowdel ;
            ncoldel = Work->ncoldel ;
        }

        if ( cholmod && (Work->fac == TRUE) && (RLinkUp [nrow] < nrow) )
        {
            PASAFLOAT const deps = epsilon_inc - PASAONE ;
            pasa_initx (dlambda, PASAZERO, nrow) ;

            /* build right side change in dlambda */
            if ( nsingni )
            {
                if ( ni )
                {
                    for (i = RLinkUp [nrow]; i < nrow; i = RLinkUp [i])
                    {
                        j = ir [i] ;
                        ASSERT (j <= ni) ;
                        if ( j == 0 )
                        {
                            t = b [i] ;
                        }
                        else if ( j > 0 ) /* for active rows j <= ni */
                        {
                            t = bu [j] ;
                        }
                        else /* j < 0 */
                        {
                            t = bl [-j] ;
                        }
                        dlambda [i] = deps*t ;
                        r [i] = epsilon_inc*t - sigma*lambda [i] ;
                    }
                }
                else /* nsing */
                {
                    for (i = RLinkUp [nrow]; i < nrow; i = RLinkUp [i])
                    {
                        ASSERT (ir [i] <= nsingni) ;
                        t = PASAZERO ;
                        if ( ir [i] ) /* there are bound singletons in row */
                        {
                            PASAINT const qlo = slo [i] ;
                            j = row_sing [i] ;
                            if ( qlo )
                            {
                                for (; j <= qlo; j++)
                                {
                                    t += bl [j] ;
                                }
                            }
                            PASAINT const qhi = row_sing1 [i] ;
                            for (; j < qhi; j++)
                            {
                                t += bu [j] ;
                            }
                        }
                        t += b [i] ;
                        dlambda [i] = deps*t ;
                        r [i] = epsilon_inc*t - sigma*lambda [i] ;
                    }
                }
            }
            else
            {
                /* pure equality constraints:
                       dlambda = eps*b
                       r       = epsilon_inc*b - sigma*lambda */
                
                pasa_scale (dlambda, b, deps,         nrow) ;
                pasa_scale (r,       b, epsilon_inc, nrow) ;
                pproj_saxpy(r, lambda, -sigma, nrow) ;
            }
            if ( iter > 1 )
            {
                for (j = 0; j < ncol; j++)
                {
                    PASAFLOAT const tx = (ib [j]) ?
                                                 /* bound : free */
                                              deps*x [j] : deps*x [j] + dx [j] ;
                    k = Ap [j] ;
                    const PASAINT q = k + Anz [j] ;
                    for (; k < q; k++)
                    {
                        dlambda [Ai [k]] -= tx*Ax [k] ;
                    }
                }
            }
            else /* iter = 1 */
            {
                for (j = 0; j < ncol; j++)
                {
                    PASAFLOAT const tx = (ib [j]) ?
                                                 /* bound : free */
                                               deps*x [j] : epsilon_inc*x [j] ;
                    k = Ap [j] ;
                    PASAINT const q = k + Anz [j] ;
                    for (; k < q; k++)
                    {
                        dlambda [Ai [k]] -= tx*Ax [k] ;
                    }
                }
            }
            pproj_lsol (Work->L, dlambda, RLinkUp [nrow], nrow, RLinkUp) ;
            i = RLinkUp [nrow] ;
            /* momentarily set the initial RLinkDn to -1, this simplifies
               indexing in dltsolve */
            RLinkDn [i] = -1 ;
            j = RLinkDn [nrow] ;
            pproj_dltsol (Work->L, dlambda, dlambda, j, i, RLinkDn) ;
            /* dlambda stores the change in the solution to the dual problem
               associated with the change in epsilon to epsilon_new and the
               update of x (x = x + d), assuming there is no change in the
               active constraints. Next, a line search is performed in
               the direction of dlambda, starting from the current solution
               of the dual problem. */
            RLinkDn [i] = nrow ; /* restore RLinkDn */

            /* scale the variables since the line search should be based
               on the new epsilon */
            if ( epsilon_inc != PASAONE )
            {
                pasa_scale (x , x , epsilon_inc, ncol) ;
                pasa_scale (b , b , epsilon_inc, nrow) ;
                if ( loExists ) pasa_scale (lo, lo, epsilon_inc, ncol) ;
                if ( hiExists ) pasa_scale (hi, hi, epsilon_inc, ncol) ;
                if ( nsingni )
                {
                    pasa_scale (bl+1, bl+1, epsilon_inc, nsingni) ;
                    pasa_scale (bu+1, bu+1, epsilon_inc, nsingni) ;
                }
            }

            pasa_step (pC, c, x, -PASAONE, ncol) ;
            pasa_initx (pA, PASAZERO, ncol) ;

            p = 0 ;
            for (i = 0; i < nrow; i++)
            {
                const PASAFLOAT lambdai = lambda [i] ;
                const PASAINT q = ATp [i+1] ;
                const PASAINT iri = ir [i] ;
                if ( iri <= nsingni ) /* active inequality */
                {
                    g [i] = PASAZERO ;
                    const PASAFLOAT dlambdai = dlambda [i] ;
                    for (; p < q; p++)
                    {
                        const PASAINT colj = ATi [p] ;
                        const PASAFLOAT ax = ATx [p] ;
                        pC [colj] -= ax* lambdai ;
                        pA [colj] += ax*dlambdai ;
                    }
                }
                else
                {
                    for (; p < q; p++)
                    {
                        pC [ATi [p]] -= ATx [p]*lambdai ;
                    }
                }
            }

            nbrk = 0 ;
            sd = PASAZERO ;
            for (j = 0; j < ncol; j++)
            {
                p = Ap [j] ;
                const PASAINT q = p + Anz [j] ;
                const PASAFLOAT pAj = pA [j] ;
                if ( ib [j] < 0 )      /* at lower bound */
                {
                    if ( q > p )
                    {
                        const PASAFLOAT loj = lo [j] ;
                        for (; p < q; p++)
                        {
                            r [Ai [p]] -= loj*Ax [p] ;
                        }
                        if ( pAj > PASAZERO )
                        {
                            Br_value [j] = (loj + pC [j])/pAj ;
                            nbrk++ ;
                            Heap [nbrk] = j ;
                        }
                    }
                }
                else if ( ib [j] > 0 ) /* at upper bound */
                {
                    if ( q > p )
                    {
                        const PASAFLOAT hij = hi [j] ;
                        for (; p < q; p++)
                        {
                            r [Ai [p]] -= hij*Ax [p] ;
                        }
                        if ( pAj < PASAZERO )
                        {
                            Br_value [j] = (hij + pC [j])/pAj ;
                            nbrk++ ;
                            Heap [nbrk] = j ;
                        }
                    }
                }
                else
                {
                    sd += pAj*pAj ;
                    const PASAFLOAT pCj = pC [j] ;
                    for (; p < q; p++)
                    {
                        g [Ai [p]] += Ax [p]*pCj ;
                    }
                }
            }

            if ( Work->shiftl_is_zero )
            {
                for (j = lLinkUp [nsingni1]; j <= nsingni; j = lLinkUp [j])
                {
                    PASAINT const row = ineq_row [j] ;
                    if ( (t = dlambda [row]) < PASAZERO )
                    {
                        /* lambda > 0 for lLinkUp */
                        if ( ni ) s = -lambda [row]/t ;
                        else      s = (singc [j] - lambda [row])/t ;

                        k = j + ncol ;
                        Br_value [k] = s ;
                        nbrk++ ;
                        Heap [nbrk] = k ;
                    }
                }

                for (j = uLinkUp [nsingni2]; j <= nsingni; j = uLinkUp [j])
                {
                    PASAINT const row = ineq_row [j] ;
                    if ( (t = dlambda [row]) > PASAZERO )
                    {
                        /* lambda < 0 for uLinkUp */
                        if ( ni ) s = -lambda [row]/t ;
                        else      s = (singc [j] - lambda [row])/t ;
                        k = j + ncol ;
                        Br_value [k] = s ;
                        nbrk++ ;
                        Heap [nbrk] = k ;
                    }
                }
            }
            else /* shift_l is nonzero */
            {
                for (j = lLinkUp [nsingni1]; j <= nsingni; j = lLinkUp [j])
                {
                    PASAINT const row = ineq_row [j] ;
                    if ( (t = dlambda [row]) < PASAZERO )
                    {
                        /* lambda > 0 for lLinkUp */
                        if ( ni ) s = -(lambda [row]+shift_l [row])/t ;
                        else      s = (singc[j]-(lambda [row]+shift_l [row]))/t;
                        k = j + ncol ;
                        Br_value [k] = s ;
                        nbrk++ ;
                        Heap [nbrk] = k ;
                    }
                }

                for (j = uLinkUp [nsingni2]; j <= nsingni; j = uLinkUp [j])
                {
                    PASAINT const row = ineq_row [j] ;
                    if ( (t = dlambda [row]) > PASAZERO )
                    {
                        /* lambda < 0 for uLinkUp */
                        if ( ni ) s = -(lambda [row]+shift_l [row])/t ;
                        else      s = (singc[j]-(lambda [row]+shift_l [row]))/t;
                        k = j + ncol ;
                        Br_value [k] = s ;
                        nbrk++ ;
                        Heap [nbrk] = k ;
                    }
                }
            }

            fd = t = PASAZERO ;                   /* 1st and 2nd derivatives */
            for (i = RLinkUp [nrow]; i < nrow; i = RLinkUp [i])
            {
                PASAFLOAT const dlambdai = dlambda [i] ;
                t += dlambdai*dlambdai ;         /* d is search direction*/
                fd += dlambdai*(r [i] + g [i]) ; /* g + r is current gradient */
            }
            sd += t*sigma ;

            nf = Work->nf ;
            st = PASAZERO ;
            PASAINT const nbrk0 = nbrk ;
            if ( (nbrk > 0) && (fd > PASAZERO) )
            {
                pproj_minheap_build (Heap, Br_value, nbrk) ;
                for (k = 1; k <= nbrk; k++)
                {
                    ns [Heap [k]] = k ;
                }
                while ( nbrk > 0 )
                {
                    if ( sd > PPZERO )
                    {
                        col = Heap [1] ;
                        PASAFLOAT const Br = PASAMAX (Br_value [col], PASAZERO);
                        pproj_minheap_delete (Heap, ns, Br_value, &nbrk, 1) ;
                        fn = fd - sd * (Br - st) ;
                        if ( PrintLevel > 2 )
                        {
                            printf("    brk: %ld col: %ld fn: %9.3e fd: %9.3e"
                                   " sd: %9.3e st: %9.3e Br_value: %9.3e\n",
                                  (LONG) nbrk, (LONG) col, fn, fd, sd, st, Br) ;
                        }

                        if ( fn <= PPZERO )
                        {
                            if ( fd != fn )
                            {
                                st += (fd/(fd-fn))*(Br - st);
                            }
                            fd = PPZERO ;
                            break ;
                        }
                        else
                        {
                            fd = fn ;
                            st = Br ;
                        }
                    }
                    else
                    {
                        break ;
                    }

                    if ( col < ncol ) /* free column */
                    {
                        F [nf++] = col ;
                        sd += pA [col]*pA [col] ;
                        if ( ib [col] < 0 )
                        {
                            t = lo [col] ;
                        }
                        else
                        {
                            t = hi [col] ;
                        }
                        PASAFLOAT const bndcol = t ;
                        ib [col] = 0 ;
                        if ( cholmod )
                        {
                            ASSERT (ColmodFlag [col] == EMPTY) ;
                            ColmodList [ncoladd] = col ;
                            ColmodFlag [col] = ncoladd ;
                            ncoladd++ ;
                        }
                        p = Ap [col] ;
                        PASAINT const q = p + Anz [col] ;
                        for (; p < q; p++)
                        {
                            i = Ai [p] ;
                            /* if the row is active, then add to AFT matrix */
                            if ( ir [i] <= nsingni )
                            {
                                PASAINT   const ki = AFTp [i] + AFTnz [i]++ ;
                                PASAFLOAT const ax = Ax [p] ;
                                AFTx [ki] = ax ;
                                r [i] += bndcol*ax ;
                                AFTi [ki] = col ;
                            }
                        }
                    }
                    else              /* drop row */
                    {
                        PASAINT const sing = col - ncol ;
                        PASAINT const row = ineq_row [sing] ;
                        Work->nactive-- ;
                        ASSERT ((ir [row] != 0) && (ir [row] <= nsingni)) ;
                        /* row dropped due to singleton sing */
                        ir [row] = sing + nsingni ;
                        i = RLinkDn [row] ;
                        k = RLinkUp [row] ;
                        RLinkUp [i] = k ;
                        RLinkDn [k] = i ;
                        if ( cholmod )
                        {
                            ASSERT (RowmodFlag [row] == EMPTY) ;
                            RowmodList [nrowdel] = row ;
                            RowmodFlag [row] = nrowdel ;
                            nrowdel++ ;
                        }
                        if ( Work->shiftl_is_zero )
                        {
                            if ( ni )
                            {
                                t = -lambda [row] ;
                                lambda [row] = PASAZERO ;
                            }
                            else /* nsing */
                            {
                                t = singc [sing] - lambda [row] ;
                                lambda [row] = singc [sing] ;
                            }
                        }
                        else
                        {
                            if ( ni )
                            {
                                t = -(lambda [row]+shift_l [row]) ;
                                lambda [row] = -shift_l [row] ;
                            }
                            else
                            {
                                t = singc [sing] - (lambda [row]+shift_l [row]);
                                lambda [row] = singc [sing] ;
                            }
                        }
                        PASAFLOAT const dlnew = t ;
                        PASAFLOAT const dlambda_row = dlambda [row] ;
                        fd -= dlambda_row*r [row] ;
                        sd -= sigma*dlambda_row*dlambda_row ;
                        if ( dlambda_row > PPZERO ) /* drop upper bound bu */
                        {
                            m = uLinkUp [sing] ;
                            l = uLinkDn [sing] ;
                            uLinkUp [l] = m ;
                            uLinkDn [m] = l ;
                            if ( cholmod )
                            {
                                blk = sol_to_blk [sing] ;
                                if ( sing == ustart [blk] )
                                {
                                    ustart [blk] = m ;
                                }
                                if ( nsing )
                                {
                                    shi [row] = 0 ;
                                    j = slo [row] ;
                                    if ( j )
                                    {
                                        m = lLinkUp [j] ;
                                        l = lLinkDn [j] ;
                                        lLinkUp [l] = m ;
                                        lLinkDn [m] = l ;
                                        slo [row] = 0 ;
                                        if ( j == lstart [blk] )
                                        {
                                            lstart [blk] = m ;
                                        }
                                    }
                                }
                            }
                        }
                        else /* drop lower bound bl */
                        {
                            m = lLinkUp [sing] ;
                            l = lLinkDn [sing] ;
                            lLinkUp [l] = m ;
                            lLinkDn [m] = l ;
                            if ( cholmod )
                            {
                                blk = sol_to_blk [sing] ;
                                if ( sing == lstart [blk] )
                                {
                                    lstart [blk] = m ;
                                }
                                if ( nsing )
                                {
                                    slo [row] = 0 ;
                                    j = shi [row] ;
                                    if ( j )
                                    {
                                        m = uLinkUp [j] ;
                                        l = uLinkDn [j] ;
                                        uLinkUp [l] = m ;
                                        uLinkDn [m] = l ;
                                        shi [row] = 0 ;
                                        if ( j == ustart [blk] )
                                        {
                                            ustart [blk] = m ;
                                        }
                                    }
                                }
                            }
                        }
                        PASAINT const q = ATp [row+1] ;
                        for (p = ATp [row]; p < q; p++)
                        {
                            PASAINT const colj = ATi [p] ;
                            PASAFLOAT const ax = ATx [p] ;
                            PASAFLOAT const pAj = pA [colj] ;
                            /* Cj = c after taking the step */
                            PASAFLOAT const Cj = pC [colj] - st*pAj ;
                            /* Modify pC to account for later update. That is,
                               when we drop the row, the contribution of the
                               elements in the row to pC need to be removed.
                               The previous contribution of row i to pC was
                               -a_{ij}lambda_i. So now we need to add this
                               to pC. Note that dlnew = -lambda_i. */
                            pC [colj] -= dlnew*ax ;
                            t = dlambda_row*ax ;
                            const PASAFLOAT pAjnew = pA [colj] = pAj - t ;
                            /* update c, pA, sd, fd */

                            if ( ib [colj] == 0 )
                            {
                                fd -= Cj*t ;
                                sd -= t*(pAj + pAjnew) ;
                            }
                            else if ( ib [colj] < 0 )
                            {
                                if ( pAjnew > PASAZERO )
                                {
                                    Br_value [colj] = st+(lo [colj]+Cj)/pAjnew ;
                                    if ( ns [colj] != EMPTY )
                                    {
                                        pproj_minheap_update (Heap,
                                           ns, Br_value, nbrk, ns [colj]) ;
                                    }
                                    else
                                    {
                                       pproj_minheap_add (colj, Heap,
                                           ns, Br_value, &nbrk) ;
                                    }
                                }
                                else
                                {
                                    if ( ns [colj] != EMPTY )
                                    {
                                        pproj_minheap_delete (Heap,
                                           ns, Br_value, &nbrk, ns [colj]);
                                    }
                                }
                            }
                            else /* ib [colj] > 0 */
                            {
                                if ( pAjnew < PASAZERO )
                                {
                                    Br_value [colj] = st+(hi [colj]+Cj)/pAjnew ;
                                    if ( ns [colj] != EMPTY )
                                    {
                                        pproj_minheap_update (Heap,
                                           ns, Br_value, nbrk, ns [colj]) ;
                                    }
                                    else
                                    {
                                       pproj_minheap_add (colj, Heap,
                                           ns, Br_value, &nbrk) ;
                                    }
                                }
                                else
                                {
                                    if ( ns [colj] != EMPTY )
                                    {
                                        pproj_minheap_delete (Heap,
                                           ns, Br_value, &nbrk, ns [colj]);
                                    }
                                }
                            }
                        }
                    }
                    if ( PrintLevel > 2 )
                    {
                        if ( col < ncol )
                        {
                            PRINTF ("    free col: %ld\n", (LONG) col) ;
                        }
                        else
                        {
                            PRINTF ("    drop row, sing: %ld\n",
                                                 (LONG) col-ncol) ;
                        }
                    }
                    if ( fd <= PASAZERO )
                    {
                        break ;
                    }
                }
            }
            if ( fd < PASAZERO )
            {
                fd = PASAZERO ;
            }
            if ( sd > PASAZERO )
            {
                st += fd/sd ;
            }
            if ( (nbrk0 == 0) && (fd > PASAZERO) )
            {
                st = PASAONE ;
            }
            if ( PrintLevel >= 1 )
            {
                printf ("line search step after epsilon update: %e\n", st) ;
            }
            if ( st > PASAZERO )
            {
                for (i = RLinkUp [nrow]; i < nrow; i = RLinkUp [i])
                {
                    lambda [i] += st*dlambda [i] ;
                }
            }
            if ( nbrk > 0 )
            {
                for (k = 1; k <= nbrk; k++)
                {
                    ns [Heap [k]] = EMPTY ;
                }
            }
            Work->nf = nf ;
            Work->ncoladd = ncoladd ;
            Work->nrowdel = nrowdel ;

            if ( scale_l_inc != PASAONE )
            {
                if ( nsing ) pasa_scale (singc+1, singc+1, scale_l_inc, nsing) ;
                pasa_scale (c, c, scale_l_inc, ncol) ;
                pasa_scale (lambda, lambda, scale_l_inc, nrow) ;
                pasa_scale (x , x , scale_l_inc, ncol) ;
                pasa_scale (b , b , scale_l_inc, nrow) ;
                if ( loExists ) pasa_scale (lo, lo, scale_l_inc, ncol) ;
                if ( hiExists ) pasa_scale (hi, hi, scale_l_inc, ncol) ;
                if ( nsingni )
                {
                    pasa_scale (bl+1, bl+1, scale_l_inc, nsingni) ;
                    pasa_scale (bu+1, bu+1, scale_l_inc, nsingni) ;
                }
            }
        }
        else /* either no cholmod or the matrix was not factored or no rows */
        {
            if ( scale_l_inc != PASAONE )
            {
                pasa_scale (c, c, scale_l_inc, ncol) ;
                pasa_scale (lambda, lambda, scale_l_inc, nrow) ;
                if ( nsing ) pasa_scale (singc+1, singc+1, scale_l_inc, nsing) ;
            }
            t = epsilon_inc*scale_l_inc ;
            if ( t != PASAONE )
            {
                pasa_scale (x , x , t, ncol) ;
                pasa_scale (b , b , t, nrow) ;
                if ( loExists ) pasa_scale (lo, lo, t, ncol) ;
                if ( hiExists ) pasa_scale (hi, hi, t, ncol) ;
                if ( nsingni )
                {
                    pasa_scale (bl+1, bl+1, t, nsingni) ;
                    pasa_scale (bu+1, bu+1, t, nsingni) ;
                }
            }
        }

        epsilon = epsilon_new*(scale_l_inc/scale_x_inc) ;

        if ( PrintLevel )
        {
            printf ("epsilon after scale adjustment: %e epsilon_inc2: %e\n",
                     epsilon, scale_l_inc/scale_x_inc) ;
        }

        /* perform translation and check error */

        /* nonzero diagonal for ssor algorithms */
        pasa_initx (WD, sigma, nrow) ;

        /* start building right side for proximal problem */
        pasa_copyx  (Wb, b, nrow) ; /* NOTE: bi = 0 for inequalities*/

        /* initial an array absAx = g (malloc'd above using arrayd) to zero */
        PASAFLOAT *absAx = g ;
        pasa_initx (absAx, PASAZERO, nrow) ;

        /* below dx become x inside of pproj, the updated x in pproj
           is returned in dx and then added to x in gradprojLP to
           obtain the primal solution approximation  */
        berr = cerr = PASAZERO ;
        /* When the if below is TRUE, check if a bound on a variable is active
           and if so, then bind the variable. Likewise, if a row has dropped
           and it is now active, then add the row. In this case, the AFT
           matrix is rebuilt. If update_bind is FALSE, then no binding of
           variable or adding of rows is done. */
        int const update_bind = FALSE ;
        if ( update_bind )
        {
            Annz = nf = p = 0 ;
            /* rebuild AFT, initialize nnz's to zero */
            pproj_initi (AFTnz, (PPINT) 0, nrow) ;
            for (j = 0; j < ncol; j++)
            {
                cj = -c [j] ;
                p0 = p ;
                PASAINT const q = Ap [j+1] ;
                for (; p < q; p++)
                {
                    cj += lambda [Ai [p]]*Ax [p] ;
                }
                int const ibj = ib [j] ;
                PASAFLOAT const loj   = (loExists) ? lo [j] : -PPINF ;
                PASAFLOAT const hij   = (hiExists) ? hi [j] :  PPINF ;
                PASAFLOAT const xj    = x [j] ;
                PASAFLOAT const xjnew = xj + cj ;
    
                if ( (xjnew > hij) || (xjnew < loj) ) /* xj at a bound */
                {
                    if ( cholmod && !ibj ) /* xj free previously, now bound*/
                    {
                        if ( PrintLevel )
                        {
                            printf ("free %ld -> bound\n", (LONG) j) ;
                        }
                        if ( ColmodFlag [j] == EMPTY ) /* add to bound list */
                        { 
                            ncoldel++ ;
                            ColmodList [ncol-ncoldel] = j ;
                            ColmodFlag [j] = ncol-ncoldel ;
                        }
                        else /* remove j from add list since it is now bound */
                        {
                            l = ColmodFlag [j] ;
                            ncoladd-- ;
                            m = ColmodList [ncoladd] ;
                            ColmodList [l] = m ;
                            ColmodFlag [m] = l ;
                            ColmodFlag [j] = EMPTY ;
                        }
                    }
                    if ( xjnew < loj ) /* xj at lower bound */
                    {
                        cerr = PASAMAX (cerr, cj) ;
                        ib [j] = -1 ;
                        Wc [j] = xjnew - loj ;
                        Wlo [j] = PPZERO ;
                        if ( hiExists ) Whi [j] = hij - loj ;
                        for (p = p0; p < q; p++)
                        {
                            Wb [Ai [p]] -= loj*Ax [p] ;
                        }
                        dx [j] = loj - xj ;
                    }
                    else               /* xj at upper bound */
                    {
                        cerr = PASAMAX (cerr, -cj) ;
                        ib [j] = 1 ;
                        Wc [j] = xjnew - hij ;
                        Whi [j] = PPZERO ;
                        if ( loExists ) Wlo [j] = loj - hij ;
                        for (p = p0; p < q; p++)
                        {
                            Wb [Ai [p]] -= hij*Ax [p] ;
                        }
                        dx [j] = hij - xj ;
                    }
                }
                else /* xj is free */
                {
                    if ( cholmod && ibj ) /* xj previously bound but now free */
                    {
                        if ( PrintLevel )
                        {
                            printf ("bound %ld ->free\n", (LONG) j) ;
                        }
                        k = ColmodFlag [j] ;
                        /*If j is not scheduled to be free or bound, free it.*/
                        if ( k == EMPTY )
                        {
                            ColmodList [ncoladd] = j ;
                            ColmodFlag [j] = ncoladd ;
                            ncoladd++ ;
                        }
                        else /* xj to be bound, remove from bind list */
                        {
                            l = ColmodList [ncol-ncoldel] ;
                            ColmodList [k] = l ;
                            ColmodFlag [l] = k ;
                            ColmodFlag [j] = EMPTY ;
                            ncoldel-- ;
                        }
                    }
                    F [nf++] = j ;
                    ib [j] = 0 ;
                    Wc [j] = PPZERO ; 
                    dx [j] = cj ;
                    cerr = PASAMAX (cerr, fabs (cj)) ;
                    /* translate bounds and determine bound violation error */
                    if ( loExists ) Wlo [j] = loj - xjnew ;
                    if ( hiExists ) Whi [j] = hi [j] - xjnew ;
                    for (p = p0; p < q; p++)
                    {
                        PASAINT   const ai = Ai [p] ;
                        PASAFLOAT const ax = Ax [p] ;
                        Wb [ai] -= xjnew*ax ;
                        if ( ir [ai] <= nsingni ) /* row is active */
                        {
                            Annz++ ;
                            WD [ai] += ax*ax ;
                            k = AFTp [ai] + AFTnz [ai]++ ;
                            AFTi [k] = j ;
                            AFTx [k] = ax ;
                        }
                    }
                }
            }
    
            Work->nf = nf ;
            ATnz = 0 ;    /* number of nonzeros in active rows of full matrix */
            nactive = 0 ; /* number of active rows */
            Ll = nsingni1 ;
            Ul = nsingni2 ;
            Rl = nrow ;
            prevblkL = prevblkU = EMPTY ;
            blks = Work->blks ;
            if ( cholmod )
            {
                for (k = 0; k < blks; k++)
                {
                    lstart [k] = nsingni1 ;
                    ustart [k] = nsingni2 ;
                }
            }
            /* continue to build Wb by accounting for inequalites/singletons */
            j = 1 ;
            if ( nsingni )
            {
                for (i = 0; i < nrow; i++)
                {
                    t = Wb [i] ;
                    PASAINT const iri = ir [i] ;
                    if ( iri <= nsingni )        /* row is active */
                    {
                        nactive++ ;
                        ATnz += ATp [i+1] - ATp [i] ;
                        Rl = i ;
                        if ( ni )
                        {
                            if ( iri < 0 )
                            {
                                PASAINT const sing = -iri ;
                                t += bl [sing] ;
                                Ll = sing ;
                                SET_LSTART(sing) ;
                            }
                            else if ( iri > 0 ) /* iri > 0 at upper bound */
                            {
                                t += bu [iri] ;
                                Ul = iri ;
                                SET_USTART(iri) ;
                            }
                        }
                        else if ( iri ) /* active row with column singletons*/
                        {
                            PASAINT const qlo = slo [i] ;
                            PASAINT const qhi  = row_sing1 [i] ;
                            if ( qlo ) /* existing lower bounds active */
                            {
                                for (; j <= qlo; j++) t += bl [j] ;
                                Ll = qlo ;
                                SET_LSTART(qlo) ;
                            }
                            /* j < qhi => shi not 0, existing upper bounds */
                            if ( j < qhi)
                            {
                                /* singleton associated with upper bound */
                                Ul = j ;
                                SET_USTART(j) ;
                                for (; j < qhi; j++) t += bu [j] ;
                            }
                        }
                        /* this also handles the case iri = 0, simple equation*/
                        berr = PASAMAX (berr, fabs (t)) ;
                        Wb [i] = t - sigma*lambda [i] ;
                    }
                    else /* currently dropped equation */
                    {
                        PASAINT addeq = FALSE ;
                        PASAINT const sing = iri - nsingni ;
                        if ( nsing ) /* add column singleton terms into t */
                        {
                            Q1 = row_sing1 [i] ;
                            Q0 = j ;
                            ASSERT (j == row_sing [i]) ;
                            for (; j < sing; j++) t += bl [j] ;
                            shii = ++j ; /* skip singleton */
                            for (; j < Q1; j++)   t += bu [j] ;
                        }
                        if ( (s = t + bl [sing]) > PASAZERO )
                        {
                            berr = PASAMAX (berr, s) ;
                        }
                        else if ( (s = t + bu [sing]) < PASAZERO )
                        {
                            berr = PASAMAX (berr, -s) ;
                        }
    
                        /* when activating constraints, also need to take into
                           account the proximal term */
                        t -= sigma*lambda [i] ;
    
                        /* check if increasing lambda_i increases dual */
                        if ( (s = t + bl [sing]) > PPZERO )
                        {
                            if ( PrintLevel )
                            {
                                printf ("dropped equation %ld, "
                                        "singleton %ld -> active lower "
                                        "(violation %e bl: %e)\n",
                                        (LONG) i, (LONG) sing, s, bl [sing]);
                            }
                            /* activate lower bound */
                            t = s ;
                            ADD_SING_IN_LLINK(sing) ;
                            SET_LSTART(sing) ;
                            addeq = TRUE ;
                            /* for column singletons, also update upper bound */
                            if ( nsing )
                            {
                                ir [i] = 1 ;
                                slo [i] = sing ;
                                if ( shii == Q1 ) shi [i] = 0 ;
                                else
                                {
                                    shi [i] = shii ;
                                    ADD_SING_IN_ULINK(shii) ;
                                    SET_USTART_SIMPLE(shii) ;
                                }
                            }
                            else
                            {
                                ir [i] = -sing ;
                            }
                        }
                        /* check if decreasing lambda_i increases dual */
                        else if ( (s = t + bu [sing]) < PPZERO )
                        {
                            if ( PrintLevel )
                            {
                                printf ("dropped equation %ld, "
                                        "singleton %ld -> active upper "
                                        "(violation %e, bu: %e)\n",
                                        (LONG) i, (LONG) sing, -s, bu [sing]);
                            }
                            /* activate upper bound */
                            t = s ;
                            ADD_SING_IN_ULINK(sing) ;
    
                            SET_USTART(sing) ;
                            addeq = TRUE ;
                            /* for column singletons, also update lower bound */
                            if ( nsing )
                            {
                                ir [i] = 1 ;
                                shi [i] = sing ;
                                if ( sing == Q0 ) slo [i] = 0 ;
                                else
                                {
                                    k = slo [i] = sing - 1 ;
                                    ADD_SING_IN_LLINK(k) ;
                                    SET_LSTART_SIMPLE(k) ;
                                }
                            }
                            else
                            {
                                ir [i] = sing ;
                            }
                        }
                        Wb [i] = t ; /* the final right side */
                        if ( addeq ) /* the equation is active */
                        {
                            nactive++ ;
                            ATnz += ATp [i+1] - ATp [i] ;
                            ADD_ROW_IN_RLINK(i) ;
                            if ( cholmod )
                            {
                                l = RowmodFlag [i] ;
                                /* if row missing from factor, add it */
                                if ( l == EMPTY )
                                {
                                    nrowadd++ ;
                                    RowmodList [nrow-nrowadd] = i ;
                                    RowmodFlag [i] = nrow-nrowadd ;
                                }
                                else /* remove row from delete list */
                                {
                                    nrowdel-- ;
                                    m = RowmodList [nrowdel] ;
                                    RowmodList [l] = m ;
                                    RowmodFlag [m] = l ;
                                    RowmodFlag [i] = EMPTY ;
                                }
                            }
                            t = PPZERO ;
                            PASAINT const q = ATp [i+1] ;
                            PASAINT l0 = l = AFTp [i] ;
                            for (p = ATp [i]; p < q; p++)
                            {
                                PPINT   const aj = ATi [p] ;
                                if ( !ib [aj] ) /* the column is free */
                                {
                                    Annz++ ;
                                    PPFLOAT const ax = ATx [p] ;
                                    t += ax*ax ;
                                    AFTx [l] = ax ;
                                    AFTi [l] = aj ;
                                    l++ ;
                                }
                            }
                            WD [i] += t ;  /* new diagonal element for SSOR */
                            AFTnz [i] = l - l0 ;
                        }
                    }
                }
            }
            else /* pure equality constraints */
            {
                berr = pasa_sup_norm (Wb, nrow) ;
                pproj_saxpy (Wb, lambda, -sigma, nrow) ;
            }
            Work->ATnz = ATnz ;
            Work->Annz = Annz ;
            Work->nactive = nactive ;
            Work->nrowadd = nrowadd ;
            Work->nrowdel = nrowdel ;
            Work->ncoladd = ncoladd ;
            Work->ncoldel = ncoldel ;
        }
        else /* do not bind constraints and add rows, recompute W->b, c, lo,
                and hi for pproj */
        {
            p = 0 ;
            for (j = 0; j < ncol; j++)
            {
                cj = -c [j] ;
                PASAINT   const  q = Ap [j+1] ;
                PASAFLOAT const xj = x [j] ;
                int const ibj = ib [j] ;
                if ( ibj < 0 )
                {
                    PASAFLOAT const loj = lo [j] ;
                    for (; p < q; p++)
                    {
                        PASAINT   const ai = Ai [p] ;
                        PASAFLOAT const ax = Ax [p] ;
                        cj += lambda [ai]*ax ;
                        PASAFLOAT const AX = loj*ax ;
                        Wb [ai] -= AX ;
                        absAx [ai] += fabs (AX) ;
                    }
                    cerr = PASAMAX (cerr, cj) ;
                    Wc  [j] = cj - xj + loj ;
                    Wlo [j] = PPZERO ;
                    if ( hiExists ) Whi [j] = hi [j] - loj ;
                    dx [j] = loj - xj ;
                }
                else if ( ibj > 0 )
                {
                    PASAFLOAT const hij = hi [j] ;
                    for (; p < q; p++)
                    {
                        PASAINT   const ai = Ai [p] ;
                        PASAFLOAT const ax = Ax [p] ;
                        cj += lambda [ai]*ax ;
                        PASAFLOAT const AX = hij*ax ;
                        Wb [ai] -= AX ;
                        absAx [ai] += fabs (AX) ;
                    }
                    cerr = PASAMAX (cerr, -cj) ;
                    Wc  [j] = cj - xj + hij ;
                    Whi [j] = PPZERO ;
                    if ( loExists ) Wlo [j] = lo [j] - hij ;
                    dx [j] = hij - xj ;
                }
                else
                {
                    p0 = p ;
                    for (; p < q; p++)
                    {
                        cj += lambda [Ai [p]]*Ax [p] ;
                    }
                    dx [j] = cj ;
                    cerr = PASAMAX (cerr, fabs (cj)) ;
                    PASAFLOAT const xjnew = xj + cj ;
                    if ( loExists ) Wlo [j] = lo [j] - xjnew ;
                    if ( hiExists ) Whi [j] = hi [j] - xjnew ;
                    Wc [j] = PPZERO ; 
                    for (p = p0; p < q; p++)
                    {
                        PASAINT   const ai = Ai [p] ;
                        PASAFLOAT const ax = Ax [p] ;
                        PASAFLOAT const AX = xjnew*ax ;
                        Wb [ai] -= AX ;
                        absAx [ai] += fabs (AX) ;
                        if ( ir [ai] <= nsingni ) /* row is active */
                        {
                            WD [ai] += ax*ax ;
                        }
                    }
                }
            }
    
            /* continue to build Wb by accounting for inequalites/singletons */
            j = 1 ;
            if ( nsingni )
            {
                PASAFLOAT R = PASAZERO ;
                for (i = 0; i < nrow; i++)
                {
                    t = Wb [i] ;
                    PASAINT const iri = ir [i] ;
                    if ( iri <= nsingni )        /* row is active */
                    {
                        if ( absAx [i] > R ) R = absAx [i] ;
                        if ( ni )
                        {
                            if ( iri < 0 )
                            {
                                PASAINT const sing = -iri ;
                                t += bl [sing] ;
                            }
                            else if ( iri > 0 ) /* iri > 0 at upper bound */
                            {
                                t += bu [iri] ;
                            }
                        }
                        else if ( iri ) /* active row with column singletons*/
                        {
                            PASAINT const qlo = slo [i] ;
                            PASAINT const qhi  = row_sing1 [i] ;
                            if ( qlo ) /* existing lower bounds active */
                            {
                                for (; j <= qlo; j++) t += bl [j] ;
                            }
                            /* j < qhi => shi not 0, existing upper bounds */
                            if ( j < qhi)
                            {
                                /* singleton associated with upper bound */
                                for (; j < qhi; j++) t += bu [j] ;
                            }
                        }
                        /* this also handles the case iri = 0, simple equation*/
                        berr = PASAMAX (berr, fabs (t)) ;
                        Wb [i] = t - sigma*lambda [i] ;
                    }
                    else /* currently dropped equation */
                    {
                        PASAINT const sing = iri - nsingni ;
                        if ( nsing ) /* add column singleton terms into t */
                        {
                            ASSERT (j == row_sing [i]) ;
                            for (; j < sing; j++)   t += bl [j] ;
                            PASAINT const qhi = row_sing1 [i] ;
                            /* skip singleton */
                            for (j++; j < qhi; j++) t += bu [j] ;
                        }
                        if ( (s = t + bl [sing]) > PASAZERO )
                        {
                            berr = PASAMAX (berr, s) ;
                        }
                        else if ( (s = t + bu [sing]) < PASAZERO )
                        {
                            berr = PASAMAX (berr, -s) ;
                        }
                        /* the final right side */
                        Wb [i] = t - sigma*lambda [i] ;
                    }
                }
                if ( R == PASAZERO ) R = PASAONE ;
                Work->absAx = R ;
            }
            else /* pure equality constraints */
            {
                berr = pasa_sup_norm (Wb, nrow) ;
                pproj_saxpy (Wb, lambda, -sigma, nrow) ;
                Work->absAx = pasa_sup_norm (absAx, nrow) ;
                if ( Work->absAx == PASAZERO ) Work->absAx = PASAONE ;
            }
        }
        berr /= (epsilon*xmax) ;
        cerr /= lmax ;
        err = PASAMAX (cerr, berr) ;
        if ( PrintLevel >= 1 )
        {
            printf ("after line search cerr: %e berr: %e xmax: %e lmax: %e "
                    "active rows: %ld nf: %ld\n", cerr, berr, xmax, lmax,
                    (LONG) Work->nactive, (LONG) Work->nf) ;
        }
        if ( err <= grad_tol )
        {
            /* include the update to x computed above, when error computed */
            pasa_step (x, x, dx, PASAONE, ncol) ;
            break ;
        }
        /* the point we project onto the polyhedron is y = -c */
#ifndef NDEBUG
        pasa_scale (Prob->y, c, -PASAONE, ncol) ;
        pasa_copyx (checkb, b, nrow) ;
        p = 0 ;
        for (j = 0; j < ncol; j++)
        {
            PASAINT   const  q = Ap [j+1] ;
            PASAFLOAT const xj = x [j] ;
            for (; p < q; p++)
            {
                checkb [Ai [p]] -= Ax [p]*xj ;
            }
        }
        if ( loExists ) pasa_step (checklo, lo, x, -PASAONE, ncol) ;
        if ( hiExists ) pasa_step (checkhi, hi, x, -PASAONE, ncol) ;
#endif
    }

#ifndef NDEBUG
    pasa_free (checkb) ;
    if ( loExists ) pasa_free (checklo) ;
    if ( hiExists ) pasa_free (checkhi) ;
#endif
    Com->E = err ;
    Com->pasastat->gpit = iter ;

    /* convert to user's problem (remove scale factors and permutations) */
    PASAFLOAT add, Hij, Loj ;
    scale_x = PASAONE/(scale_x*epsilon) ;
    scale_l = PASAONE/scale_l ;
    PASAINT *rowperm = ppcom->Prob->rowperm ;
    PASAINT *colperm = ppcom->Prob->colperm ; /* after fixed removed */
    /* if there were fixed variables, then temp_ifree contains the
       reordering associated with the removal of the fixed variables */
    PASAINT *ifree = Com->temp_ifree ;
    PASAFLOAT *lambdatemp = ppcom->Work->arrayd ;
    PASAFLOAT *userx = Com->userx ;

    /* compute the dual multipliers */
    for (i = 0; i < nrow; i++)
    {
        k = rowperm [i] ;
        lambdatemp [k] = scale_l * lambda [i] * Com->RowScale [k] ;
    }
    pasa_copyx (lambda, lambdatemp, nrow) ;

    /* compute the part of x not associated with column singletons */
    if ( ifree == NULL )
    {
        for (j = 0; j < ncol; j++)
        {
            i = colperm [j] ;
            userx [i] = Com->offset [i] + x [j]*Com->ColScale [i]*scale_x ;
        }
    }
    else
    {
        for (j = 0; j < ncol; j++)
        {
            i = colperm [j] ;  /* user variable for pproj's column j */
            k = ifree [i] ;    /* user location accounting for bound variables*/
            userx [k] = Com->offset [i] + x [j]*Com->ColScale [i]*scale_x ;
        }
    }

    /* now compute the part of x associated with column singletons
       The statement SET_USERX1 below represents the following block of code.
       Note that the variable col corresponds to a column of the original
       matrix. If col < 0, it is not a column of the matrix; it corresponds
       to the right and left side bounds for an equation and hence there is
       no corresponding component of userx to compute.

           col = Com->sj [l] ;
           if ( col >= 0 )
           {
               if ( Com->sx [l] > PASAZERO )
               {
                   userx [col] = Com->shi[l] ;
               }
               else
               {
                   userx [col] = Com->slo[l] ;
               }
           }

        SET_USERX2 is the same except that shi and slo are switched. */

    PASAINT const *singperm = Com->singperm ;
    for (j = 1; j <= nsing; )
    {
        i = ineq_row [j] ;                /* row in the permuted matrix */
        PASAINT const ksing = ir [i] ;
        PASAINT const row = rowperm [i] ; /* row in original matrix */

        if ( ksing == 1 ) /* row is active, all singletons at bounds */
        {
            k = userRowSing [row] - j ;
            /* NOTE: Work->slo is a singleton index while Com->slo is the
                     lower bound for a column singleton.
                     Similar for Prob->shi and Com->shi */
            PASAINT const q = slo [i] ;
            for (; j <= q; j++) /*variables at lower bounds */
            {
                /* l = singleton number in original matrix when l > 0 */
                l = singperm [j+k] ;
                if ( l < 0 ) /* compression has occurred */
                {
                    l = -(l+1) ; /* singleton number in original matrix */
                    while ( l != EMPTY )    /* l empty <=> last compression */
                    {
                        SET_USERX1
                        l = Com->si [l] ;
                    }
                }
                else /* no compression */
                {
                    SET_USERX1
                }
            }
            PASAINT const qtop = row_sing1 [i] ;
            for (; j < qtop; j++) /*variable at upper bounds */
            {
                /* l = singleton number in original matrix when l > 0 */
                l = singperm [j+k] ;
                if ( l < 0 ) /* compression has occurred */
                {
                    l = -(l+1) ; /* singleton number in original matrix */
                    while ( l != EMPTY )    /* l empty <=> last compression */
                    {
                        SET_USERX2 ;
                        l = Com->si [l] ;
                    }
                }
                else /* no compression */
                {
                    SET_USERX2 ;
                }
            }
        }
        else /* row inactive */
        {
            /* compute the singleton associated with inactive row */
            PASAINT const sing = ksing - nsing ;
            /* in pproj problem, evaluate the bound singletons and their sum */
            t = PASAZERO ;
            k = userRowSing [row] - j ;
            PASAINT const q = sing ;
            for (; j < q; j++) /*variables at lower bounds */
            {
                /* l = singleton number in original matrix when l > 0 */
                l = singperm [j+k] ;
                t += bl [j] ;
                if ( l < 0 ) /* compression has occurred */
                {
                    l = -(l+1) ; /* singleton number in original matrix */
                    while ( l != EMPTY )    /* l empty <=> last compression */
                    {
                        SET_USERX1 ;
                        l = Com->si [l] ;
                    }
                }
                else /* no compression */
                {
                    SET_USERX1 ;
                }
            }
            PASAINT const qtop = row_sing1 [i] ;
            for (j++; j < qtop; j++) /*variable at upper bounds */
            {
                /* l = singleton number in original matrix when l > 0 */
                l = singperm [j+k] ;
                t += bu [j] ;
                if ( l < 0 ) /* compression has occurred */
                {
                    l = -(l+1) ; /* singleton number in original matrix */
                    while ( l != EMPTY )    /* l empty <=> last compression */
                    {
                        SET_USERX2 ;
                        l = Com->si [l] ;
                    }
                }
                else /* no compression */
                {
                    SET_USERX2 ;
                }
            }

            /* take into account b and the nonsingleton part of the matrix */
            t += Prob->b [i] ;
            PASAINT const q2 = ATp [i+1] ;
            for (p = ATp [i]; p < q2; p++)
            {
                t -= ATx [p]*x [ATi [p]] ;
            }
            /* remove the scaling from the right side */
            t *= scale_x ;

            /* we now have the right side of the permuted problem; the only
               scaling is RowScale, however, this scaling was previously
               applied to Com->sx [l]. Now solve for the column singleton. */
            l = singperm [sing+k] ;
            if ( l < 0 ) /* compression has occurred */
            {
                l = -(l+1) ; /* singleton number in original matrix */
                   /* The relationship between the solution yj in the
                   transformed problem that we compute, and the solution xj
                   in the original problem is yj = -sx*(xj - oj), oj = offset.
                   Also, in the transformed equation, the term appears as -yj.
                   If t denotes the right side of the transformed equation
                   after moving all terms to the right except for the -yj term,
                   then we have -yj = t = sx*(xj-oj).
                   Hence, xj = oj + t/sx. When compression occurs, we have
                   sum_j sx_j(xj-oj) = t because each yj term in the
                   transformed problem is a sum of terms in the original
                   problem. Here xj lies between its bounds loj and hij,
                   and oj is either 0 or loj or hij, where oj is chosen so
                   xj - oj has 0 in its range of possible values. To solve
                   for xj, proceed as follows: If t < 0, then in the
                   successive terms in sum_j, choose xj to make the j-th
                   term as small as possible, until the partial sum reaches t.
                   Then set xj = oj for the remaining terms in the sum.
                   If t > 0, then choose xj to make the j-th term as large as
                   possible, until the partial sum reaches t, then set xj = oj
                   for the remaining terms in the sum.  */
                while ( l != EMPTY )
                {
                    col = Com->sj [l] ; /* column in original matrix */
                    Loj = Com->slo [l] ;
                    Hij = Com->shi [l] ;
                    /* compute the offset denoted oj below */
                    if      ( Loj > PASAZERO ) oj = Loj ;
                    else if ( Hij < PASAZERO ) oj = Hij ;
                    else                       oj = PASAZERO ;

                    if ( t < PASAZERO )
                    {
                        PASAFLOAT const sx  = Com->sx  [l] ;
                        /* make the terms as small as possible */
                        if ( sx > PASAZERO )
                        {
                            add = sx*(Loj-oj) ;
                        }
                        else
                        {
                            add = sx*(Hij-oj) ;
                            Hij = Loj ;
                        }
                        if ( add <= t ) /* done, t has been reached */
                        {
                            /* if col < 0, this was a strict inequality
                               converted to an equality, nothing to do */
                            if ( col >= 0 ) userx [col] = oj + t/sx ;
#ifndef NDEBUG
                            if ( col >= 0 )
                            {
                                t = userx [col] ;
                                if ( (t > Com->shi [l]) || (t < Com->slo [l]) )
                                {
                                    printf ("Computed singleton outside "
                                            "bounds\n") ;
                                    printf ("Computed: %e lo: %e hi: %e\n",
                                             t, Com->slo [l], Com->shi [l]) ;
                                    /* pproj_error
                                          (-1, __FILE__, __LINE__, "stop\n") ;*/
                                }
                            }
#endif
                            l = Com->si [l] ;
                            break ;
                        }
                    }
                    else /* t >= 0 */
                    {
                        /* make the terms as large as possible */
                        PASAFLOAT const sx  = Com->sx  [l] ;
                        if ( sx > PASAZERO )
                        {
                            add = (Hij-oj)*sx ;
                            Loj = Hij ;
                        }
                        else
                        {
                            add = (Loj-oj)*sx ;
                        }
                        if ( add >= t ) /* done, t has been reached */
                        {
                            if ( col >= 0 ) userx [col] = oj + t/sx ;
#ifndef NDEBUG
                            if ( col >= 0 )
                            {
                                t = userx [col] ;
                                if ( (t > Com->shi [l]) || (t < Com->slo [l]) )
                                {
                                    printf ("Computed singleton outside "
                                            "bounds\n") ;
                                    printf ("Computed: %e lo: %e hi: %e\n",
                                             t, Com->slo [l], Com->shi [l]) ;
                                    /*pproj_error
                                          (-1, __FILE__, __LINE__, "stop\n") ;*/
                                }
                            }
#endif
                            l = Com->si [l] ;
                            break ;
                        }
                    }
                    t += add ;
                    /* the correct bound was set to Loj above */
                    if ( col >= 0 ) userx [col] = Loj ;
                    l = Com->si [l] ;
                }

                /* set the remaining variables to the offset */
                while ( l != EMPTY )
                {
                    oj = Com->slo [l] ;
                    if      (           oj > PASAZERO ) ;
                    else if ( Com->shi [l] < PASAZERO ) oj = Com->shi [l] ;
                    else                                oj = PASAZERO ;

                    col = Com->sj [l] ;
                    if ( col >= 0 ) userx [col] = oj ;
                    l = Com->si [l] ;
                }
            }
            else /* no compression oj */
            {
                /* The problem fed to pproj involved an offset. We need
                   to compute that offset in order to solve for the
                   column singleton.  The offset is denoted oj below. */
                oj = Com->slo [l] ;
                if      (           oj > PASAZERO ) ;
                else if ( Com->shi [l] < PASAZERO ) oj = Com->shi [l] ;
                else                                oj = PASAZERO ;
                /* xj = offset + rhsj/sx where rhsj denote all the terms in
                   the equation moved to the right side of the equation. This
                   is stored in the variable t above. */
                col = Com->sj [l] ;
                if ( col >= 0 ) userx [col] = oj + t/Com->sx [l] ;
#ifndef NDEBUG
                if ( col >= 0 )
                {
                    t = userx [col] ;
                    if ( (t > Com->shi [l]) || (t < Com->slo [l]) )
                    {
                        printf ("Computed singleton outside bounds\n") ;
                        printf ("Computed: %e lo: %e hi: %e\n",
                                 t, Com->slo [l], Com->shi [l]) ;
                        /*pproj_error (-1, __FILE__, __LINE__, "stop\n") ;*/
                    }
                }
#endif
            }
        }
    }

    /* store objective value */
    Com->f = pasa_dot (Com->userc, Com->userx, Com->ucol) ;

    /* store iteration count and test for error */
    Com->pasastat->gpit = iter ;
    if ( iter >= gpmaxit )
    {
        return (PASA_ITERATIONS_EXCEED_MAXITS_IN_GRAD_PROJ) ;
    }
    return (PASA_ERROR_TOLERANCE_SATISFIED) ;
}

/* ==========================================================================
   === pasa_errLP ===========================================================
   ==========================================================================
   Consider the following LP:

       min c'x subject to bl <= Ax <= bu, lo <= x <= hi

   At optimality, the constraints are satisfied and there exists lambda
   with the following properties:

       1. If A_j' lambda < c_j, then x_j = hi_j
       2. If A_j' lambda > c_j, then x_j = lo_j
       3. If bl_i < A_i'x < bu_i, then lambda_i = 0
       4. If A_i'x = bu_i, then lambda_i <= 0
       5. If A_i'x = bl_i, then lambda_i >= 0
   ========================================================================== */
void pasa_errLP
(
    PASAFLOAT epsilon, /* some data is scaled by epsilon in gradprojLP */
    PASAcom      *Com  /* common data for PASA */
)
{
    PASAINT i, j, k, p ;
    PASAFLOAT d, e, Eprimal, Elambda, Emax, loj, hij, Lmax, max_absAx, Xmax, t,
             *absAx ;
    PPwork   *Work ;  /* work structure in PPROJ */
    PASAparm *Parm ;  /* parameters for PASA */
    PPprob   *Prob ;

    Prob = Com->ppcom->Prob ;
    Parm = Com->pasaparm ; /* parameters */
    int       const  PrintLevel = Parm->PrintLevel ;
    int       const    loExists = Com->loExists ;
    int       const    hiExists = Com->hiExists ;
    PASAINT   const        ncol = Com->ncol ;
    PASAINT   const        nrow = Com->nrow ;
    PASAINT   const       nsing = Com->pasadata->ppdata->nsing ;
    PASAINT   const          ni = Com->pasadata->ppdata->ni ;
    PASAFLOAT const          *x = Com->x ;
    PASAFLOAT const          *c = Com->c ;
    PASAFLOAT const     *lambda = Com->lambda ;
    PASAINT   const         *Ap = Prob->Ap ;
    PASAINT   const         *Ai = Prob->Ai ;
    PASAFLOAT const         *Ax = Prob->Ax ;
    PASAFLOAT const          *b = Prob->b ;
    PASAFLOAT const         *lo = Prob->lo ;
    PASAFLOAT const         *hi = Prob->hi ;
    PASAFLOAT const         *bl = Prob->bl ;
    PASAFLOAT const         *bu = Prob->bu ;
    PASAINT   const   *row_sing = Prob->row_sing ;
    PPFLOAT   const      *singc = Prob->singc ;
    PPFLOAT   const     *singlo = Prob->singlo ;
    PPFLOAT   const     *singhi = Prob->singhi ;
    PASAINT   const  *row_sing1 = row_sing+1 ;

    Work   = Com->ppcom->Work ;
    PASAINT   const         *ir = Work->ir ;
    PASAFLOAT                *y = Work->arrayd ;
    pasa_initx (y, PASAZERO, nrow) ;
    absAx = y+nrow ;
    pasa_initx (absAx, PASAZERO, nrow) ;
    Xmax = PASAZERO ;
    Emax = PASAZERO ;
    if ( PrintLevel > 1 ) printf ("Primal Bound Errors:\n") ;
    k = 0 ;
    for (j = 0; j < ncol; j++)
    {
        e = PASAZERO ;
        if ( loExists ) loj = lo [j] ;
        else            loj = -PASAINF ;
        if ( hiExists ) hij = hi [j] ;
        else            hij = PASAINF ;
        if ( x [j] < loj )
        {
            t = loj ;
            e = t - x [j] ;
        }
        else if ( x [j] > hij )
        {
            t = hij ;
            e = x [j] - t ;
        }
        else
        {
            t = x [j] ;
        }
        if ( PrintLevel > 2 )
        {
            printf ("%ld %e %e %e %e\n",
                  (LONG) j, e/epsilon, loj/epsilon, x [j]/epsilon, hij/epsilon);
        }
        if ( e > Emax ) Emax = e ;
        if ( fabs (t) > Xmax )
        {
            Xmax = fabs (t) ;
        }
        PASAINT const l = Ap [j+1] ;
        for (; k < l; k++)
        {
            y [Ai [k]] += t*Ax [k] ;
            absAx [Ai [k]] += fabs (t*Ax [k]) ;
        }
    }

    if ( PrintLevel > 1 )
    {
        printf ("Total: %e relative: %e Xmax: %e\n\n",
                 Emax/epsilon, Emax/Xmax, Xmax/epsilon) ;
        printf ("Primal Equation Errors:\n") ;
    }
    max_absAx = pasa_sup_norm (absAx, nrow) ;
    k = 0 ;
    Eprimal = PASAZERO ;
    Elambda = PASAZERO ;
    for (i = 0; i < nrow; i++)
    {
        e = PASAZERO ;
        if ( ir [i] == 0 ) /* equality constraint, no singletons */
        {
            e = fabs (b [i] - y [i]) ;
            if ( PrintLevel > 2 )
            {
                printf ("%ld %e %e %e %e\n", (LONG) i, e/epsilon,
                        b [i]/epsilon, y [i]/epsilon, b [i]/epsilon) ;
            }
        }
        else if ( ni ) /* there are strict inequality constraints */
        {
            k++ ;
            if ( bl [k] >= y [i] )
            {
                e = bl [k] - y [i] ;
            }
            else if ( y [i] >= bu [k] )
            {
                e = y [i] - bu [k] ;
            }
            if ( PrintLevel > 2 )
            {
                printf ("p: %ld %e %e %e %e\n", (LONG) i, e/epsilon,
                         bl [k]/epsilon, y [i]/epsilon, bu [k]/epsilon) ;
            }
            d = PASAZERO ;
            if ( lambda [i] > PASAZERO )
            {
                d = PASAMIN (lambda [i], fabs (bl [k] - y [i])/epsilon) ;
            }
            else if ( lambda [i] < PASAZERO )
            {
                d = PASAMIN (-lambda [i], fabs (bu [k] - y [i])/epsilon) ;
            }
            if ( PrintLevel > 2 )
            {
                printf ("d: %ld %e %e %e %e\n",
                  (LONG) i, e, bl [k]/epsilon, lambda [i], bu [k]/epsilon) ;
            }
            if ( d > Elambda ) Elambda = d ;
        }
        else if ( nsing ) /* potential column singletons */
        {
            if ( ir [i] == 1 ) /* all singletons at bounds */
            {
                t = b [i] - y [i] ;
                PASAINT const q = Work->slo [i] ;
                /* singletons at lower bounds */
                for (k = row_sing [i]; k <= q; k++)
                {
                    t += singlo [k] ;
                }
                PASAINT const q1 =row_sing1 [i] ;
                /* singletons at upper bounds */
                for (; k < q1; k++)
                {
                    t += singhi [k] ;
                }
                e = fabs (t) ;
                if ( PrintLevel > 2 )
                {
                    printf ("%ld %e %e %e\n",
                    (LONG) i, e/epsilon, b [i]/epsilon, y [i]/epsilon) ;
                }
            }
            else
            {
                PASAINT const ksing = ir [i] - nsing ;
                t = PASAZERO ;
                /* singletons at lower bounds */
                for (k = row_sing [i]; k < ksing; k++)
                {
                    t += singlo [k] ;
                }
                /* singletons at upper bounds */
                for (k++; k < row_sing1 [i]; k++)
                {
                    t += singhi [k] ;
                }
                t += b [i] - y [i] ;
                t = -t ;
                /* t should lie between singlo [ksing] and singhi [ksing] */
                if ( t < singlo [ksing] ) e = singlo [ksing] - t ;
                else                      e = PASAZERO ;
                if ( t > singhi [ksing] )
                {
                    e = PASAMAX (e, t - singhi [ksing]) ;
                }
                if ( PrintLevel > 2 )
                {
                    printf ("psing: %ld %e %e %e %e\n", (LONG) i, e/epsilon,
                         singlo [ksing]/epsilon, t/epsilon,
                         singhi [ksing]/epsilon) ;
                }
                /* dual error */
                d = fabs (lambda [i] - singc [ksing]) ;
                if ( PrintLevel > 2 )
                {
                    printf ("dsing: %ld %e %e %e\n",
                            (LONG) i, d, lambda [i],singc [ksing]) ;
                }
                if ( d > Elambda ) Elambda = d ;
            }
        }
        if ( e > Eprimal ) Eprimal = e ;
    }

    if ( PrintLevel > 1 )
    {
        printf ("Primal Total: %e Relative: %e absAx: %e\n",
                        Eprimal/epsilon, Eprimal/max_absAx, max_absAx/epsilon) ;
        printf ("Lambda Total: %e Relative: %e\n\n",
                                           Elambda, Elambda*epsilon/max_absAx) ;
        printf ("Dual Equation Errors:\n") ;
    }
    Emax = PASAZERO ;
    p = 0 ;
    for (j = 0; j < ncol; j++)
    {
        PASAINT const q = Ap [j+1] ;
        t = c [j] ;
        for (; p < q; p++)
        {
            t -= Ax [p]*lambda [Ai [p]] ;
        }
        e = PASAZERO ;
        if ( loExists ) loj = lo [j] ;
        else            loj = -PASAINF ;
        if ( hiExists ) hij = hi [j] ;
        else            hij = PASAINF ;
        if ( t > PASAZERO )
        {
            e = PASAMIN (t, fabs (loj - x [j])/epsilon) ;
        }
        else if ( t < PASAZERO )
        {
            e = PASAMIN (-t, fabs (hij - x [j])/epsilon) ;
        }
        if ( e > Emax ) Emax = e ;
        if ( PrintLevel > 2 )
        {
            printf ("%ld %e %e %e %e\n",
                  (LONG) j, e, loj/epsilon, x [j]/epsilon, hij/epsilon) ;
        }
    }
    Lmax = pasa_sup_norm (lambda, nrow) ;
    if ( PrintLevel > 1 )
    {
        printf ("Total: %e Relative: %e lmax: %e\n\n", Emax, Emax/Lmax, Lmax) ;
    }
}

/* ==========================================================================
   === pasa_bound_compress ==================================================
   ==========================================================================
    Check for new active bounds, and compress the problem when they are found.
    Only used during a partial step towards the projection point so potentially
    ib does not correspond to the active constraints at the current iterate.
    This is only employed when use_pproj is TRUE.
   ========================================================================== */
int pasa_bound_compress /* return:
                                   PASA_NEW_ACTIVE
                                   PASA_TRY_PROJECT2
                                   PASA_NO_CHANGE_IN_ACTIVE   */
(
    PASAcom    *Com  /* common variables */
)
{
    int *ib ;
    PASAINT change_in_active, cols, j, k, nc, ncol, ni, row,
           *bound_cols, *ir, *ineq_row ;
    PASAFLOAT *lo, *hi, *x ;
    PPprob *Prob ;
    PPwork *Work ;

    const int PrintLevel = Com->pasaparm->PrintLevel ;
    const int loExists = Com->loExists ;
    const int hiExists = Com->hiExists ;
    if ( PrintLevel >= 2 )
    {
        printf ("bound compress\n") ;
    }

    if ( Com->Aexists == FALSE )
    {
        return (PASA_NO_CHANGE_IN_ACTIVE) ;
    }
    /* otherwise Aexists */


    Prob = Com->ppcom->Prob ;
    ncol = Prob->ncol ;
    ni = Prob->ni ;
    ineq_row = Prob->ineq_row ;

    Work = Com->ppcom->Work ;
    ib = Work->ib ;
    ir = Work->ir ;

    lo = Com->lo ;
    hi = Com->hi ;
    x = Com->x ;

    change_in_active = FALSE ;
    if ( Com->Bounds == FALSE )
    {
        cols = ncol ; /* no bound constraints are present */
    }
    else
    {
        /* check for new active bounds */
        for (cols = 0; cols < ncol; cols++)
        {
            if ( ib [cols] != 0 )
            {
                /* if a bound was active at the projection point,
                   then change_in_active is TRUE */
                change_in_active = TRUE ;
                if ( ib [cols] < 0 )
                {
                    if ( x [cols] == lo [cols] )
                    {
                        break ;
                    }
                }
                else if ( x [cols] == hi [cols] )
                {
                    break ;
                }
            }
        }
    }
    if ( cols == ncol )
    {
        if ( change_in_active == FALSE )
        {
            /* check whether an inequality became active */
            for (k = 1; k <= ni; k++)
            {
                row = ineq_row [k] ;
                if ( ir [row] <= ni ) /* active inequality */
                {
                    break ;
                }
            }
            if ( k <= ni ) /* an inequality became active */
            {
                /* Project the current iterate onto the feasible set.
                   After this projection, the current factorization
                   corresponds to the current iterate. */
                return (PASA_TRY_PROJECT2) ;
            }
            /* otherwise, there was no change in the active set */
            return (PASA_NO_CHANGE_IN_ACTIVE) ;
        }
        else
        {
            /* else, there was a change in the active bounds, try project2 */
            return (PASA_TRY_PROJECT2) ;
        }
    }

    {
        int blks ;
        PASAINT blk, i, iri, l, nrow, pp, p, q,
               *Ap, *Ai, *Anz, *ATp, *ATi, *colmap, *col_start, *ifree ;
        PASAFLOAT *Ax, *ATx, *b, *bl, *bu, *g ;
        nrow = Com->nrow ;
        colmap = Work->arrayi ; /* borrow temporary space from pproj */
        ifree = Com->ifree ;
        g = Com->g ;
        b  = Com->b ;
        bl = Com->bl ;
        bu = Com->bu ;
        nc = Com->nc ;
        bound_cols = Com->bound_cols ;
        Ap = Prob->Ap ;
        Ai = Prob->Ai ;
        Ax = Prob->Ax ;
        Anz = Prob->Anz ;
        ATp = Work->ATp ;
        ATi = Work->ATi ;
        ATx = Work->ATx ;
#ifndef NDEBUG
        pasa_check_feas (Com) ;
#endif

        /* cols < ncol => at least one constraint is activated.
           delete from A the columns associated with bound variables
           and update the start of each column in the multilevel
           decomposition. cols will count the number of remaining columns in A*/
        blks = Work->blks ;
        col_start = Work->col_start ;

        /* the initial columns are mapped to themselves */
        for (i = 0; i < cols; i++)
        {
            colmap [i] = i ;
        }
        /* When columns are deleted, the column start of each block is
           reduced by the number of proceeding columns that were deleted.
           Find the first block with column start > j. The following code
           terminates since col_start [blks] = number of columns
           in matrix. */
        for (blk = 1; blk <= blks; blk++)
        {
            if ( col_start [blk] > cols )
            {
                break ;
            }
        }
        l = col_start [blk] ;

        /* start of storage for this first dropped column, k increments
           over the compressed matrix, while p increments over the
           original matrix */
        p = k = Ap [cols] ;
        for (j = cols; j < ncol; j++)
        {
            if ( j == l )
            {
                col_start [blk] = cols ;
                blk++ ;
                while ( col_start [blk] == l )
                {
                    col_start [blk] = cols ;
                    blk++ ;
                }
                l = col_start [blk] ;
            }
            q = Ap [j+1] ;
            /* retained column in the following cases: */
            if ( (ib [j] == 0) || ((ib [j] < 0) && (x [j] != lo [j]))
                               || ((ib [j] > 0) && (x [j] != hi [j])) )
            {
                /* retain associated x, g, lo, hi, and ifree entries */
                x [cols] = x [j] ;
                g [cols] = g [j] ;
                if ( loExists ) lo [cols] = lo [j] ;
                if ( hiExists ) hi [cols] = hi [j] ;
                ib[cols] = ib[j] ;
                ifree [cols] = ifree [j] ;
                colmap [j] = cols ;
                Anz [cols] = Anz [j] ;

                /* save column j in matrix */
                Ap [cols+1] = Ap [cols] + q - p ;
                cols++ ;
                for (; p < q; p++)
                {
                    Ai [k] = Ai [p] ;
                    Ax [k] = Ax [p] ;
                    k++ ;
                }
            }
            else /* bound column */
            {
                i = ifree [j] ;
                PASAFLOAT const xj = x [j] ;
                Com->userx [i] = xj ;
                colmap [j] = -1 ;
                if ( ib [j] > 0 )
                {
                    bound_cols [nc] = i ;
                }
                else
                {
                    bound_cols [nc] = -(i + 1) ;
                }
                nc++ ;
                /* The replacement column is free in the reduced matrix,
                   and the right side of constraints is adjusted */
                for (; p < q; p++)
                {
                    i = Ai [p] ;
                    PASAFLOAT const Axj = Ax [p]*xj ;
                    iri = ir [i] ;
                    if ( iri == 0 ) /* equality constraint */
                    {
                        b [i] -= Axj ;
                    }
                    else            /* inequality constraint */
                    {
                        if ( iri < 0 )
                        {
                            iri = -iri ;
                        }
                        else if ( iri > ni )
                        {
                            iri -= ni ;
                        }
                        bl [iri] -= Axj ;
                        bu [iri] -= Axj ;
                    }
                }
            }
        }
        Prob->ncol = cols ;
        Work->A->ncol = cols ;
        Work->A->nzmax = Ap [cols] ;
        Com->nf = cols ;
        Com->nc = nc ;
        col_start [blk] = cols ;
        while ( blk < blks )
        {
            blk++ ;
            col_start [blk] = cols ;
        }

        if ( PrintLevel >= 1 )
        {
            printf ("new bounds activated: %ld "
                    "(free before: %ld after: %ld)\n",
                   (LONG) (ncol-cols), (LONG) ncol, (LONG) cols);
        }
        /* Update AT in one pass by exploiting colmap, the
           map from the columns of the original matrix to the
           to the columns in the compressed matrix
           (col = -1 if the column was deleted). */
        pp = 0 ;
        p = 0 ;
        for (i = 0; i < nrow; i++)
        {
            q = ATp [i+1] ;
            for (; p < q; p++)
            {
                k = colmap [ATi [p]] ;
                if ( k >= 0 ) /* keep the element */
                {
                    ATi [pp] = k ;
                    ATx [pp] = ATx [p] ;
                    pp++ ;
                }
            }
            ATp [i+1] = pp ;
        }
        pasa_copyi (Work->AFTp, ATp, nrow+1) ;
        return (PASA_NEW_ACTIVE) ;
    }
}

/* ==========================================================================
   === pasa_compress_prob ===================================================
   ==========================================================================
    Check for new active bounds and inequalities, and compress the problem
    when they are found. This is only called from activeGP.
   ========================================================================== */
int pasa_compress_prob /* return:
                                  PASA_NEW_ACTIVE
                                  PASA_NO_CHANGE_IN_ACTIVE
                                  PASA_GRAD_PROJ */
(
    int local_error, /* TRUE => compute local error */
    PASAcom    *Com  /* common variables */
)
{
    int status ;
    PASAINT nc, nf, *bound_cols, *ifree ;
    PASAFLOAT *lo, *hi, *g, *x ;
    const int loExists = Com->loExists ;
    const int hiExists = Com->hiExists ;
    lo = Com->lo ;
    hi = Com->hi ;
    g = Com->g ;
    x = Com->x ;
    ifree = Com->ifree ;
    bound_cols = Com->bound_cols ;
    nc = Com->nc ;

    const int PrintLevel = Com->pasaparm->PrintLevel ;
    if ( PrintLevel >= 1 )
    {
        printf ("compress, nc: %ld\n", (LONG) nc) ;
    }

    if ( Com->use_napheap == TRUE )
    {
        int DelCol, AddRow ;
        PASAINT cols, i, j ;
        PASAFLOAT a2, s, *a ;
        a = Com->nap_a ;
        DelCol = FALSE ;
        AddRow = FALSE ;
        if ( abs (Com->nap_constraint) == 1 )
        {
            if ( Com->nap_bl != Com->nap_bu )
            {
                AddRow = TRUE ;
                Com->nr = 1 ;
                Com->bound_rows [0] = Com->nap_constraint ;
                if ( Com->nap_constraint > 0 )
                {
                    Com->nap_bl = Com->nap_bu ;
                }
                else
                {
                    Com->nap_bu = Com->nap_bl ;
                }
            }
        }
        nf = Com->nf ;
        for (cols = 0; cols < nf; cols++)
        {
            if ( (loExists && (x [cols] == lo [cols])) ||
                 (hiExists && (x [cols] == hi [cols])) )
            {
                break ;
            }
        }
        if ( cols < nf ) /* at least one bound constraint is activated */
        {
            a2 = s = PASAZERO ;
            for (j = cols; j < nf; j++)
            {
                DelCol = TRUE ;
                const PASAFLOAT xj = x [j] ;
                const PASAFLOAT aj = a [j] ;
                /* a bound xj is stored in userx and removed from the problem */
                if ( (loExists && (xj == lo [j])) ||
                     (hiExists && (xj == hi [j])) )
                {
                    i = ifree [j] ;
                    Com->userx [i] = xj ;
                    /* save the indices for the bound xj */
                    if ( xj == lo [j] )
                    {
                        bound_cols [nc] = -(i + 1) ;
                    }
                    else
                    {
                        bound_cols [nc] = i ;
                    }
                    nc++ ;

                    /* update the bounds for the linear inequality */
                    s += a [j]*xj ;
                    a2 += aj*aj ;
                }
                else /* xj is free and retained */
                {
                    /* retain associated x, g, lo, hi, and ifree entries */
                    x [cols] = xj ;
                    g [cols] = g [j] ;
                    a [cols] = aj ;
                    if ( loExists ) lo [cols] = lo [j] ;
                    if ( hiExists ) hi [cols] = hi [j] ;
                    ifree [cols] = ifree [j] ;
                    cols++ ;
                }
            }
            nf = cols ;
            Com->nf = nf ;
            Com->nc = nc ;
            Com->nap_bl -= s ;
            Com->nap_bu -= s ;
            if ( PrintLevel >= 1 )
            {
                printf ("nf: %ld nc: %ld\n", (LONG) nf, (LONG) nc) ;
            }
        }
        if ( (AddRow == TRUE) || (DelCol == TRUE) )
        {
            if ( AddRow == TRUE )
            {
                Com->nap_a2save = Com->nap_a2 = pasa_dot (a, a, nf) ;
            }
            else if ( DelCol == TRUE )
            {
                Com->nap_a2 -= a2 ;
                /* recompute ||a||^2 when it drops by the given factor */
                if ( a2 <= .01*Com->nap_a2save )
                {
                    Com->nap_a2save = Com->nap_a2 = pasa_dot (a, a, nf) ;
                }
            }
        }

        /* if local_error is TRUE, then estimate the local error and
           determine where to branch to */
        if ( local_error == TRUE )
        {
            /* if all bounds are active, then switch to grad_proj */
            if ( cols == 0 )
            {
                Com->e = PASAZERO ;
                return (PASA_GRAD_PROJ) ;
            }
            /* estimate the local error by projecting gradient into null
               space associated with current active constraints */
            pasa_null_project (NULL, g, NULL, TRUE, Com) ;

            /* Test if the local error criterion is satisfied. */
            if ( Com->e < Com->testtol )
            {
                status = pasaAG_checktol (Com) ;
                /* switch to gradient projection algorithm */
                if ( status != PASA_OK )
                {
                    return (status) ;
                }
            }
        }
        if ( (AddRow == TRUE) || (DelCol == TRUE) )
        {
            return (PASA_NEW_ACTIVE) ;
        }
        else
        {
            return (PASA_NO_CHANGE_IN_ACTIVE) ;
        }
    }
    else if ( Com->use_pproj == TRUE )
    {
        int blks, *ib, *sol_to_blk ;
        PASAINT blk, cols, iri, i, j, k, l, nr, ni, ncol, nrow, pp, p, q, row,
                *Ap, *Ai, *Anz, *ATp, *ATi, *bound_rows, *colmap,
               *col_start, *ir, *ineq_row ;
        PASAFLOAT t, *Ax, *ATx, *b, *bl, *bu ;
        PPprob *Prob ;
        PPwork *Work ;

        Prob = Com->ppcom->Prob ;
        Work = Com->ppcom->Work ;
        nrow = Com->nrow ;
        colmap = Work->arrayi ; /* borrow temporary space from pproj */
        ifree = Com->ifree ;
        ib = Work->ib ;
        ir = Work->ir ;
        ineq_row = Prob->ineq_row ;
        b  = Com->b ;
        bl = Com->bl ;
        bu = Com->bu ;
        Ap = Prob->Ap ;
        Ai = Prob->Ai ;
        Ax = Prob->Ax ;
        Anz = Prob->Anz ;
        ATp = Work->ATp ;
        ATi = Work->ATi ;
        ATx = Work->ATx ;
        sol_to_blk = Work->sol_to_blk ;
        blks = Work->blks ;
        bound_rows = Com->bound_rows ;
        /* Check to see if an inactive inequality constraint became active.
           If so, then make it an equality constraint and remove it from
           the list of inequality constraints. */
        ni = Prob->ni ;
        nr = Com->nr ;
        PASAINT newrows = 0 ;
        for (k = 1; k <= ni; k++)
        {
            row = ineq_row [k] ;
            if ( ir [row] <= ni ) /* active inequality */
            {
                /* shift bl and bu down if newrows > 0 */
                if ( newrows > 0 )
                {
                    for (; j < k; j++)
                    {
                        bl [j-newrows] = bl [j] ;
                        bu [j-newrows] = bu [j] ;
                        sol_to_blk [j-newrows] = sol_to_blk [j] ;
                    }
                }
                j = k + 1 ;
                if ( ir [row] > 0 )
                {
                    b [row] = bu [k] ;
                    bound_rows [nr] = row + 1 ;
                }
                else
                {
                    b [row] = bl [k] ;
                    bound_rows [nr] = -(row+1) ;
                }
                ir [row] = 0 ;
                nr++ ;
                newrows++ ;
            }
            else if ( newrows > 0 ) /* keep the inactive row */
            {
                ineq_row [k-newrows] = row ;
                ir [row] -= newrows ; /* adjust for number of deleted rows */
            }
        }

        if ( newrows > 0 )
        {
            if ( PrintLevel >= 1 )
            {
                printf ("%ld new inequalities activated\n", (LONG) newrows) ;
                if ( PrintLevel >= 3 )
                {
                    printf ("The active rows are:\n") ;
                    for (i = 0; i < nr; i++)
                    {
                        if ( bound_rows [i] > 0 )
                        {
                            printf ("%10ld upper\n", (LONG) bound_rows [i] - 1);
                        }
                        else
                        {
                            printf ("%10ld lower\n", (LONG) -(bound_rows[i]+1));
                        }
                    }
                }
            }
            /* the remaining inequalities are inactive, shift down */
            for (; j <= ni; j++)
            {
                bl [j-newrows] = bl [j] ;
                bu [j-newrows] = bu [j] ;
                sol_to_blk [j-newrows] = sol_to_blk [j] ;
            }
            sol_to_blk [j-newrows] = Work->blks ;
            Com->nr = nr ;
            ni -= newrows ;
            Prob->ni = ni ;
            for (k = 1; k <= ni; k++)
            {
                ir [ineq_row [k]] -= newrows ;
            }
        }

#ifndef NDEBUG
        pasa_check_feas (Com) ;
#endif

        /* Check for new active bounds. If they are found,
           then delete from A the columns associated with bound variables
           and update the start of each column in the multilevel
           decomposition. */
        ncol = Prob->ncol ;
        for (cols = 0; cols < ncol; cols++)
        {
            if ( ib [cols] != 0 )
            {
                break ;
            }
        }
        if ( cols < ncol ) /* at least one constraint is activated */
        {
            /* cols will count the number of remaining columns in A */
            blks = Work->blks ;
            col_start = Work->col_start ;

            /* the initial columns are mapped to themselves */
            for (i = 0; i < cols; i++)
            {
                colmap [i] = i ;
            }
            /* When columns are deleted, the column start of each block is
               reduced by the number of proceeding columns that were deleted.
               Find the first block with column start > j. The following code
               terminates since col_start [blks] = number of columns
               in matrix. */
            for (blk = 1; blk <= blks; blk++)
            {
                if ( col_start [blk] > cols )
                {
                    break ;
                }
            }
            l = col_start [blk] ;

            /* start of storage for this first dropped column, k increments
               over the compressed matrix, while p increments over the
               original matrix */
            p = k = Ap [cols] ;
            for (j = cols; j < ncol; j++)
            {
                if ( j == l )
                {
                    col_start [blk] = cols ;
                    blk++ ;
                    while ( col_start [blk] == l )
                    {
                        col_start [blk] = cols ;
                        blk++ ;
                    }
                    l = col_start [blk] ;
                }
                q = Ap [j+1] ;
                if ( ib [j] == 0 ) /* retained column */
                {
                    /* retain associated x, g, lo, hi, and ifree entries */
                    x [cols] = x [j] ;
                    g [cols] = g [j] ;
                    if ( loExists ) lo [cols] = lo [j] ;
                    if ( hiExists ) hi [cols] = hi [j] ;
                    ifree [cols] = ifree [j] ;
                    colmap [j] = cols ;
                    Anz [cols] = Anz [j] ;

                    /* save column j in matrix */
                    Ap [cols+1] = Ap [cols] + q - p ;
                    cols++ ;
                    for (; p < q; p++)
                    {
                        Ai [k] = Ai [p] ;
                        Ax [k] = Ax [p] ;
                        k++ ;
                    }
                }
                else /* bound column */
                {
                    i = ifree [j] ;
                    Com->userx [i] = x [j] ;
                    colmap [j] = -1 ;
                    if ( ib [j] > 0 )
                    {
                        bound_cols [nc] = i ;
                    }
                    else
                    {
                        bound_cols [nc] = -(i + 1) ;
                    }

                    nc++ ;
                    /* The replacement column is free in the reduced matrix,
                       and the right side of constraints is adjusted */
                    ib [j] = 0 ;
                    PASAFLOAT const xj = x [j] ;
                    for (; p < q; p++)
                    {
                        i = Ai [p] ;
                        iri = ir [i] ;
                        t = Ax [p]*xj ;
                        if ( iri == 0 ) /* equality constraint */
                        {
                            b [i] -= t ;
                        }
                        else /* inactive inequality, above ir = 0 if active */
                        {
                            iri -= ni ;
                            bl [iri] -= t ;
                            bu [iri] -= t ;
                        }
                    }
                }
            }
            Prob->ncol = cols ;
            Work->A->ncol = cols ;
            Work->A->nzmax = Ap [cols] ;
            Com->nf = cols ;
            Com->nc = nc ;
            col_start [blk] = cols ;
            while ( blk < blks )
            {
                blk++ ;
                col_start [blk] = cols ;
            }

            if ( PrintLevel >= 1 )
            {
                printf ("new bounds activated: %ld "
                        "(free before: %ld after: %ld)\n",
                        (LONG) (ncol-cols), (LONG) ncol, (LONG) cols);
            }
            /* Update AT in one pass by exploiting colmap, the
               map from the columns of the original matrix to the
               to the columns in the compressed matrix
               (col = -1 if the column was deleted). */
            pp = 0 ;
            p = 0 ;
            for (i = 0; i < nrow; i++)
            {
                q = ATp [i+1] ;
                for (; p < q; p++)
                {
                    k = colmap [ATi [p]] ;
                    if ( k >= 0 ) /* keep the element */
                    {
                        ATi [pp] = k ;
                        ATx [pp] = ATx [p] ;
                        pp++ ;
                    }
                }
                ATp [i+1] = pp ;
            }
            pasa_copyi (Work->AFTp, ATp, nrow+1) ;
        }

        /* if local_error is TRUE, then estimate the local error and
           determine where to branch to */
        if ( local_error == TRUE )
        {
            /* if all bounds are active, then switch to grad_proj */
            if ( cols == 0 )
            {
                Com->e = PASAZERO ;
                return (PASA_GRAD_PROJ) ;
            }
            /* estimate the local error by projecting gradient into null
               space associated with current active constraints */
            pasa_null_project (NULL, g, NULL, TRUE, Com) ;

            /* Test if the local error criterion is satisfied. */
            if ( Com->e < Com->testtol )
            {
                if ( PrintLevel >= 1 )
                {
                    printf ("local error: %e < testtol: %e\n",
                             Com->e, Com->testtol) ;
                }
                status = pasaAG_checktol (Com) ;
                /* switch to gradient projection algorithm */
                if ( status != PASA_OK )
                {
                    return (status) ;
                }
            }
        }
        if ( (cols < ncol) || (newrows > 0) )
        {
            return (PASA_NEW_ACTIVE) ;
        }
        else
        {
            return (PASA_NO_CHANGE_IN_ACTIVE) ;
        }
    }
    /* at the top of activeGP, only bound constraints present, ifree is set  */
    else if ( (Com->location == PASA_GRAD_PROJ) ||
              (Com->location == PASA_BASE_CODE) )
    {
        PASAINT j, ncol ;
        ncol = Com->ncol ;
        for (j = 0; j < ncol; j++)
        {
            /* variable at lower bound */
            if ( loExists && (x [j] == lo [j])
                          && (g [j] >= PASAZERO) )
            {
                Com->userx [ifree [j]] = lo [j] ;
                bound_cols [nc] = -(j+1) ;
                nc++ ;
            }
            /* variable at upper bound */
            else if ( hiExists && (x [j] == hi [j])
                               && (g [j] <= PASAZERO) )
            {
                Com->userx [ifree [j]] = hi [j] ;
                bound_cols [nc] = j ;
                nc++ ;
            }
            else
            {
                if ( nc > 0 )
                {
                    ifree [j-nc] = ifree [j] ;
                    x [j-nc] = x [j] ;
                    g [j-nc] = g [j] ;
                    if ( loExists ) lo [j-nc] = lo [j] ;
                    if ( hiExists ) hi [j-nc] = hi [j] ;
                }
            }
        }
        if ( PrintLevel >= 1 )
        {
            printf ("compress at top of active GP, final nc: %ld\n", (LONG) nc);
        }
        if ( nc > 0 )
        {
            Com->nf -= nc ;
            Com->nc = nc ;
            return (PASA_NEW_ACTIVE) ;
        }
        else
        {
            return (PASA_NO_CHANGE_IN_ACTIVE) ;
        }
    }
    else /* only bound constraints, coming from bottom part of active GP */
    {
        int search ;
        PASAINT i, i0, i1, j, k, l, nc_temp ;
        nc_temp = Com->nc_temp ;
        if ( nc == nc_temp )
        {
            return (PASA_NO_CHANGE_IN_ACTIVE) ;
        }

        if ( (Com->nf == Com->ncol) && (Com->fixed == FALSE) )
        {
            search = FALSE ;
        }
        else
        {
            search = TRUE ;
        }
        /* i0 = first available location
            i = element to delete
           i1 = first element to move */
        i0 = -1 ; /* indicates the start of search */
        i = 0 ;   /* look for first delete element starting at i = 0 */
        i1 = 1 ;
        if ( PrintLevel >= 1 )
        {
            printf ("compress in bottom half of active GP\n") ;
            printf ("search: %i, nc_temp: %ld\n", search, (LONG) nc_temp) ;
        }
        for (; nc <= nc_temp; nc++)
        {
            if ( nc < nc_temp )
            {
            /* variable to compress in user coordinates */
                k = j = bound_cols [nc] ;
                if ( j < 0 )
                {
                    j = -(j+1) ;
                }

                /* find the pasa index corresponding to j */
                if ( search == FALSE ) /* ifree [j] = j for each j */
                {
                    i = j ;
                    ASSERT (ifree [j] == j) ;
                    if ( i0 >= 0 )
                    {
                        pasa_copyi (ifree+i0, ifree+i1, i-i1) ;
                    }
                }
                else if ( i0 < 0 )     /* search for first j index */
                {
                    while ( ifree [i] != j )
                    {
                        i++ ;
                    }
                }
                else                   /* search for subsequent j indices */
                {
                    l = i0 - i ;
                    while ( ifree [i] != j )
                    {
                        ifree [i+l] = ifree [i] ; /* move ifree down */
                        i++ ;
                    }
                }

                /* store the bound value in userx */
                if ( k < 0 )
                {
                    Com->userx [j] = lo [i] ;
                    if ( PrintLevel >= 2 )
                    {
                        printf ("lower bind %ld (userx) using %ld (pasa)\n",
                                 (LONG) j, (LONG) i) ;
                    }
                }
                else
                {
                    Com->userx [j] = hi [i] ;
                    if ( PrintLevel >= 2 )
                    {
                        printf ("upper bind %ld (userx) using %ld (pasa)\n",
                                 (LONG) j, (LONG) i) ;
                    }
                }
                /* determine number k of elements to move */
                k = i - i1 ;
            }
            else
            {
                k = Com->nf - i1 ;
                pasa_copyi (ifree+i0, ifree+i1, k) ;
            }
            if ( i0 < 0 ) /* first pass, do not copy */
            {
                i0 = i ;
                i++ ;
                i1 = i ;
            }
            else /* move stuff from locations i1 to i - 1
                            or from locations i1 to nf - 1 */
            {
                if ( k > 0 )
                {
                    if ( PrintLevel >= 2 )
                    {
                        printf ("do copy i0: %ld i1: %ld k: %ld\n",
                                 (LONG) i0, (LONG) i1, (LONG) k) ;
                    }
                    pasa_copyx (x+i0, x+i1, k) ;
                    pasa_copyx (g+i0, g+i1, k) ;
                    if ( loExists )
                    {
                        pasa_copyx (lo+i0, lo+i1, k) ;
                    }
                    if ( hiExists )
                    {
                        pasa_copyx (hi+i0, hi+i1, k) ;
                    }
                    i0 += k ;
                }
                i++ ;
                i1 = i ;
            }
        }
        Com->nf -= nc_temp - Com->nc ;
        Com->nc = nc_temp ;
        return (PASA_NEW_ACTIVE) ;
    }
#ifndef NDEBUG
    pasa_check_feas (Com) ;
#endif
}

/* =========================================================================
   ==== pasaAG_checktol ====================================================
   =========================================================================
   This routine is only called from the compress_prob routine.
   It is entered when the nominal stopping criterion
   Com->e <= switchfactor*Com->E is satisfied where Com->E is the
   current global error bound. The goal is to decide what
   is done next. There are four possibilities:

   1. Terminate with status = PASA_ERROR_TOLERANCE_SATISFIED if the global
      error is sufficiently small.
   2. Terminate AG and return to grad_proj where bound variables are freed.
   3. Continue the iteration.

   Recall that the optimization problem is

   min f(x) subject to lo <= x <= hi, bl <= Ax <= bu

   Let L(x, y) = f(x) + y'(Ax - b) be the Lagrangian where b_i = 0 if
   bl_i < (Ax)_i < bu_i, b_i = bu_i if (Ax)_i = bu_i, and b_i = bl_i otherwise.
   The first-order optimality conditions are that

       L'_j (x, y)  = 0 if lo_j < x_j < hi_j,
       L'_j (x, y) <= 0 if        x_j = hi_j,
       L'_j (x, y) >= 0 if lo_j = x_j,
           y_i      = 0 if bl_i < (Ax)_i < bu_i,
           y_i     <= 0 if bl_i = (Ax)_i
           y_i     >= 0 if        (Ax)_i = bu_i

   max {|L'_j(x, y)| : lo_j < x_j < hi_j} is stored in Com->e (computed
   previously in null_project).  The multiplier is chosen so that y_i = 0
   when bl_i < (Ax)_i < bu_i.  We let maxsignerr denote the maximum
   violation of the sign constraints on L'_j (x, y) and on the multiplier y.
   The estimate for the Lagrange multiplier was computed previously in
   null_project.  Our decision about what to do next is based on the
   following rules:

   A. If MAX (Com->e, maxsignerr) <= Com->grad_tol,
      then we terminate with status = PASA_ERROR_TOLERANCE_SATISFIED.
   B. If Com->e <= switchfactor*maxsignerr, then we branch to grad_proj.
      The rationale is that we wish to determine the active constraints
      at the solution and reduce maxsignerr.
   C. Otherwise, continue the active gradient projection iteration.

   NOTE: This routine is basically a simplified version of cg_checktol.
   ========================================================================= */
int pasaAG_checktol
(
    PASAcom    *Com  /* pasa com structure */
)
{
    PASAINT j, k, nc, nrow, *bound_cols, *RLinkUp ;
    PASAFLOAT E, maxsignerr, *userg, *lambda ;
    PASAparm *Parm ;      /* parameters for PASA */
    PPwork *Work ;

    Parm = Com->pasaparm ; /* parameters */
    const int PrintLevel = Parm->PrintLevel ;
    const int use_pproj = Com->use_pproj ;
    const int use_napheap = Com->use_napheap ;
    int Aexists = Com->Aexists ;

    if ( Com->QP )
    {
        userg = Com->gtot ;
    }
    else
    {
        userg = Com->userg ;
    }

    maxsignerr = PASAZERO ;
    nc = Com->nc ;
    bound_cols = Com->bound_cols ;
    nrow = Com->nrow ;
    if ( use_pproj == TRUE )
    {
        Work = Com->ppcom->Work ;
        RLinkUp = Work->RLinkUp ;
        /* the problem is effectively bound constrained if there are no
           active equations */
        if ( RLinkUp [nrow] == nrow )
        {
            Aexists = FALSE ;
        }
    }
    else if ( use_napheap == TRUE )
    {
        if ( (Com->nap_constraint == 0) || (Com->nap_a2 == PASAZERO) )
        {
            Aexists = FALSE ;
        }
    }

    if ( Aexists == FALSE ) /* only bound constraints present */
    {
        for (k = 0; k < nc; k++)
        {
            j = bound_cols [k] ;
            if ( j < 0 ) /* at lower bound */
            {
                j = -(j+1) ;
                maxsignerr = PASAMAX (maxsignerr, -userg [j]) ;
            }
            else
            {
                maxsignerr = PASAMAX (maxsignerr, userg [j]) ;
            }
        }
    }
    else if ( use_pproj == TRUE ) /* linear equations/inequalities present*/
    {
        PASAINT l, m, nr, p, q,
             *Ap, *Ai, *bound_rows, *invperm ;
        PASAFLOAT t, *Ax ;

        /* lambda was computed inside null_project */
        lambda = Com->lambda ;

        Ap = Com->Copy->Ap ;
        Ax = Com->Copy->Ax ;
        Ai = Com->Copy->Ai ;
        invperm = Com->invperm ;
        for (k = 0; k < nc; k++)
        {
            j = bound_cols [k] ;
            if ( j < 0 ) /* at lower bound */
            {
                m = -(j+1) ;      /* user indexing */
                l = invperm [m] ; /* convert from user index to pproj index */
            }
            else
            {
                m = j ;
                l = invperm [j] ;
            }
            t = PASAZERO ;
            q = Ap [l+1] ;
            for (p = Ap [l]; p < q; p++)
            {
                t -= lambda [Ai [p]]*Ax [p] ;
            }
            t += userg [m] ;
            if ( j < 0 ) /* variable at lower bound */
            {
                maxsignerr = PASAMAX (maxsignerr, -t) ;
            }
            else
            {
                maxsignerr = PASAMAX (maxsignerr, t) ;
            }
            if ( PrintLevel >= 2 )
            {
                if ( j < 0 )
                {
                    if ( t < PASAZERO )
                    {
                        printf("lower bound %ld (pproj index) wrong sign: %e\n",
                              (LONG) l, -t) ;
                    }
                }
                else
                {
                    if ( t > PASAZERO )
                    {
                        printf("upper bound %ld (pproj index) wrong sign: %e\n",
                              (LONG) l, t) ;
                    }
                }
            }
        }
        nr = Com->nr ;
        bound_rows = Com->bound_rows ;
        for (k = 0; k < nr; k++)
        {
            j = bound_rows [k] ;
            if ( j < 0 ) /* at lower bound */
            {
                m = -(j+1) ;      /* row index */
                maxsignerr = PASAMAX (maxsignerr, -lambda [m]) ;
            }
            else
            {
                m = j - 1 ;
                maxsignerr = PASAMAX (maxsignerr, lambda [m]) ;
            }
            if ( PrintLevel >= 2 )
            {
                t = lambda [m] ;
                if ( j < 0 )
                {
                    if ( t < PASAZERO )
                    {
                        printf ("lower ineqn %ld wrong sign: %e\n",
                               (LONG) m, -t) ;
                    }
                }
                else
                {
                    if ( t > PASAZERO )
                    {
                        printf ("upper ineqn %ld wrong sign: %e\n",
                               (LONG) m, t) ;
                    }
                }
            }
        }
    }
    else /* use_napheap = TRUE */
    {
        PASAINT m ;
        const PASAFLOAT Lambda = *(Com->lambda) ;
        for (k = 0; k < nc; k++)
        {
            j = bound_cols [k] ;
            if ( j < 0 ) /* at lower bound */
            {
                m = -(j+1) ;      /* user indexing */
            }
            else
            {
                m = j ;
            }
            const PASAFLOAT t = userg [m] - Lambda*Com->nap_auser [m] ;
            if ( j < 0 ) /* variable at lower bound */
            {
                maxsignerr = PASAMAX (maxsignerr, -t) ;
                if ( (PrintLevel >= 2) && (t < PASAZERO) )
                {
                    printf("lower bound %ld (user index) wrong sign: %e\n",
                          (LONG) m, -t) ;
                }
            }
            else         /* variable at upper bound */
            {
                maxsignerr = PASAMAX (maxsignerr, t) ;
                if ( (PrintLevel >= 2) && (t > PASAZERO) )
                {
                    printf("upper bound %ld (user index) wrong sign: %e\n",
                          (LONG) m, t) ;
                }
            }
        }
        if ( Com->nr > 0 )
        {
            if ( Com->bound_rows [0] < 0 ) /* at lower bound */
            {
                maxsignerr = PASAMAX (maxsignerr, -Lambda) ;
                if ( (PrintLevel >= 2) && (Lambda < PASAZERO) )
                {
                    printf ("lower ineqn %i wrong sign: %e\n", 0, -Lambda) ;
                }
            }
            else                           /* at upper bound */
            {
                maxsignerr = PASAMAX (maxsignerr, Lambda) ;
                if ( (PrintLevel >= 2) && (Lambda > PASAZERO) )
                {
                    printf ("upper ineqn %i wrong sign: %e\n", 0, Lambda) ;
                }
            }
        }
    }

    /* TEST A: If MAX (Com->e, maxsignerr) <= Com->grad_tol,
       then we terminate with status = PASA_ERROR_TOLERANCE_SATISFIED. */
    E = PASAMAX (maxsignerr, Com->e) ;
    if ( E < Com->E )
    {
        Com->E = E ;
        E *= Parm->switchfactor ;
        Com->testtol = PASAMAX (E, Com->grad_tol) ;
    }
    if ( PrintLevel >= 1 )
    {
        printf ("maxsignerr: %e Com->E: %e testtol: %e\n",
                 maxsignerr, Com->E, Com->testtol) ;
    }
    if ( Com->E <= Com->grad_tol )
    {
        if ( PrintLevel >= 1 )
        {
            printf ("checktol: A. Error tolerance satisfied\n") ;
        }
        return (PASA_ERROR_TOLERANCE_SATISFIED) ;
    }

    /* TEST B: If Com->e <= switchfactor*maxsignerr, then we branch
       to grad_proj.  The rationale is that we wish to determine the active
       constraints at the solution and reduce maxsignerr. */
    if ( Com->e <= Com->testtol )
    {
        if ( PrintLevel >= 1 )
        {
            printf ("AG checktol: B. Branch to grad_proj\n") ;
        }
        return (PASA_GRAD_PROJ) ;
    }

    /* TEST C. Otherwise, continue the active gradient projection iteration. */
    if ( PrintLevel >= 1 )
    {
        printf ("AG checktol: C. Continue AG\n") ;
    }
    return (PASA_OK) ;
}

/* ==========================================================================
   === pasa_update_bb =======================================================
   ==========================================================================
    A cycle version of the BB algorithm is employed. This means that we
    reuse the BB approximation bbk*I to the Hessian for one or more
    iterations before updating its value. This function attempts to update
    bbk using the BB formula (g_k - g_k-1)'(x_k - x_k-1)/||x_k - x_k-1||^2.
    If either the numerator is <= 0 or the denominator = 0, then there are
    two possible outcomes: If the BB cycle length has reached the maximum
    allowed cycle length, then bbk is reinitialized using the startup
    procedure of iteration 1. Before the bb cycle length is reached, the
    function returns -1, which means to retain the current bbk.
   ========================================================================== */
PASAINT pasa_update_bb /* return lambda_k */
(
    PASAcom    *Com  /* common variables */
)
{
    PASAINT k, n, nf ;
    PASAFLOAT dk, sy, ss, normg, normx, f, fnew, sy1, sy2, t,
              *x, *d, *g, *xnew, *gnew ;
    PASAparm *Parm ;
    PASAstat *Stat ;

    Parm = Com->pasaparm ;
    Stat = Com->pasastat ;
    x = Com->x ;
    g = Com->g ;

    n = Com->ncol ;
    nf = Com->nf ; /* number of free variables */

    /* At startup, we use the given bbk if provided, otherwise we
       estimate bbk using ||x|| and ||g|| */
    if ( (Stat->gpit == 0) && (Stat->agpit == 0) && (Com->cgstat->iter == 0) )
    {
        if ( Parm->bbk == PASAZERO )
        {
            normg = pasa_sup_norm (g, n) ;    /* ||g_k||_inf */
            if ( normg == PASAZERO )
            {
                return (PASA_ERROR_TOLERANCE_SATISFIED) ;
            }
            normx = pasa_sup_norm (x, n) ;    /* ||x_k||_inf */
            /* check if x = 0 */
            if ( normx > PASAZERO )
            {
                t = PASAMAX (Parm->lambda0Factor*normx/normg, Parm->lambda0) ;
            }
            else /* x = 0 */
            {
                t = PASAMAX (PASAONE/normg, Parm->lambda0) ;
            }
            Com->bbk = t ;
        }
        else
        {
            Com->bbk = Parm->bbk ;
        }
        /* stimulate a new evaluation of the BB parameter */
        Com->its_in_cycle = Parm->NominalCycle ;
        return (PASA_OK) ;
    }

    if ( Com->first == TRUE )
    {
        /* Reuse the previous choice for the BB parameter.
           Next iteration, we want to try to compute a new value
           for the BB parameter, so we inialize its_in_cycle =
           NominalCycle. */
        Com->its_in_cycle = Parm->NominalCycle ;
        return (PASA_OK) ;
    }

    t = -PASAONE ; /* initialize BB parameter */

    /* Try to compute a new value for the BB parameter in any of the
       following cases: 1. The iteration did not employ a unit step.
                        2. The number of iterations in the current cycle
                           exceeds the nominal cycle length. */
    Com->its_in_cycle++ ;
    if ( (Com->alpha < PASAONE) || (Com->its_in_cycle >= Parm->NominalCycle) )
    {
        xnew = Com->xnew ;
        gnew = Com->gnew ;
        d = Com->d ;
        sy = PASAZERO ;
        ss = PASAZERO ;
        for (k = 0; k < nf; k++)
        {
            dk = d [k] ;                     /* dk = kth component of d */
            if ( dk != PASAZERO )
            {
                ss += dk*dk ;                /* ss = ||s||^2 (neglect alpha)*/
                sy += dk*(gnew [k] - g [k]) ;/* update sy (BB denominator)*/
            }
        }
        ss *= Com->alpha ;      /* adjust for neglected alpha */

        /* check if sty is nonpositive OR if ss = 0 */
        if ( sy <= PASAZERO || ss == PASAZERO )
        {
            if ( Parm->PrintLevel > 0 )
            {
                printf ("BB step: sy = %e <= 0 or ss = %e\n", sy, ss) ;
            }
            /* check if current cycle >= MaximumCycle */
            if ( Com->its_in_cycle >= Parm->MaximumCycle )
            {
                if ( Parm->PrintLevel > 0 )
                {
                    printf ("its_in_cycle = %i >= MaximumCycle = %i\n",
                             Com->its_in_cycle, Parm->MaximumCycle) ;
                }
                normg = pasa_sup_norm (gnew, nf) ;    /* ||gnew_k||_inf */
                if ( normg == PASAZERO )
                {
                    return (PASA_ERROR_TOLERANCE_SATISFIED) ;
                }
                normx = pasa_sup_norm (xnew, nf) ;    /* ||xnew_k||_inf */
                /* check if x = 0 */
                if ( normx > PASAZERO )
                {
                    t = PASAMAX(Parm->lambda0Factor*normx/normg, Parm->lambda0);
                }
                else /* x = 0 */
                {
                    t = PASAMAX (PASAONE/normg, Parm->lambda0) ;
                }
                if ( t < Parm->bbexpand*Com->bbk )
                {
                    t = Parm->bbexpand*Com->bbk ;
                }
            }
        }
        else /* lambda_k possibly updated by BB formula */
        {
            /* If the BB formula is not used in this iteration, then we want to
               try it in the next iteration. Hence, we inialize its_in_cycle =
               NominalCycle. Whenever we use the BB formula, we set
               its_in_cycle = 0. */
            Com->its_in_cycle = Parm->NominalCycle ;
            fnew = Com->fnew ;
            f    = Com->f ;
            /* If the relative change in the function value is small enough,
               then we always use the BB step */
            if ( fabs (f-fnew) < Parm->bbSwitchFactor*fabs (f) )
            {
                t = ss/sy ; /* s's/s'y */
                if ( t >= Parm->lambda0 )
                {
                    Com->its_in_cycle = 0 ;
                }
                else
                {
                    t = Parm->lambda0 ;
                }
                if ( Parm->PrintLevel > 0 )
                {
                    printf ("use BB formula for step: %e\n", t) ;
                }
            }
            else /* further investigate the convexity of f */
            {
                /* If function is not convex along the line segment between x
                   and xnew, then we do not use BB step. Above, we already
                   checked if the denominator sy < 0 in the BB formula.
                   In addition, we now consider additional tests for a loss
                   of convexity based on the formulas from COAP, 22 (2002),
                   pp. 103-109 by Dai and Yuan^2. Let sy1 and sy2 denote the
                   numerator of (2.7) and (2.10) respectively. If sy1 < 0 or
                   sy2 < 0, the function is not convex between x and xnew.
                   Also, if sy1 > 2*sy, then the function is not convex.
                   This is because
                       sy1 = 2*s_{k-1}'*
                       int_0^1 nabla^2 f(x_k + t(x_{k-1}-x_k))(1-t)dt s_{k-1}
                   while sy is the same thing but with the factor 2 deleted
                   as well as the (1-t) factor in the integral. */

                sy1 = 2.*((f - fnew)/Com->alpha + sy + Com->gtd) ;
                sy2 = 6.*(Com->gtd + (f - fnew)/Com->alpha) + 4.*sy ;

                if ( (sy1 > PASAZERO) && (sy2 > PASAZERO) && (sy1 <= 2*sy) )
                {
                    t = ss/sy ; /* s's/s'y */
                    if ( Parm->PrintLevel > 0 )
                    {
                        printf ("function passes convexity test, use BB "
                                "formula for step: %e\n", t) ;
                    }
                    if ( t >= Parm->lambda0 )
                    {
                        Com->its_in_cycle = 0 ;
                    }
                    else
                    {
                        t = Parm->lambda0 ;
                    }
                }
                else
                {
                    normx = pasa_sup_norm (xnew, nf) ;    /* ||xnew_k||_inf */
                    normg = pasa_sup_norm (gnew, nf) ;    /* ||gnew_k||_inf */
                    /* check if x = 0 */
                    if ( normx > PASAZERO )
                    {
                        t = PASAMAX(Parm->lambda0Factor*normx/normg,
                                    Parm->lambda0);
                    }
                    else /* x = 0 */
                    {
                        t = PASAMAX (PASAONE/normg, Parm->lambda0) ;
                    }
                    if ( Parm->PrintLevel > 0 )
                    {
                        printf ("function not convex, use %e\n", t) ;
                    }
                }
            }
        }
    }
    if ( t > PASAZERO )
    {
        Com->bbk = t ;
    }
    return (PASA_OK) ;
}

/* ==========================================================================
   === pasa_update_reference ================================================
   ==========================================================================
    The Armijo line search condition is satisfied when the function value
    is small enough relative to a reference function value fR. This function
    updates fR. The following internal variable are used in the update process:

    f_min(k)   = min { fj : j <= k}
    fmaxmin(k) = max { fi : j <= i <= k, fj = f_min(k), j as small as possible}
    f_max(k)   = max { fj : max(k-M, 0) < j <= k }, M = memory of func values
    fr(k)      = fmaxmin or f_max or fr(k-1) according to the rules below

    In the first iteration of a bb cycle, we return fr while in the
    subsequent iteration, we return the current function value fk. This
    implies that the subsequent iterations are monotone while the first
    is nonmonotone.
   ========================================================================== */
PASAFLOAT pasa_update_reference
(
    PASAcom   *Com /* common variables */
)
{
    PASAparm *Parm ;

    Parm = Com->pasaparm ;

    int k, memp ;
    PASAFLOAT f, fdelete, f_max, *fmem, fr, t ;

    if ( Parm->PrintLevel > 0 )
    {
        printf ("update reference\n") ;
    }
    f = Com->f ;
    if ( Com->first == TRUE ) /* do initialization */
    {
        /* it is assumed that update_reference is called after update_bb
           so we can set first = FALSE here */
        Com->fr = f ;
        Com->f_min = f ;
        Com->fmaxmin = f ;
        Com->f_max = f ;
        /* Parm->M prior function values stored in fmem */
        Com->fmem [0] = f ;
        Com->memp = 1 ;
        for (k = 1; k < Parm->M; k++)
        {
            Com->fmem [k] = -PASAINF ; /* initialize fmem [k] = -infinity */
        }
        Com->its_since_f_min_update = 0 ; /* count iters since f_min update */
        Com->nunit_steps = 0 ;
        return (f) ;
    }

    if ( Com->alpha == PASAONE )
    {
        Com->nunit_steps++ ;
    }
    else
    {
        Com->nunit_steps = 0 ;
    }

    /* update f_min and fmaxmin */
    if ( f < Com->f_min )
    {
        Com->f_min = f ;       /* set f_min = f_{k+1} */
        Com->fmaxmin = f ;     /* set fmaxmin = f_{k+1} */
        Com->its_since_f_min_update = 0 ;  /* count its since f_min update */
    }
    else /* else f_min value remains the same */
    {
        Com->its_since_f_min_update++ ;

        if ( f > Com->fmaxmin ) /* if fmaxmin < f, update its value */
        {
            Com->fmaxmin = f ; /* update fmaxmin value */
        }
    }

    /* update f_max */
    memp = Com->memp ;
    fmem = Com->fmem ;
    f_max = Com->f_max ;

    fdelete = fmem [memp] ;  /* fdelete = value removed from fmem */
    fmem [memp] = f ;       /* store new function value in fmem vector */
    memp++ ;                /* increase fmemp value */

    /* reset memp to 0 when it reaches M */
    if ( memp == Parm->M )
    {
        memp = 0 ;
    }
    Com->memp = memp ;

    if ( f > f_max ) /* update f_max */
    {
        f_max = f ; /* set f_max = new function value f */
    }
    else if ( f_max == fdelete ) /* need to search memory for max value */
    {
        f_max = -PASAINF ;
        for (k = 0; k < Parm->M; k++)
        {
            if ( fmem [k] > f_max )
            {
                f_max = fmem [k] ;
            }
        }
    }
    Com->f_max = f_max ;

    /* Update reference function value. First check if f_min value
       has been used L times */
    fr = Com->fr ;

    if ( Com->its_since_f_min_update == Parm->L )
    {
        fr = f_max ;
        Com->its_since_f_min_update = 0 ;   /* reset f_min iters counter */
        t = Com->fmaxmin - Com->f_min ;
        if ( t > PASAZERO )
        {
            t = (fr - Com->f_min)/t ;
            if ( t > Parm->gamma1 )
            {
                fr = Com->fmaxmin ; /* update reference function value */
            }
        }
    }

    /* check if number of unit steps exceeds parameter P AND if f_max > f */
    if ( (Com->nunit_steps > Parm->P) && (f_max > f) )
    {
        if ( (fr - f)/(f_max - f) > Parm->gamma2 )
        {
            fr = f_max ;    /* update reference function value */
        }
    }

    /* check if fr differs too much from f */
    if ( fr - f > Parm->gamma3*fabs(f) )
    {
         fr = f_max ;
    }

    if ( Parm->PrintLevel > 0 )
    {
        printf ("fr: %e f_max: %e fmaxmin: %e f_min: %e\n",
        fr, f_max, Com->fmaxmin, Com->f_min) ;
    }
    Com->fr = fr ;            /* save the new reference function value */

    if ( Com->its_in_cycle == 0 )
    {
        return (fr) ;
    }
    else
    {
        return (f) ;
    }
}

/* ==========================================================================
   === pasa_gradproj_step ===================================================
   ==========================================================================
    Project xk - stepsize*gk onto the polyhedron. The projection pk is
    stored in Com->xnew and the search direction dk = pk - xk is
    stored in Com->d. If A does not exist, then we also compute global and
    local errors.
   ========================================================================== */
int pasa_gradproj_step /* return:
                              PASA_OK
                              PASA_POLYHEDRON_INFEASIBLE
                              PASA_INVALID_LINEAR_CONSTRAINT_BOUNDS
                              PASA_OUT_OF_MEMORY_IN_PPROJ
                              PASA_SSOR_ASCENT_FAILURE_IN_PPROJ
                              PASA_SSOR_ITERATIONS_EXCEED_MAX_ITS_IN_PPROJ */
(
    PASAFLOAT stepsize,
    PASAcom       *Com  /* common data for PASA */
)
{
    int location, PrintLevel, status ;
    PASAINT i, j, k, ncol, nrow, nf, p, *Ap, *Ai, *ifree, *ir ;
    PASAFLOAT E, xj, *Ax, *d, *g, *lo, *hi, *x, *xnew, *rhs ;
    PASAparm *Parm ;
    PPprob *Prob ;
    const int loExists = Com->loExists ;
    const int hiExists = Com->hiExists ;

    Parm = Com->pasaparm ;
    PrintLevel = Parm->PrintLevel ;
    if ( PrintLevel >= 1 )
    {
        printf ("start gradproj_step\n") ;
    }
    nf = Com->nf ;
    ncol = Com->ncol ;
    d = Com->d ;
    xnew = Com->xnew ;
    x = Com->x ;
    g = Com->g ;
    location = Com->location ;

    /* when there is a single linear constraint and napheap can be used */
    if ( Com->use_napheap == TRUE )
    {
        NAPdata *napdata ;
        napdata = Com->pasadata->napdata ;
        /* put x - stepsize*g in d */
        pasa_step (d, x, g, -stepsize, nf) ;

        /* update the napdata structure */
        napdata->x      = xnew ;
        napdata->n      = Com->nf ;
        napdata->y      = d ;
        napdata->blo    = Com->nap_bl ;
        napdata->bhi    = Com->nap_bu ;
        /* Com->lambda stores the current estimate for the multiplier
           associated with the NLP we are trying to solve. The minus sign
           is due to the fact that the multiplier sign is flipped in napheap. */
        napdata->lambda = -*(Com->lambda) ;

        status = napheap (napdata) ;

        Com->pasastat->nproject++ ;
        /* flip sign of lambda for consistency with rest of code */
        Com->nap_lambda = -napdata->lambda ;
        pasa_step (d, xnew, x, -PASAONE, nf) ;

        /* compute derivative in the search direction */
        Com->gtd = pasa_dot (g, d, nf) ;

        /* check that d is a descent direction if PrintLevel >= 2 */
        if ( PrintLevel >= 2 )
        {
            if ( Com->gtd > PASAZERO )
            {
                printf ("search direction not a descent direction: %e\n",
                         Com->gtd);
            }
        }
        if ( status > 0 )
        {
            return (status) ;
        }

        if ( (stepsize == PASAONE) && (nf == ncol) )
        {
            Com->E = pasa_sup_norm (d, nf) ;
            Com->testtol = Parm->switchfactor*Com->E ;
            Com->testtol = PASAMAX (Com->testtol, Com->grad_tol) ;
        }
        else if ( location == PASA_GRAD_PROJ ) /* => nf = ncol */
        {
            Com->estE = pasa_sup_norm (d, ncol)/stepsize ;
            /* if stepsize <= 1, then ||d||/stepsize is an upper bound on the
               error */
            if ( stepsize <= 1 )
            {
                if ( Com->estE < Com->E )
                {
                    Com->E = Com->estE ;
                    Com->testtol = Com->E*Parm->switchfactor ;
                    Com->testtol = PASAMAX (Com->testtol, Com->grad_tol) ;
                }
            }
            else /* stepsize > 1, ||d|| is upper bound on error */
            {
                if ( stepsize*Com->estE < Com->E )
                {
                    Com->E = stepsize*Com->estE ;
                    Com->testtol = Parm->switchfactor * Com->E ;
                    Com->testtol = PASAMAX (Com->testtol, Com->grad_tol) ;
                }
            }
        }
        if ( PrintLevel >= 1 )
        {
            printf ("Global Error: %e testtol: %e\n", Com->E, Com->testtol);
        }
        return (PASA_OK) ;
    }

    /* If A does not exist, then column permutation of A does not enter
       into the analysis and the computation of local and global error and
       the step is simplified. */
    if ( Com->Aexists == FALSE )
    {
        PASAFLOAT e, gtd, s, t, gj, loj, hij ;
        PASAINT nc, *bound_cols ;

        e = PASAZERO ;
        E = PASAZERO ;
        gtd = PASAZERO ;

        /* If the problem is unconstrained, the computation trivializes */
        if ( Com->Bounds == FALSE )
        {
            Com->e = Com->E = pasa_sup_norm (g, ncol) ;
            gtd = -pasa_dot (g, g, ncol) ;
            pasa_step (xnew, x, g, -stepsize, ncol) ;
            pasa_scale (d, g, -PPONE, ncol) ;
            if ( PrintLevel >= 1 )
            {
                printf ("global error: %e local error: %e\n", Com->E, Com->e) ;
            }
            return (PASA_OK) ;
        }

        ifree = Com->ifree ;
        lo = Com->lo ;
        hi = Com->hi ;
        if ( nf == 0 ) /* all variables at bound, e = 0 xnew = x */
        {
            for (j = 0; j < ncol; j++)
            {
                gj = g [j] ;
                xj = x [j] ;
                t = xj - gj ;
                if ( loExists && (t < lo [j]) )
                {
                    gj = lo [j] - xj ;
                }
                else if ( hiExists && (t > hi [j]) )
                {
                    gj = hi [j] - xj ;
                }
                s = fabs (gj) ;
                if ( s > E )
                {
                    E = s ;
                }
            }
        }
        else if ( (location == PASA_GRAD_PROJ) || (location == PASA_BASE_CODE) )
        {
            for (j = 0; j < ncol; j++)
            {
                gj = g [j] ;
                s = -stepsize*gj ;
                xj = x [j] ;
                t = s + xj ;

                if ( loExists )
                {
                    loj = lo [j] ;
                }
                else
                {
                    loj = -PASAINF ;
                }

                if ( hiExists )
                {
                    hij = hi [j] ;
                }
                else
                {
                    hij = PASAINF ;
                }

                if ( t < loj )
                {
                    xnew [j] = loj ;
                    s = loj - xj ;
                }
                else if ( t > hij )
                {
                    xnew [j] = hij ;
                    s = hij - xj ;
                }
                else
                {
                    xnew [j] = t ;
                }
                gtd += s*gj ;
                d [j] = s ;

                /* free components enter into local error */
                if ( (xj > loj) && (xj < hij) )
                {
                    if ( fabs(gj) > e )
                    {
                        e = fabs(gj) ;
                    }
                }

                /* all components enter into global error */
                t = xj - gj ;
                if ( t <= loj )
                {
                    gj = loj - xj ;
                }
                else if ( t >= hij )
                {
                    gj = hij - xj ;
                }
                gj = fabs (gj) ;
                if ( gj > E )
                {
                    E = gj ;
                }
            }
        }
        else if ( location == PASA_ACTIVE_GP )
        {
            nc = Com->nc ;
            ASSERT (nc+nf == Com->ncol) ;
            bound_cols = Com->bound_cols ;
            for (j = 0; j < nf; j++)
            {
                gj = g [j] ;
                s = -stepsize*gj ;
                xj = x [j] ;
                t = s + xj ;
                if ( loExists ) loj = lo [j] ;
                else            loj = -PASAINF ;
                if ( hiExists ) hij = hi [j] ;
                else            hij = PASAINF ;
                if ( t < loj )
                {
                    xnew [j] = loj ;
                    s = loj - xj ;
                    bound_cols [nc] = -(ifree [j]+1) ;
                    nc++ ;
                }
                else if ( t > hij )
                {
                    xnew [j] = hij ;
                    s = hij - xj ;
                    bound_cols [nc] = ifree [j] ;
                    nc++ ;
                }
                else
                {
                    xnew [j] = t ;
                }
                gtd += s*gj ;
                d [j] = s ;

                /* free components enter into local error */
                if ( (xj > loj) && (xj < hij) )
                {
                    if ( fabs(gj) > e )
                    {
                        e = fabs(gj) ;
                    }
                }

                /* all components enter into global error */
                t = xj - gj ;
                if ( t < loj )
                {
                    gj = loj - xj ;
                }
                else if ( t > hij )
                {
                    gj = hij - xj ;
                }
                s = fabs (gj) ;
                if ( s > E )
                {
                    E = s ;
                }
            }
            Com->nc_temp = nc ;
        }
        Com->estE = Com->E = E ;
        E *= Parm->switchfactor ;
        Com->testtol = PASAMAX (E, Com->grad_tol) ;
        Com->e = e ;
        Com->gtd = gtd ;
        if ( PrintLevel >= 1 )
        {
            printf ("global error: %e local error: %e testtol: %e\n",
                     Com->E, Com->e, Com->testtol) ;
        }
        return (PASA_OK) ;
    }

    /* Now consider the case where A is not NULL.  The point
       xk - stepsize*gk is projected into the polyhedron.  This is
       equivalent to minimizing ||x - (xk - stepsize*gk)||.
       We first make the change of variables y = x - xk. The projection
       problem is to minimize ||y - (-stepsize*gk)|| subject to
       bl - A*xk <= A*y <= bu - A*xk, lo - xk <= y <= hi - xk. Since we
       have previously called pproj and its internal variables are
       present, we directly modify the bounds associated with the
       polyhedral constraint to reflect the bounds on y. First, we
       compute rhs = A*xk. */
    rhs = Com->ppcom->Work->arrayd ; /* work space from pproj */
    Prob = Com->ppcom->Prob ;
    ifree = Com->ifree ; /* free column relative to pproj's permutation */
    Ap = Prob->Ap ;
    Ax = Prob->Ax ;
    Ai = Prob->Ai ;
    nrow = Prob->nrow ;
    ncol = Prob->ncol ;

    /* the goal is to project -stepsize*gk onto the polyhedron */
    pasa_scale (Com->userg, g, -stepsize, ncol) ;
    pasa_step (Prob->lo, Com->lo, x, -PASAONE, ncol) ;
    pasa_step (Prob->hi, Com->hi, x, -PASAONE, ncol) ;
#ifndef NDEBUG
    PASAFLOAT *lo2 = NULL ;
    if ( loExists )
    {
        lo2 = (PASAFLOAT *) pasa_malloc (&status, ncol, sizeof (double)) ;
        pasa_copyx (lo2, Prob->lo, ncol) ;
    }
    PASAFLOAT *hi2 = NULL ;
    if ( hiExists )
    {
        hi2 = (PASAFLOAT *) pasa_malloc (&status, ncol, sizeof (double)) ;
        pasa_copyx (hi2, Prob->hi, ncol) ;
    }
#endif
    PASAFLOAT *absAx = rhs+nrow ; /* grab more workspace from pproj */
    pasa_initx (absAx, PASAZERO, nrow) ;
    pasa_initx (rhs, PASAZERO, nrow) ;
    p = 0 ;
    for (j = 0; j < ncol; j++)
    {
        PASAFLOAT const Xj = x [j] ;
        PASAINT const q = Ap [j+1] ;
        /* x [i] = variable in original prob corresponding to xj in
                   permuted problem */
        for (; p < q; p++)
        {
            PASAFLOAT const AXj = Ax [p]*Xj ;
            PASAINT const row = Ai [p] ;
            rhs [row] += AXj ;
            absAx [row] += fabs (AXj) ;
        }
    }
    Com->ppcom->Work->absAxk = pasa_sup_norm (absAx, nrow) ;
    /* adjust the upper and lower bounds for the constraints on Ay */
    ir = Com->ppcom->Work->ir ;
    k = 0 ;

    /* If the debugger is turned on, then save copies of the constraints
       in order to check later the accuracy of the solution generated by
       pproj. */
#ifndef NDEBUG
    PASAFLOAT *bl2 = (PASAFLOAT *) pasa_malloc (&status, nrow, sizeof (double));
    PASAFLOAT *bu2 = (PASAFLOAT *) pasa_malloc (&status, nrow, sizeof (double));
    Com->ppdataDEBUG->y    = Com->userg ; /* point to be projected */
    Com->ppdataDEBUG->lo   = lo2 ;
    Com->ppdataDEBUG->hi   = hi2 ;
    Com->ppdataDEBUG->bl   = bl2 ;
    Com->ppdataDEBUG->bu   = bu2 ;
    Com->ppdataDEBUG->ncol = Com->nf ;
    Com->ppdataDEBUG->Ap   = Com->Ap ;
    Com->ppdataDEBUG->Ai   = Com->Ai ;
    Com->ppdataDEBUG->Ax   = Com->Ax ;
#endif

    for (i = 0; i < nrow; i++) /* i corresponds to the permuted constraints */
    {
        if ( ir [i] == 0 )     /* equality constraint */
        {
            Prob->b [i] = Com->b [i] - rhs [i] ;
#ifndef NDEBUG
            bl2 [i] = Prob->b [i] ;
            bu2 [i] = Prob->b [i] ;
#endif
        }
        else                   /* strict inequality constraint */
        {
            k++ ;
            Prob->bl [k] = Com->bl [k] - rhs [i] ;
            Prob->bu [k] = Com->bu [k] - rhs [i] ;
#ifndef NDEBUG
            bl2 [i] = Prob->bl [k] ;
            bu2 [i] = Prob->bu [k] ;
#endif
        }
    }

    ASSERT (k == Prob->ni) ;

    if ( PrintLevel >= 2 )
    {
        printf ("projection in pasa_gradproj_step\n") ;
        printf ("sup-norm of g: %e stepsize: %e\n",
                 pasa_sup_norm(g, ncol), stepsize) ;
    }
    /* project Com->userg = -alpha*gk onto polyhedron */
    status = pasa_project (d, Com->userg, Com) ;
    if ( PrintLevel >= 2 )
    {
        printf ("sup-norm of d: %e sup-norm of lambda: %e absAx: %e\n",
                 pasa_sup_norm(d, ncol), pasa_sup_norm(Com->lambda, nrow),
                 Com->ppcom->Work->absAx) ;
    }
    /* if an error occurred, then break */
    if ( status > 1 )
    {
        return (status) ;
    }

#ifndef NDEBUG
    /* If debug is TRUE, then check that all rows in the active part of
       A are active. Also, compare solution generated by pproj to
       solution obtained with starting guess = 0 and no userI structure.

       To check that the algorithm does a fast return when the starting
       guess = computed solution, remove comments from the statements
       labelled "fast return" and change "if 1" to "if 0" where indicated
       below. */
    if ( Parm->debug == TRUE )
    {
        PASAFLOAT s, t, *Ax2, *comlo, *comhi, *combu, *combl, *dnew,
                 *lambdanew, *lambdaold ;
        PASAINT p0, *Ai2, *Anz, *w1, *w2 ;
        PPparm *parm ;
        PPstat *stat ;
        Anz = Prob->Anz ;
        for (j = 0; j < ncol; j++)
        {
            PASAINT const q = Ap [j] + Anz [j] ;
            for (p = Ap [j]; p < q; p++)
            {
                if ( ir [Ai [p]] > k ) /* row is dropped */
                {
                    printf ("row: %ld col: %ld in active part of A, but "
                            "row is dropped since ir = %ld > ni = %ld\n",
                            (LONG) Ai [p], (LONG) j, (LONG) ir [Ai [p]],
                            (LONG) k) ;
                    pasa_error (-1, __FILE__, __LINE__, "gradproj_step") ;
                }
            }
        }
        stat = Com->pprojstat ;
        comlo = Com->lo ;
        comhi = Com->hi ;
        combl = Com->bl ;
        combu = Com->bu ;

        /* change "if 1" to "if 0" to check !!fast return!! */
#if 1
#ifndef NOPPROJ
        Com->pprojstat = NULL ;
        Com->lo = lo2 ;
        Com->hi = hi2 ;
        Com->bl = bl2 ;
        Com->bu = bu2 ;

        /* for starting guess lambda = 0, need sorted columns */
        Ax2 = (double *) pasa_malloc (&status, Ap [ncol], sizeof (double)) ;
        Ai2 = (PASAINT *) pasa_malloc (&status, Ap [ncol], sizeof (PASAINT)) ;
        w1 = (PASAINT *) pasa_malloc (&status, nrow, sizeof (PASAINT)) ;
        w2 = (PASAINT *) pasa_malloc (&status, nrow, sizeof (PASAINT)) ;
        p = 0 ;
        for (j = 0; j < ncol; j++)
        {
            /* sort column j */
            pproj_iminsort (w1, Ai+Ap [j], w2, Ap [j+1] - Ap [j]) ;
            p0 = p ;
            PASAINT const q = Ap [j+1] ;
            for (; p < q; p++)
            {
                i = w1 [p-p0] ;
                Ax2 [p] = Ax [p0+i] ;
                Ai2 [p] = Ai [p0+i] ;
            }
        }
        Com->Ax = Ax2 ;
        Com->Ai = Ai2 ;
#endif
#endif

        parm = Com->pprojparm ;
        Com->pprojparm = NULL ;
        /* Com->pprojparm = parm ;*/ /* !!fast return!! */
        dnew = (PASAFLOAT *) pasa_malloc (&status, ncol, sizeof (PASAFLOAT)) ;
        lambdanew = (PASAFLOAT *) pasa_malloc (&status, nrow,
                                               sizeof (PASAFLOAT)) ;
        lambdaold = Com->lambda ;
        /* pasa_copyx (lambdanew, lambdaold, nrow) ; */ /*!!fast return!!*/
        Com->lambda = lambdanew ;
        /* Com->lambda = lambdaold ;*/ /* !!fast return!! */
        /* ----------------------------------------- */
        status = pasa_project (dnew, Com->userg, Com) ;
        /* ----------------------------------------- */
        Com->pprojparm = parm ;
        Com->pprojstat = stat ;
        Com->Ax = Ax ;
        Com->Ai = Ai ;

        Com->pasastat->nproject-- ;
        /* compare dnew and d */
        s = PASAZERO ;
        t = PASAZERO ;
        for (j = 0; j < ncol; j++)
        {
            t += fabs(dnew [j] - d [j]) ;
            s += fabs(dnew [j]) ;
        }
        if ( t == PASAZERO )
        {
            printf ("\n") ;
        }
        else
        {
            printf (" rel error: %e\n", t/s) ;
        }
        /* report error if t too big */
        if ( (t > Com->GPtol*1.e6) && (t > s*Com->GPtol*1.e7) )
        {
            printf ("pasa projection error, abs error: %e rel error: %e\n",
                     t, t/s) ;
            pasa_error (-1, __FILE__, __LINE__, "projection error in pasa") ;
        }

        /* compare lambdanew and lambdaold */
        if ( (nrow > 0) && (Ap [ncol] > 0) )
        {
            s = PASAZERO ;
            t = PASAZERO ;
            for (j = 0; j < nrow; j++)
            {
                t += fabs(lambdanew [j] - lambdaold [j]) ;
                s += fabs(lambdanew [j]) ;
            }
            printf ("abs pasa multiplier error: %e", t) ;
            if ( t == PASAZERO )
            {
                printf ("\n") ;
            }
            else
            {
                printf (" rel error: %e\n", t/s) ;
            }
        }
        /* report error if t too big */
        /*if ( t > s*Com->GPtol*1.e9 )
        {
            printf ("pasa multiplier error, abs error: %e rel error: %e\n",
                     t, t/s) ;
            pasa_error (-1, __FILE__, __LINE__,
                                               "multiplier error in pasa") ;
        }*/
        /* restore Com structure */
        Com->lambda = lambdaold ;
        Com->pprojparm = parm ;
        Com->pprojstat = stat ;
        Com->lo = comlo ;
        Com->hi = comhi ;
        Com->bl = combl ;
        Com->bu = combu ;
        /* free memory */
        pasa_free (dnew) ;
        pasa_free (lambdanew) ;
        if ( Ai2 != NULL )
        {
            pasa_free (Ax2) ;
            pasa_free (Ai2) ;
        }
        if ( nrow != 0 )
        {
            pasa_free (w1) ;
            pasa_free (w2) ;
        }
    }
    if ( lo2 != NULL )
    {
        pasa_free (lo2) ;
    }
    if ( hi2 != NULL )
    {
        pasa_free (hi2) ;
    }
    pasa_free (bl2) ;
    pasa_free (bu2) ;
#endif
    /* compute derivative in the search direction */
    Com->gtd = pasa_dot (g, d, ncol) ;
    /* check that d is a descent direction if PrintLevel >= 2 */
    if ( PrintLevel >= 2 )
    {
        if ( Com->gtd > PASAZERO )
        {
            printf ("search direction not a descent direction: %e\n",
                     Com->gtd);
        }
    }
    if ( status > 1 )
    {
        return (status) ;
    }

    if ( (stepsize == PASAONE) && (nf == Com->ncol) )
    {
        Com->E = pasa_sup_norm (d, nf) ;
        Com->testtol = Parm->switchfactor * Com->E ;
        Com->testtol = PASAMAX (Com->testtol, Com->grad_tol) ;
        if ( PrintLevel >= 1 )
        {
            printf ("Global Error: %e testtol: %e\n", Com->E, Com->testtol) ;
        }
    }
    else if ( location == PASA_GRAD_PROJ ) /* nf = ncol in grad_proj */
    {
        /* if stepsize <= 1, then ||d||/stepsize is an upper bound on the
           error */
        PASAFLOAT const dnorm = pasa_sup_norm (d, Com->ncol) ;
        Com->estE = dnorm/stepsize ;
        if ( PrintLevel >= 1 )
        {
            printf ("Global Error estimate: %e\n", Com->estE) ;
        }
        if ( (stepsize < PASAONE) && (Com->estE < Com->E) )
        {
            Com->E = Com->estE ;
            Com->testtol = Parm->switchfactor * Com->E ;
            Com->testtol = PASAMAX (Com->testtol, Com->grad_tol) ;
            if ( PrintLevel >= 1 )
            {
                printf ("Reset Global error to: %e testtol: %e step < 1\n",
                         Com->E, Com->testtol) ;
            }
        }
        else if ( (stepsize >= PASAONE) && (stepsize*Com->estE < Com->E) )
        {
            Com->E = stepsize*Com->estE ;
            Com->testtol = Parm->switchfactor * Com->E ;
            Com->testtol = PASAMAX (Com->testtol, Com->grad_tol) ;
            if ( PrintLevel >= 1 )
            {
                printf ("Reset Global error to: %e testtol: %e step > 1\n",
                         Com->E, Com->testtol) ;
            }
        }
    }
    return (status) ;
}

/* ==========================================================================
   === pasa_null_project ====================================================
   ==========================================================================
    Project the gradient g associated with the free components of the current
    iterate into the null space associated with the current active constraint
    gradients.  If B is the part of the matrix A associated with active linear
    constraints and free components of x, then the projection is given by
    p = g - B'(BB')^{-1}B*g. If B is vacuous, then p = g. If gproj = NULL,
    then the sup norm of the projected gradient is computed and stored in
    Com->e and the projected gradient is discarded.
   ========================================================================== */
void pasa_null_project
(
    PASAFLOAT   *gproj, /* the projected gradient */
    PASAFLOAT       *g, /* the gradient to be projected */
    PASAFLOAT    *gpen, /* gradient of penalty term if it exists */
    int     LocalError, /* T => compute local error */
    PASAcom       *Com  /* common data for PASA */
)
{
    int Bounds, location, PrintLevel, status, *ib ;
    PASAINT ncol, nrow, i, j, k, l, *Ai, *Ap, *Anz, *RLinkUp, *RLinkDn ;
    PASAFLOAT e, t, u, *Ax, *G, *lambda ;
    PPprob *Prob ;
    PPwork *W ;
/*cholmod_factor *L ;
PASAFLOAT *Lx ;*/

    PrintLevel = Com->pasaparm->PrintLevel ;
    if ( PrintLevel >= 2 )
    {
        printf ("start null project\n") ;
    }
    ncol = Com->ncol ;
    e = PASAZERO ;
    if ( Com->Aexists == FALSE )
    {
        /* If Com->Aexist is FALSE, then the code was called from either CG
           or from activeGP.  In this case, simply return the sup-norm of
           the gradient since all the variables in the local problem are free.*/
        if ( LocalError == TRUE )
        {
            Com->e = pasa_sup_norm (g, Com->nf) ;
            if ( PrintLevel >= 1 )
            {
                printf ("Local Error: %e\n", Com->e) ;
            }
        }
        return ;
    }

    /* If there are no free variables, then all variables are at bounds,
       and the projected gradient is 0. */
    location = Com->location ;
    const PASAINT nf = Com->nf ;
    if ( nf == 0 )
    {
        Com->e = PASAZERO ;
        /* following block makes no sense, delete for now */
#if 0
        if ( location == PASA_CG_DESCENT )
        {
            pasa_initx (g, PASAZERO, ncol) ;
        }
#endif
        if ( PrintLevel >= 1 )
        {
            printf ("Local Error: %e\n", e) ;
        }
        return ;
    }

    Bounds = Com->Bounds ;
    if ( Com->use_napheap == TRUE )
    {
        PASAFLOAT *a, *lo, *hi, *x ;
        const int loExists = Com->loExists ;
        const int hiExists = Com->hiExists ;
        lo = Com->lo ;
        hi = Com->hi ;
        x  = Com->x ;
        if ( Com->nap_constraint == 0 ) /* the constraint is inactive */
        {
            if ( (location == PASA_ACTIVE_GP) || (location == PASA_CG_DESCENT) )
            {
                /* compute the sup-norm of the free components of g */
                Com->e = pasa_sup_norm (g, nf) ;
                if ( gproj != NULL )
                {
                    pasa_copyx (gproj, g, nf) ;
                }
            }
            else /* either BASE_CODE or GRAD_PROJ */
            {
                if ( Bounds == FALSE ) /* no bounds */
                {
                    e = pasa_sup_norm (g, nf) ;
                }
                else /* bounds exist, find free components */
                {
                    for (j = 0; j < nf; j++)
                    {
                        PASAFLOAT const xj = x [j] ;
/*printf ("j: %i x: %e lo: %e hi: %e\n", j, xj, lo [j], hi [j]) ;*/
                        if ( (loExists && (xj == lo [j])) ||
                             (hiExists && (xj == hi [j])) )
                        {
                            continue ;
                        }
                        if ( e < fabs (g [j]) )
                        {
                            e = fabs (g [j]) ;
                        }
                    }
                }
                Com->e = e ;
            }
            if ( PrintLevel >= 1 )
            {
                printf ("Local Error: %e\n", e) ;
            }
            return ;
        }
        if ( (location == PASA_CG_DESCENT) && (Com->use_penalty == TRUE)
                                           && (gpen != NULL) )
        {
            /* napxwork is a work area associated with napheap, it can be
               used here because it changes in each call to napheap */
            G = Com->napxwork ;
            /* store g + gpen in G when CG is used */
            pasa_step (G, g, gpen, PASAONE, nf) ;
        }
        else
        {
            G = g ;
        }
        /* Compute B*g and store in lambda. In the base code or in grad_proj,
           the matrix is not compressed, so ib must be used to extract the
           free columns of A */
        a = Com->nap_a ;
        if ( ((location == PASA_BASE_CODE) || (location == PASA_GRAD_PROJ)) &&
             (Bounds == TRUE) )
        {
            PASAINT *ifree = Com->napiwork ;
            u = t = PASAZERO ;
            i = 0 ;
            for (j = 0; j < nf; j++)
            {
                /* skip over variables at bounds */
                if ( (loExists && (x [j] == lo [j])) ||
                     (hiExists && (x [j] == hi [j])) )
                {
                    continue ;
                }
                ifree [i] = j ;
/*printf ("free index: %i x: %e lo: %e hi: %e\n", j, x [j], lo [j], hi [j]) ;*/
                i++ ;
                const PASAFLOAT aj = a [j] ;
                t += G [j]*aj ;
                u += aj*aj ;
            }
            const PASAFLOAT s = (u > PASAZERO) ? t/u : PASAZERO ;
            *(Com->lambda) = s ; /* multiplier estimate = a'g/a'a */
            const PASAINT nfree = i ;
            for (i = 0; i < nfree; i++)
            {
                j = ifree [i] ;
                t = G [j] - s*a [j] ;
                if ( e < fabs (t) )
                {
                    e = fabs (t) ;
                }
            }
            Com->e = e ;
            if ( PrintLevel >= 1 )
            {
                printf ("Local Error: %e\n", e) ;
            }
            return ;
        }
        /* Bounds = FALSE or location = activeGP or CG */
        t = pasa_dot (G, a, nf) ;
        const PASAFLOAT s = t/Com->nap_a2 ;
        *(Com->lambda) = s ; /* multiplier estimate = a'g/a'a */
        if ( gproj != NULL )
        {
            pasa_step (gproj, G, a, -s, nf) ;
            if ( LocalError )
            {
                Com->e = pasa_sup_norm (gproj, nf) ;
            }
        }
        else if ( LocalError ) /* do not store gproj */
        {
            e = PASAZERO ;
            for (j = 0; j < nf; j++)
            {
                t = G [j] - s*a [j] ;
                if ( e < fabs (t) )
                {
                    e = fabs (t) ;
                }
            }
            Com->e = e ;
        }
        if ( PrintLevel >= 1 )
        {
            printf ("Local Error: %e\n", Com->e) ;
        }
        return ;
    }
    W = Com->ppcom->Work ;
    RLinkUp = W->RLinkUp ;
    RLinkDn = W->RLinkDn ;
    ib = W->ib ;
/*L = W->L ;
Lx = L->x ;
printf ("Lx [0]: %25.15e\n", Lx [0]) ;*/
    nrow = Com->nrow ;
    /* If all rows in the matrix have dropped, then local error is the
       sup-norm of the free components of the gradient. */
    Prob = Com->ppcom->Prob ;
    if ( nrow == RLinkUp [nrow] )
    {
        if ( (location == PASA_ACTIVE_GP) || (location == PASA_CG_DESCENT) )
        {
            /* compute the sup-norm of the free components of g */
            ncol = Prob->ncol ;
            Com->e = pasa_sup_norm (g, ncol) ;
            if ( gproj != NULL )
            {
                pasa_copyx (gproj, g, ncol) ;
            }
        }
        else /* either BASE_CODE or GRAD_PROJ */
        {
            /* use ib to find free components unless there are no bounds */
            if ( Bounds == FALSE )
            {
                e = pasa_sup_norm (g, Com->ncol) ;
            }
            else
            {
                for (j = 0; j < ncol; j++)
                {
                    if ( ib [j] == 0 )
                    {
                        if ( e < fabs (g [j]) )
                        {
                            e = fabs (g [j]) ;
                        }
                    }
                }
            }
            Com->e = e ;
        }
        if ( PrintLevel >= 1 )
        {
            printf ("Local Error: %e\n", e) ;
        }
        return ;
    }
    Ap = Prob->Ap ;
    Anz = Prob->Anz ;
    Ai = Prob->Ai ;
    Ax = Prob->Ax ;
    ncol = Prob->ncol ;
    lambda = Com->lambda ;
    if ( (location == PASA_CG_DESCENT) && (Com->use_penalty == TRUE)
                                       && (gpen != NULL) )
    {
        G = W->arrayd ; /* work area in pproj */
        /* store g + gpen in G when CG is used */
        pasa_step (G, g, gpen, PASAONE, ncol) ;
    }
    else
    {
        G = g ;
    }
    pasa_initx (lambda, PASAZERO, nrow) ;
    /* compute B*gf, B = matrix of active rows and free columns */

#ifndef NDEBUG
    if ( PrintLevel >= 1 )
    {
        printf ("norm g: %e\n", pasa_sup_norm (G, ncol)) ;
    }
#endif


    /* Compute B*g and store in lambda. In the base code or in grad_proj,
       the matrix is not compressed, so ib must be used to extract the
       free columns of A */
    if ( ((location == PASA_BASE_CODE) || (location == PASA_GRAD_PROJ)) &&
         (Bounds == TRUE) )
    {
        for (j = 0; j < ncol; j++)
        {
            if ( ib [j] == 0 ) /* the variable is free */
            {
                t = g [j] ;
                if ( t != PASAZERO )
                {
                    k = Ap [j] ;
                    l = k + Anz [j] ;
                    for (; k < l; k++)
                    {
                        lambda [Ai [k]] += t*Ax [k] ;
/* printf ("%i %i %25.15e\n", Ai [k], j, Ax [k]) ;*/
                    }
                }
            }
        }
    }
    else /* Bounds = FALSE or location = activeGP or CG */
    {
        for (j = 0; j < ncol; j++)
        {
            t = G [j] ;
            if ( t != PASAZERO )
            {
                k = Ap [j] ;
                l = k + Anz [j] ;
                for (; k < l; k++)
                {
                    lambda [Ai [k]] += t*Ax [k] ;
/* printf ("%i %i %25.15e\n", Ai [k], j, Ax [k]) ;*/
                }
            }
        }
    }
#ifndef NOPPROJ
    /* compute (B*B')^{-1}B*gf */
    pproj_lsol (W->L, lambda, RLinkUp [nrow], nrow, RLinkUp) ;
    k = RLinkUp [nrow] ;
    /* momentarily set the initial RLinkDn to -1, this simplifies
       indexing in dltsolve */
    RLinkDn [k] = -1 ;
    l = RLinkDn [nrow] ;
    pproj_dltsol (W->L, lambda, lambda, l, k, RLinkDn) ;
    RLinkDn [k] = nrow ; /* restore RLinkDn */
#endif

    /* compute (I - B'*(B*B')^{-1}B)*gf */
    /* in the base code or in grad_proj, the matrix is not compressed,
       so ib must be used to extract the free columns of A */
    if ( ((location == PASA_BASE_CODE) || (location == PASA_GRAD_PROJ)) &&
         (Bounds == TRUE) )
    {
        for (j = 0; j < ncol; j++)
        {
            if ( ib [j] == 0 )
            {
/*printf ("free index: %i x: %e lo: %e hi: %e\n",
j, Com->x [j], Com->lo [j], Com->hi [j]) ;*/
                t = PASAZERO ;
                k = Ap [j] ;
                l = k + Anz [j] ;
                for (; k < l; k++)
                {
                    t += Ax [k]*lambda [Ai [k]] ;
                }
                t = g [j] - t ;
                if ( fabs (t) > e )
                {
                    e = fabs (t) ;
                }
                /* projected g not returned in either base code or grad proj */
            }
        }
    }
    else /* Bounds = FALSE or location = activeGP or CG */
    {
        const int gproj_exists = (gproj != NULL) ? TRUE : FALSE ;
        for (j = 0; j < ncol; j++)
        {
            t = PASAZERO ;
            k = Ap [j] ;
            l = k + Anz [j] ;
            for (; k < l; k++)
            {
                t += Ax [k]*lambda [Ai [k]] ;
/* printf ("i: %i j: %i Ax: %e\n", Ai [k], j, Ax [k]) ;*/
            }
            t = G [j] - t ;
            if ( fabs (t) > e )
            {
                e = fabs (t) ;
            }
            /* return projected gradient if it exists */
            if ( gproj_exists ) gproj [j] = t ;
        }
    }
    if ( LocalError == TRUE )
    {
        Com->e = e ;
    }
    if ( PrintLevel >= 1 )
    {
        printf ("Local Error: %e\n", Com->e) ;
    }
#ifndef NDEBUG
    /* check that the projected gradient lies in the null space */
    if ( (location == PASA_CG_DESCENT) && (gproj != NULL) )
    {
        PASAFLOAT *v ;
        v = (PASAFLOAT *) pasa_malloc (&status, nrow, sizeof (PASAFLOAT)) ;
/* printf ("matrix elements in check computation\n") ;*/
        t = CGZERO ;
        pasa_initx (v, PASAZERO, nrow) ;
        for (j = 0; j < ncol; j++)
        {
            e = gproj [j] ;
            if ( e != PASAZERO )
            {
                k = Ap [j] ;
                l = k + Anz [j] ;
                for (; k < l; k++)
                {
                    v [Ai [k]] += e*Ax [k] ;
/* printf ("i: %i j: %i Ax: %e\n", Ai [k], j, Ax [k]) ;*/
                    t = PASAMAX (t, fabs (Ax [k])) ;
                }
            }
        }
        printf ("projection error: %e maxAx: %e\n", pasa_sup_norm (v, nrow), t);
        pasa_free (v) ;
    }
#endif
}

/* ==========================================================================
   === pasa_project =========================================================
   ==========================================================================
    Project a given point y onto the polyhedren, storing the projection in x
   ========================================================================== */
int pasa_project /* return:
                            PASA_OK
                            PASA_POLYHEDRON_INFEASIBLE
                            PASA_INVALID_LINEAR_CONSTRAINT_BOUNDS
                            PASA_OUT_OF_MEMORY_IN_PPROJ
                            PASA_SSOR_ASCENT_FAILURE_IN_PPROJ
                            PASA_SSOR_ITERATIONS_EXCEED_MAX_ITS_IN_PPROJ */
(
    PASAFLOAT         *x, /* projection of y onto the polyhedron */
    PASAFLOAT         *y, /* point to project onto the polyhedron */
    PASAcom         *Com  /* common data for PASA */
)
{
    int start_guess, status ;
    PASAparm *Parm ;
    PPparm   *parm ;
    PPdata   *ppdata ;
    Com->pasastat->nproject++ ;

    if ( Com->use_napheap == TRUE )
    {
        NAPdata *napdata ;
        napdata = Com->pasadata->napdata ;

        /* update the napdata structure */
        napdata->x      = x ;
        napdata->lambda = -*(Com->lambda) ;
        napdata->n      = Com->nf ;
        napdata->y      = y ;
        napdata->blo    = Com->nap_bl ;
        napdata->bhi    = Com->nap_bu ;

        status = napheap (napdata) ;

        /* flip sign of lambda for consistency with rest of code */
        *(Com->lambda) = -napdata->lambda ;

        if ( Com->initial_projection == TRUE )
        {
            napdata->Parm->use_prior_data = TRUE ;
            Com->initial_projection = FALSE ;
        }
        /* map napheap status to pasa status */
        if ( status == NAPHEAP_STATUS_OK )
        {
            status = PASA_OK ;
        }
        return (status) ;
    }

    const int loExists = Com->loExists ;
    const int hiExists = Com->hiExists ;

    Parm = Com->pasaparm ;
    parm = Com->pprojparm ;

    /* If A does not exist, then the projection is simply trunction at bounds.*/
    if ( Com->Aexists == FALSE )
    {
        PASAINT j ;
        PASAINT const ncol = Com->nf ;
        for (j = 0; j < ncol; j++)
        {
            if ( loExists && (y [j] < Com->lo [j]) )
            {
                x [j] = Com->lo [j] ;
            }
            else if ( hiExists && (y [j] > Com->hi [j]) )
            {
                x [j] = Com->hi [j] ;
            }
            else
            {
                x [j] = y [j] ;
            }
        }
        return (PASA_OK) ;
    }

    if ( Com->initial_projection == TRUE )
    {
        /* For the initial projection, the only option is 1 if we are supposed
           to use the provided lambda, or 0 (start from initial guess = 0)
           if no initial lambda is provided. Hence, we save the value of
           start_guess in the parameter structure, reset its value to be 0 or 1,
           and then below, restore the original value of start_guess. */

        start_guess = parm->start_guess ;
        if ( Parm->use_lambda == TRUE )
        {
            parm->start_guess = 3 ;
        }
        else
        {
            parm->start_guess = 0 ;
        }
    }
#ifndef NOPPROJ

    ppdata = Com->pasadata->ppdata ;
    ppdata->x    = x ; /* projection on polyhedron */
    ppdata->y    = y ; /* point to project on polyhedron */
    ppdata->ncol = Com->nf ;

    status = pproj (ppdata) ;

#endif

    /* if the debugger is on, then check the accuracy of x */
#ifndef NDEBUG
#ifndef NOPPROJ
    {
        PPFLOAT errb, errx, absAx ;
        PPdata *ppdataDEBUG ;

        ppdataDEBUG = Com->ppdataDEBUG ;
        ppdataDEBUG->x    = x ;
        ppdataDEBUG->y    = y ;
        ppdataDEBUG->ncol = Com->nf ;
        pproj_KKTerror (&errb, &errx, &absAx, ppdataDEBUG) ;
        printf ("----------- Projection Error Statistics ------------\n") ;
        printf ("specified tolerance ..................... %e\n", Com->GPtol) ;
        printf ("rel. sup norm of dual function gradient . %e\n", errb);
        printf ("relative diff between x & dual minimizer  %e\n", errx) ;
        printf ("absAx ................................... %e\n", absAx) ;
    }
#endif
#endif
    if ( Com->initial_projection == TRUE )
    {
        Com->initial_projection = FALSE ;

        /* save the priordata in the ppdata structure */
        Com->ppcom = ppdata->priordata ;

        /* restore original value for start_guess */
        parm->start_guess = start_guess ;
    }

    /* map pproj status to pasa status */
    if ( status == PPROJ_SOLUTION_FOUND )
    {
        status = PASA_OK ;
    }
    else if ( status == PPROJ_ERROR_DECAY_STAGNATES )
    {
        if ((Com->pprojstat != NULL)&&(Com->pprojstat->errdual > Com->grad_tol))
        {
            status = PASA_POLYHEDRON_INFEASIBLE ;
        }
        else /* the weaker tolerance for the problem was satisfied, ok */
        {
            status = PASA_OK ;
        }
    }
    return (status) ;
}

/* ==========================================================================
   === pasa_project2 ========================================================
   ==========================================================================
    Project a given point y onto the polyhedren, storing the projection in x.
    project2 and project are the same except that in project2, we also extract
    the bl2 and bu2 arrays needed for checking the KKT conditions. In project,
    the extract was done earlier in either the base code or in gradproj_step.
   ========================================================================== */
int pasa_project2 /* return:
                              PASA_OK
                              PASA_POLYHEDRON_INFEASIBLE
                              PASA_INVALID_LINEAR_CONSTRAINT_BOUNDS
                              PASA_OUT_OF_MEMORY_IN_PPROJ
                              PASA_SSOR_ASCENT_FAILURE_IN_PPROJ
                              PASA_SSOR_ITERATIONS_EXCEED_MAX_ITS_IN_PPROJ */
(
    PASAFLOAT       *x, /* projection of y onto the polyhedron */
    PASAFLOAT       *y, /* point to project onto the polyhedron */
    PASAcom       *Com  /* common data for PASA */
)
{
    int PrintLevel, status ;
    PASAINT i, j, *ir ;
    PASAFLOAT normx, normy, s, *b, *bl, *bu, *lo, *hi ;
    PPprob *Prob ;

    PrintLevel = Com->pasaparm->PrintLevel ;
    if ( PrintLevel >= 2 )
    {
        printf ("project2\n") ;
    }
    if ( Com->use_napheap == TRUE )
    {
        status = pasa_project (x, y, Com) ;
        return (status) ;
    }

    /* we project onto the original polyhedron, not the translated one,
       hence, we save the data for the original polyhedron, and then
       restore it later */
    Prob = Com->ppcom->Prob ;
    b  = Prob->b ;
    bl = Prob->bl ;
    bu = Prob->bu ;
    lo = Prob->lo ;
    hi = Prob->hi ;
    /* now save data for the original, untranslated polyhedron */
    Prob->lo = Com->lo ;
    Prob->hi = Com->hi ;
    Prob->bl = Com->bl ;
    Prob->bu = Com->bu ;
    Prob->b  = Com->b ;
    /* since there is no translation, set absAxk = 0 */
    Com->ppcom->Work->absAxk = PASAZERO ;

#ifndef NDEBUG
    PASAINT const nrow = Com->nrow ;
    PASAFLOAT *bl2 = (PASAFLOAT *) pasa_malloc (&status, nrow, sizeof (double));
    PASAFLOAT *bu2 = (PASAFLOAT *) pasa_malloc (&status, nrow, sizeof (double));
    Com->ppdataDEBUG->bl = bl2 ;
    Com->ppdataDEBUG->bu = bu2 ;
    Com->ppdataDEBUG->lo = Com->lo ;
    Com->ppdataDEBUG->hi = Com->hi ;
    Com->ppdataDEBUG->Ap = Com->Ap ;
    Com->ppdataDEBUG->Ai = Com->Ai ;
    Com->ppdataDEBUG->Ax = Com->Ax ;
    ir = Com->ppcom->Work->ir ;
    j = 0 ;

    for (i = 0; i < nrow; i++) /* i corresponds to the permuted constraints */
    {
        if ( ir [i] == 0 )     /* equality constraint */
        {
            bl2 [i] = Com->b [i] ;
            bu2 [i] = Com->b [i] ;
        }
        else                   /* strict inequality constraint */
        {
            j++ ;
            bl2 [i] = Com->bl [j] ;
            bu2 [i] = Com->bu [j] ;
        }
    }
#endif
    /* ----------------------------------------- */
    status = pasa_project (x, y, Com) ;
    /* ----------------------------------------- */

#ifndef NDEBUG
    /* x and d should be the same to within rounding errors */
    PASAINT const ncol = Com->ncol ;
    normx = pasa_sup_norm (x, ncol) ;
    normy = pasa_sup_norm (y, ncol) ;
    if ( (normx == PASAZERO) && (normy == PASAZERO) )
    {
        normx = PASAONE ;
    }
    s = PASAZERO ;
    for (j = 0; j < ncol; j++)
    {
        s += fabs (x [j] - y [j]) ;
    }
    printf ("project2, s: %e, normx: %e, normy: %e\n",
             s, normx, normy) ;
    if ( s > Com->pasaparm->debugtol*(normx + normy) )
    {
        pasa_error (-1, __FILE__, __LINE__, "stop") ;
    }
    pasa_free (bl2) ;
    pasa_free (bu2) ;
#endif
    Prob->lo = lo ;
    Prob->hi = hi ;
    Prob->bl = bl ;
    Prob->bu = bu ;
    Prob->b  = b  ;
    return (status) ;
}

/* =========================================================================
   === pasa_compress_matrix ================================================
   =========================================================================
    Compress a matrix by retaining only columns for which drop [j] = 0.
    The original matrix is overwritten by the compressed matrix. The
    number of columns in the compressed matrix is returned.
   ========================================================================= */
PASAINT pasa_compress_matrix
(
    PASAINT    *Ap, /* matrix column pointers */
    PASAINT    *Ai, /* matrix row indices */
    PASAFLOAT  *Ax, /* matrix numerical values */
    PASAINT   ncol, /* number of cols in A */
    PASAINT  *drop  /* drop columns for which drop [j] != 0 */
)
{
    PASAINT cols, j, l, p, q ;
    /* find the index of the first dropped column */
    cols = ncol ;
    for (j = 0; j < ncol; j++)
    {
        if ( drop [j] != 0 )
        {
            cols = j ;
            break ;
        }
    }
    /* if nothing drops, then return */
    if ( cols == ncol )
    {
        return (ncol) ;
    }
    /* cols stores the number of remaining columns in A */
    l = Ap [cols] ; /* start of storage for this first dropped column */
    for (j++; j < ncol; j++)
    {
        if ( drop [j] == 0 ) /* retained column */
        {
            p = Ap [j] ;
            q = Ap [j+1] ;
            Ap [cols+1] = Ap [cols] + q - p ;
            cols++ ;
            for (; p < q; p++)
            {
                Ai [l] = Ai [p] ;
                Ax [l] = Ax [p] ;
                l++ ;
            }
        }
    }
    return (cols) ;
}

/* =========================================================================
   === pasa_step ============================================================
   =========================================================================
    Set xnew = x + alpha*d
   ========================================================================= */
void pasa_step
(
    PASAFLOAT       *xnew, /* updated x vector */
    PASAFLOAT const    *x, /* current x */
    PASAFLOAT const    *d, /* search direction */
    PASAFLOAT const alpha, /* stepsize */
    PASAINT   const     n  /* dimension */
)
{
    PASAINT j, n5 ;

    if ( x == NULL )
    {
        return ;
    }
    n5 = n % 5 ;     /* n5 = n mod 5 */
    /* check if step size equals 1 */
    if ( alpha == PASAONE )
    {
        for (j = 0; j < n5; j++)
        {
            xnew [j] = x [j] + d [j] ;
        }
        if ( xnew == x )
        {
            for (; j < n; )
            {
                 xnew [j] += d [j] ; j++ ;
                 xnew [j] += d [j] ; j++ ;
                 xnew [j] += d [j] ; j++ ;
                 xnew [j] += d [j] ; j++ ;
                 xnew [j] += d [j] ; j++ ;
            }
        }
        else
        {
            for (; j < n; )
            {
                 xnew [j] = x [j] + d [j] ; j++ ;
                 xnew [j] = x [j] + d [j] ; j++ ;
                 xnew [j] = x [j] + d [j] ; j++ ;
                 xnew [j] = x [j] + d [j] ; j++ ;
                 xnew [j] = x [j] + d [j] ; j++ ;
            }
        }
    }
    else if ( alpha == -PASAONE )
    {
        for (j = 0; j < n5; j++)
        {
            xnew [j] = x [j] - d [j] ;
        }
        for (; j < n; )
        {
            xnew [j] = x [j] - d [j] ; j++ ;
            xnew [j] = x [j] - d [j] ; j++ ;
            xnew [j] = x [j] - d [j] ; j++ ;
            xnew [j] = x [j] - d [j] ; j++ ;
            xnew [j] = x [j] - d [j] ; j++ ;
        }
    }
    /* else step size is not 1 */
    else
    {
        for (j = 0; j < n5; j++)
        {
            xnew [j] = x [j] + alpha*d [j] ;
        }
        for (; j < n; )
        {
            xnew [j] = x [j] + alpha*d [j] ; j++ ;
            xnew [j] = x [j] + alpha*d [j] ; j++ ;
            xnew [j] = x [j] + alpha*d [j] ; j++ ;
            xnew [j] = x [j] + alpha*d [j] ; j++ ;
            xnew [j] = x [j] + alpha*d [j] ; j++ ;
        }
    }
}

/* =========================================================================
   === pasa_hadamard =======================================================
   =========================================================================
    Compute the Hadamard product of y and z and store in x
   ========================================================================= */
void pasa_hadamard
(
    PASAFLOAT       *x, /* x = y.*z */
    PASAFLOAT const *y,
    PASAFLOAT const *z,
    PASAINT   const  n  /* length of vectors */
)
{
    PASAINT j, n5 ;
    n5 = n % 5 ;        /* n5 = n mod 5 */
    if ( x == z )
    {
        for (j = 0; j < n5; j++)
        {
            x [j] *= y [j] ;
        }
        for (; j < n; )
        {
            x [j] *= y [j] ; j++ ;
            x [j] *= y [j] ; j++ ;
            x [j] *= y [j] ; j++ ;
            x [j] *= y [j] ; j++ ;
            x [j] *= y [j] ; j++ ;
        }
    }
    else
    {
        for (j = 0; j < n5; j++)
        {
            x [j] = y [j]*z [j] ;
        }
        for (; j < n; )
        {
            x [j] = y [j]*z [j] ; j++ ;
            x [j] = y [j]*z [j] ; j++ ;
            x [j] = y [j]*z [j] ; j++ ;
            x [j] = y [j]*z [j] ; j++ ;
            x [j] = y [j]*z [j] ; j++ ;
        }
    }
}

/* =========================================================================
   ===================== pasa_scale ========================================
   =========================================================================
   Scale a PASAFLOAT array
   ========================================================================= */
void pasa_scale
(
    PASAFLOAT      *x,  /* array to be scaled */
    PASAFLOAT      *y,  /* array used for the scaling */
    PASAFLOAT const s,  /* scale */
    PASAINT   const n   /* length of x */
)
{
    PASAINT j, n5 ;
    PASAFLOAT *xj, *yj ;
    n5 = n % 5 ;
    if ( x == y )
    {
        for (j = 0; j < n5; j++) x [j] *= s ;
        xj = x+j ;
        for (; j < n; j += 5)
        {
            *(xj++) *= s ;
            *(xj++) *= s ;
            *(xj++) *= s ;
            *(xj++) *= s ;
            *(xj++) *= s ;
        }
    }
    else if ( s != -PPONE )
    {
        for (j = 0; j < n5; j++) x [j] = s*y [j] ;
        xj = x+j ;
        yj = y+j ;
        for (; j < n; j += 5)
        {
            *(xj++) = s*(*yj++) ;
            *(xj++) = s*(*yj++) ;
            *(xj++) = s*(*yj++) ;
            *(xj++) = s*(*yj++) ;
            *(xj++) = s*(*yj++) ;
        }
    }
    else
    {
        for (j = 0; j < n5; j++) x [j] = -y [j] ;
        xj = x+j ;
        yj = y+j ;
        for (; j < n; j += 5)
        {
            *(xj++) = -(*yj++) ;
            *(xj++) = -(*yj++) ;
            *(xj++) = -(*yj++) ;
            *(xj++) = -(*yj++) ;
            *(xj++) = -(*yj++) ;
        }
    }
}

/* =========================================================================
   === PASAdot =============================================================
   =========================================================================
    Compute dot product of x and y
   ========================================================================= */
PASAFLOAT pasa_dot
(
    PASAFLOAT const *x, /* first vector */
    PASAFLOAT const *y, /* second vector */
    PASAINT   const  n  /* length of vectors */
)
{
    PASAINT j, n5 ;
    PASAFLOAT t ;
    t = PASAZERO ;      /* initialize t */
    n5 = n % 5 ;        /* n5 = n mod 5 */
    for (j = 0; j < n5; j++) t += x [j]*y [j] ;
    for (; j < n; j += 5)
    {
        t += x [j]*y[j] + x [j+1]*y [j+1] + x [j+2]*y [j+2]
                        + x [j+3]*y [j+3] + x [j+4]*y [j+4] ;
    }
    return (t) ;        /* return x dot y */
}

/* =========================================================================
   === pasa_copyx ==========================================================
   =========================================================================
    Copy the second vector to the first, x = y
   ========================================================================= */
void pasa_copyx
(
    PASAFLOAT    *x, /* copy of y */
    PASAFLOAT    *y, /* given vector */
    PASAINT const n  /* length of vectors */
)
{
    PASAINT i, n5 ;
    PASAFLOAT *X, *Y ;
    if ( (y == NULL) || (x == y) || (n == 0) )
    {
        return ;
    }
    n5 = n % 5 ;  /* n5 = n mod 5 */
    for (i = 0; i < n5; i++) x [i] = y [i] ;
    X = x+n5 ;
    Y = y+n5 ;
    for (; i < n; i += 5)
    {
        *X++ = *Y++ ;
        *X++ = *Y++ ;
        *X++ = *Y++ ;
        *X++ = *Y++ ;
        *X++ = *Y++ ;
    }
}

/* =========================================================================
   === pasa_copyi ==========================================================
   =========================================================================
    Copy the second vector to the first, x = y
   ========================================================================= */
void pasa_copyi
(
    PASAINT      *x, /* copy of y */
    PASAINT      *y, /* given vector */
    PASAINT const n  /* length of vectors */
)
{
    PASAINT i, n5, *X, *Y ;
    if ( n ) /* if n nonzero, then copy */
    {
        n5 = n % 5 ;  /* n5 = n mod 5 */
        for (i = 0; i < n5; i++) x [i] = y [i] ;
        X = x+n5 ;
        Y = y+n5 ;
        for (; i < n; i += 5)
        {
            *X++   =  *Y++ ;
            *X++   =  *Y++ ;
            *X++   =  *Y++ ;
            *X++   =  *Y++ ;
            *X++   =  *Y++ ;
        }
    }
}

/* =========================================================================
   === pasa_copyi_int ======================================================
   =========================================================================
    Copy the second vector to the first, x = y (needed for mex)
   ========================================================================= */
void pasa_copyi_int
(
    int          *x, /* copy of y */
    int          *y, /* given vector */
    PASAINT const n  /* length of vectors */
)
{
    PASAINT i, n5 ;
    int    *X, *Y ;
    n5 = n % 5 ;  /* n5 = n mod 5 */
    for (i = 0; i < n5; i++) x [i] = y [i] ;
    X = x+n5 ;
    Y = y+n5 ;
    for (; i < n; i += 5)
    {
        *X++   =  *Y++ ;
        *X++   =  *Y++ ;
        *X++   =  *Y++ ;
        *X++   =  *Y++ ;
        *X++   =  *Y++ ;
    }
}

/* =========================================================================
   ===================== pasa_initi ========================================
   =========================================================================
   Initialize a PASAINT array
   ========================================================================= */
void pasa_initi
(
    PASAINT      *x,  /* array to be initialized */
    PASAINT const s,  /* scalar */
    PASAINT const n   /* length of x */
)
{
    PASAINT j, n5 ;
    PASAINT *xj ;
    n5 = n % 5 ;
    for (j = 0; j < n5; j++) x [j] = s ;
    xj = x+j ;
    for (; j < n; j += 5)
    {
        *(xj++) = s ;
        *(xj++) = s ;
        *(xj++) = s ;
        *(xj++) = s ;
        *(xj++) = s ;
    }
}

/* =========================================================================
   ===================== pasa_initx ========================================
   =========================================================================
   Initialize a PASAFLOAT array
   ========================================================================= */
void pasa_initx
(
    PASAFLOAT      *x,  /* array to be initialized */
    PASAFLOAT const s,  /* scalar */
    PASAINT   const n   /* length of x */
)
{
    PASAINT j, n5 ;
    PASAFLOAT *xj ;
    n5 = n % 5 ;
    for (j = 0; j < n5; j++) x [j] = s ;
    xj = x+j ;
    for (; j < n; j += 5)
    {
        *(xj++) = s ;
        *(xj++) = s ;
        *(xj++) = s ;
        *(xj++) = s ;
        *(xj++) = s ;
    }
}

/* =========================================================================
   ==== pasa_sup_norm ======================================================
   =========================================================================
   Return max {|x [j]| : 1 <= j <= n}
   ========================================================================= */
PASAFLOAT pasa_sup_norm
(
    PASAFLOAT const *x, /* vector */
    PASAINT   const  n  /* length of vector */
)
{
    PASAFLOAT xnorm ;
    PASAINT j, n5 ;
    n5 = n % 5 ;            /* n5 = n mod 5 */
    xnorm = PASAZERO ;      /* initializing xnorm */
    for (j = 0; j < n5; j++)
    {
        if ( xnorm < fabs (x [j]) ) xnorm = fabs (x [j]) ;
    }
    for (; j < n; j += 5)
    {
        if ( xnorm < fabs (x [j]  ) ) xnorm = fabs (x [j]) ;
        if ( xnorm < fabs (x [j+1]) ) xnorm = fabs (x [j+1]) ;
        if ( xnorm < fabs (x [j+2]) ) xnorm = fabs (x [j+2]) ;
        if ( xnorm < fabs (x [j+3]) ) xnorm = fabs (x [j+3]) ;
        if ( xnorm < fabs (x [j+4]) ) xnorm = fabs (x [j+4]) ;
    }
    return (xnorm) ;        /* return ||x||_inf */
}

/* =========================================================================
   === pasa_isup_norm ======================================================
   =========================================================================
   Return max x [j] : 1 <= j <= n}
   ========================================================================= */
PASAINT pasa_isup
(
    PASAINT const *x, /* vector */
    PASAINT const  n  /* length of vector */
)
{
    PASAINT xsup ;
    PASAINT j, n5 ;
    n5 = n % 5 ;            /* n5 = n mod 5 */
    xsup = 0 ;      /* initializing xnorm */
    for (j = 0; j < n5; j++)
    {
        if ( xsup < x [j] ) xsup = x [j] ;
    }
    for (; j < n; j += 5)
    {
        if ( xsup < x [j]   ) xsup = x [j] ;
        if ( xsup < x [j+1] ) xsup = x [j+1] ;
        if ( xsup < x [j+2] ) xsup = x [j+2] ;
        if ( xsup < x [j+3] ) xsup = x [j+3] ;
        if ( xsup < x [j+4] ) xsup = x [j+4] ;
    }
    return (xsup) ;
}

/* ========================================================================== */
/* === pasa_transpose ======================================================= */
/* ========================================================================== */
/*    Transpose a sparse matrix: B = A' */
/* ========================================================================== */

void pasa_transpose
(
    PASAINT   *Bp,   /* size nrow+1, column pointers (output) */
    PASAINT   *Bi,   /* size Ap [ncol], row indices of B (output) */
    PASAFLOAT *Bx,   /* size Ap [ncol], numerical entries of B (output) */
    PASAINT   *Ap,   /* size ncol+1, column pointers */
    PASAINT   *Ai,   /* size Ap [ncol], row indices for A */
    PASAFLOAT *Ax,   /* size Ap [ncol], numerical entries of A */
    PASAINT  nrow,   /* number of rows in A */
    PASAINT  ncol,   /* number of cols in A */
    PASAINT    *W    /* work array of size nrow */
)
{
    PASAINT i, j, p, q, pp ;

    /* ====================================================================== */
    /* === compute row counts of A ========================================== */
    /* ====================================================================== */

    for (i = 0; i < nrow; i++)
    {
        W [i] = 0 ;
    }
    p = 0 ;
    for (j = 1; j <= ncol; j++)
    {
        q = Ap [j] ;
        for (; p < q; p++)
        {
            W [Ai [p]]++ ;
        }
    }

    /* ====================================================================== */
    /* === compute column pointers of B given the counts ==================== */
    /* ====================================================================== */

    Bp [0] = 0 ;
    for (i = 0; i < nrow; i++)
    {
        Bp [i+1] = Bp [i] + W [i] ;
        W [i] = Bp [i] ;
    }

    /* ====================================================================== */
    /* === B = A' =========================================================== */
    /* ====================================================================== */

    p = 0 ;
    for (j = 0 ; j < ncol ; j++)
    {
        q = Ap [j+1] ;
        for (; p < q; p++)
        {
            pp = W [Ai [p]]++ ;
            Bi [pp] = j ;
            Bx [pp] = Ax [p] ;
        }
    }
}

/* =========================================================================
   === pasa_convert_to_user ===============================================
   =========================================================================
   convert from pproj's order of variables to user's order
   ========================================================================= */
void pasa_convert_to_user
(
    PASAFLOAT       *x, /* x in user order */
    PASAFLOAT const *y, /* y in pproj order */
    PASAINT     *ifree, /* ifree [j] = element of x associated with y [j] */
    PASAINT   const  n  /* dimension */
)
{
    PASAINT j ;
    for (j = 0; j < n; j++)
    {
        x [ifree [j]] = y [j] ;
    }
}

/* =========================================================================
   === pasa_convert_to_pproj ==============================================
   =========================================================================
   convert from user's order of variables to pproj's order
   ========================================================================= */
void pasa_convert_to_pproj
(
    PASAFLOAT   *x, /* x in pproj order */
    PASAFLOAT   *y, /* y in user order */
    PASAINT *ifree, /* ifree [j] = element of y associated with x [j] */
    PASAINT      n  /* dimension */
)
{
    PASAINT j ;
    for (j = 0; j < n; j++)
    {
        x [j] = y [ifree [j]] ;
    }
}

/* ==========================================================================
   === pasa_evaluate ========================================================
   ==========================================================================
    Evaluate function, gradient, or both
   ========================================================================== */
int pasa_evaluate
(
    PASAFLOAT alpha_good, /* a value of alpha for which function is finite */
    PASAFLOAT     *Alpha, /* stepsize along the search direction */
    PASAcom         *Com, /* PASAcom structure */
    char           *what  /* f = function, g = gradient, fg =function+gradient*/
)
{
    PASAFLOAT alpha, df, t, *d, *f, *g, *gtemp, *Qd, *x ;
    PASAINT i, j, n, nf, *nfunc, *ngrad, *order ;
    PASAstat *Stat ;
    PASAparm *Parm ;
    int location ;
    alpha = *Alpha ;
    Parm = Com->pasaparm ;
    Stat = Com->pasastat ;
    location = Com->location ; /* PASA_BASE_CODE (main code)
                                  PASA_GRAD_PROJ (gradient projection)
                                  PASA_ACTIVE_GP (active set grad proj)
                                  PASA_CG_DESCENT (conjugate gradient) */
    order = NULL ; /* do not reorder the variables */
    if ( (location == PASA_ACTIVE_GP) || (location == PASA_CG_DESCENT) )
    {
        if ( Com->order_with_ifree == TRUE )
        {
            order = Com->ifree ;
        }
        else
        {
            if ( Com->order_with_colperm == TRUE )
            {
                order = Com->colperm ;
            }
        }
    }
    if ( location == PASA_GRAD_PROJ )
    {
        if ( Com->order_with_colperm == TRUE )
        {
            order = Com->colperm ;
        }
    }
    int const QP = Com->QP ;
    d = Com->d ;
    Qd = Com->Qd ;
    n = Com->ucol ; /* user specified number of variables */
    nf = Com->nf ;

    /* The point where the function is evaluated depends on alpha.
       alpha < 0 => evaluate at xnew
       alpha = 0 => evaluate at x (the first evaluation of the objective)
       otherwise, evaluate at xnew = x + alpha*d. If the function is
       evaluated at xnew, then the gradient and objective value are
       stored in gnew and fnew respectively. Similarly, if the function
       is evaluated at x, then the gradient and objective values are stored
       in g and f respectively. */
    if ( alpha != PASAZERO ) /* CG, GP, AGP */
    {
        x = Com->xnew ;
        g = Com->gnew ;
        if ( location == PASA_CG_DESCENT )
        {
            f = &Com->f ; /* no fnew in CG */
        }
        else
        {
            f =&Com->fnew ;
        }
    }
    else /* alpha = 0, initial function evaluation */
    {
        x = Com->userx ;
        g = Com->userg ;
        f =&Com->f ;
    }

    /* if alpha > 0, then compute the step xnew = x + alpha*d */
    if ( alpha > PASAZERO ) /* GP, AGP, CG but not QP */
    {
        pasa_step (Com->xnew, Com->x, d, alpha, nf) ;
        if ( (location == PASA_CG_DESCENT) && (alpha == Com->maxstep) )
        {
            /* component of x hits bound if boundtype = 1 */
            if ( Com->boundtype == 1 )
            {
                if ( Com->boundindex < 0 )
                {
                    j = -(Com->boundindex + 1) ;
                    Com->xnew [j] = Com->lo [j] ;
                }
                else
                {
                    j = Com->boundindex - 1 ;
                    Com->xnew [j] = Com->hi [j] ;
                }
            }
        }
    }

    gtemp = g ; /* default */
    /* The very first time that the function is evaluated (that is, when
       alpha = 0), we employ the user's ordering of the variables.
       Subsequently, x in the code corresponds to pproj's ordering
       if the A matrix exists. Hence, before evaluating the function,
       we need to change from pproj's ordering to the user's ordering.
       If we come from grad_proj and there is no A,
       then there is no permutation.  */
    if ( (alpha != PASAZERO) && (QP == FALSE) )
    {
        if ( order != NULL )
        {
            for (j = 0; j < nf; j++)
            {
                Com->userx [order [j]] = x [j] ;
            }
            x = Com->userx ;
            /* if gradient is evaluated, store it's value temporarily in
               userg since we need to convert from user ordering to pproj
               ordering */
            gtemp = Com->userg ;
        }
        /* else coming from grad_proj with only bound constraints, directly
           store gradient in g */
    }

    /* Determine which counter to increment when counting the number of
       function and gradient evaluations. */
    if ( location == PASA_BASE_CODE )
    {
        nfunc = &Stat->mcnf ;
        ngrad = &Stat->mcng ;
    }
    else if ( location == PASA_GRAD_PROJ ) /* gradient projection */
    {
        nfunc = &Stat->gpnf ;
        ngrad = &Stat->gpng ;
    }
    else if ( location == PASA_ACTIVE_GP )
    {
        nfunc = &Stat->agpnf ;
        ngrad = &Stat->agpng ;
    }
    else                      /* conjugate gradients */
    {
        nfunc = &Com->cgstat->nfunc ;
        ngrad = &Com->cgstat->ngrad ;
    }

    if ( QP ) /* Hessian = Q */
    {
        (*ngrad)++ ;
        if ( alpha == PASAZERO ) /* initial evaluation of func & grad */
        {
            Com->hprod (g, x, NULL, n, n) ;         /* g = Q*x */
            *f = 0.5*pasa_dot (g, x, n) ;               /* f = .5x'Qx */
            if ( Com->userc != NULL )
            {
                *f += pasa_dot(x, Com->userc, n) ;/* f = .5*x'*Qx + c'*x*/
                /* store gradient in userg */
                pasa_step (g, g, Com->userc, PASAONE, n) ; /* g = Qx+c */
            }
            /* else c is taken as zero so there is no effect on objective value
               or the gradient */
            /* *f += Parm->fadjust ; */    /* user adjustment moved to wrapup */
            return (PASA_OK) ;
        }
        Com->hprod (Qd, d, order, n, nf) ;
        if ( alpha == PASAONE ) /* alpha = -1 when evaluation in CG */
        {
            if ( order == NULL )
            {
                pasa_step (g, Com->gtot, Qd, alpha, nf) ;
            }
            else
            {
                for (j = 0; j < nf; j++)
                {
                    i = order [j] ;
                    g [j] = Com->gtot [i] + Qd [i] ;
                }
            }
        }
        return (PASA_OK) ; /* immediately return when objective is quadratic */
    }
    else if ( !strcmp (what, "fg") ) /* compute function and grad values */
    {
        (*nfunc)++ ;
        (*ngrad)++ ;
        if ( Com->valgrad != NULL ) /* user provided valgrad */
        {
            /* evaluate func and grad together */
            Com->valgrad (f, gtemp, x, n) ;
        }
        else /* evaluate func and grad separately */
        {
            Com->value (f, x, n) ;    /* evaluate func */
            Com->grad (gtemp, x, n) ; /* evaluate grad */
        }

        /* direct return if evaluating at starting point */
        if ( alpha == PASAZERO )
        {
            return (PASA_OK) ;
        }
        /* for CG we need to compute the derivative in the search direction */
        if ( location == PASA_CG_DESCENT )
        {
            /* convert from user variables to variables in pasa */
            if ( order != NULL )
            {
                pasa_convert_to_pproj (g, gtemp, order, nf) ;
            }
            df = Com->df = pasa_dot (g, d, nf) ;
            /* if an infinite function value or an nan was encountered,
               then we attempt to find a finite function value */
            if ( (*f != *f) || (*f >= CGINF) || (*f <= -CGINF) ||
                 (df != df) || (df >= CGINF) || (df <= -CGINF) )
            {
                t = Parm->infdecay ;
                for (i = 0; i < Parm->ninf_tries; i++)
                {
                    nfunc++ ;
                    ngrad++ ;
                    alpha = alpha_good + t*(alpha - alpha_good) ;
                    t *= Parm->infdecay_rate ;
                    pasa_step (Com->xnew, Com->x, d, alpha, nf) ;
                    if ( order != NULL )
                    {
                        for (j = 0; j < nf; j++)
                        {
                            Com->userx [order [j]] = Com->xnew [j] ;
                        }
                    }
                    x = Com->userx ;
                    if ( Com->valgrad != NULL )
                    {
                        Com->valgrad (f, gtemp, x, n) ;
                    }
                    else
                    {
                        Com->grad  (gtemp, x, n) ;
                        Com->value (f, x, n) ;
                    }
                    /* convert from user variables to variables in pasa */
                    if ( order != NULL )
                    {
                        pasa_convert_to_pproj (g, gtemp, order, nf) ;
                    }
                    df = Com->df = cg_dot (g, d, n) ;
                    if ( (*f == *f) && (*f < CGINF) && (*f > -CGINF) &&
                         (df == df) && (df < CGINF) && (df > -CGINF) )
                    {
                        break ;
                    }
                }
                if ( i == Parm->ninf_tries )
                {
                    return (PASA_FUNCTION_NAN_OR_INF) ;
                }
            }
            if ( Com->use_penalty == TRUE )
            {
                Com->f_orig = *f ;
                *f += Com->fp + alpha*(Com->dp + 0.5*alpha*Com->Ad2) ;
                Com->df += Com->dp + alpha*Com->Ad2 ;
            }
        }
        else /* not in cg_descent, no need to compute directional derivative */
        {
            /* if an infinite function value or an nan was encountered,
               then we attempt to find a finite function value */
            if ( (*f != *f) || (*f >= CGINF) || (*f <= -CGINF) )
            {
                t = Parm->infdecay ;
                for (i = 0; i < Parm->ninf_tries; i++)
                {
                    nfunc++ ;
                    ngrad++ ;
                    alpha = alpha_good + t*(alpha - alpha_good) ;
                    t *= Parm->infdecay_rate ;
                    pasa_step (Com->xnew, Com->x, d, alpha, nf) ;
                    if ( order != NULL )
                    {
                        for (j = 0; j < nf; j++)
                        {
                            Com->userx [order [j]] = Com->xnew [j] ;
                        }
                    }
                    x = Com->userx ;
                    if ( Com->valgrad != NULL )
                    {
                        Com->valgrad (f, gtemp, x, n) ;
                    }
                    else
                    {
                        Com->grad  (gtemp, x, n) ;
                        Com->value (f, x, n) ;
                    }
                    if ( (*f == *f) && (*f < CGINF) && (*f > -CGINF) )
                    {
                        break ;
                    }
                }
                if ( i == Parm->ninf_tries )
                {
                    return (PASA_FUNCTION_NAN_OR_INF) ;
                }
            }
            /* convert from user variables to variables in pasa */
            if ( order != NULL )
            {
                pasa_convert_to_pproj (g, gtemp, order, nf) ;
            }
        }
        *Alpha = alpha ;
        return (PASA_OK) ;
    }
    else if ( !strcmp (what, "f") )   /* only compute function value */
    {
        (*nfunc)++ ;
        Com->value (f, x, n) ;
        /* if an infinite function value or an nan was encountered,
           then we attempt to find a finite function value */
        if ( (*f != *f) || (*f >= CGINF) || (*f <= -CGINF) )
        {
            t = Parm->infdecay ;
            for (i = 0; i < Parm->ninf_tries; i++)
            {
                nfunc++ ;
                alpha = alpha_good + t*(alpha - alpha_good) ;
                t *= Parm->infdecay_rate ;
                pasa_step (Com->xnew, Com->x, d, alpha, nf) ;
                if ( order != NULL )
                {
                    for (j = 0; j < nf; j++)
                    {
                        Com->userx [order [j]] = Com->xnew [j] ;
                    }
                }
                x = Com->userx ;
                Com->value (f, x, n) ;
                if ( (*f == *f) && (*f < CGINF) && (*f > -CGINF) )
                {
                    break ;
                }
            }
            if ( i == Parm->ninf_tries )
            {
                return (PASA_FUNCTION_NAN_OR_INF) ;
            }
            *Alpha = alpha ;
        }

        if ( (location == PASA_CG_DESCENT) &&  (Com->use_penalty == TRUE) )
        {
            Com->f_orig = *f ;
            *f += Com->fp + alpha*(Com->dp + 0.5*alpha*Com->Ad2) ;
        }
        return (PASA_OK) ;
    }
    else                              /* only compute gradient */
    {
        (*ngrad)++ ;
        Com->grad (gtemp, x, n) ;
        /* convert from user variables to variables in pasa */
        if ( order != NULL )
        {
            pasa_convert_to_pproj (g, gtemp, order, nf) ;
        }

        /* for CG we need to compute the derivative in the search direction */
        if ( location == PASA_CG_DESCENT )
        {
            df = Com->df = pasa_dot (g, d, nf) ;
            /* if an infinite function value or an nan was encountered,
               then we attempt to find a finite function value */
            if ( (df != df) || (df >= CGINF) || (df <= -CGINF) )
            {
                t = Parm->infdecay ;
                for (i = 0; i < Parm->ninf_tries; i++)
                {
                    ngrad++ ;
                    alpha = alpha_good + t*(alpha - alpha_good) ;
                    t *= Parm->infdecay_rate ;
                    pasa_step (Com->xnew, Com->x, d, alpha, nf) ;
                    if ( order != NULL )
                    {
                        for (j = 0; j < nf; j++)
                        {
                            Com->userx [order [j]] = Com->xnew [j] ;
                        }
                    }
                    Com->grad  (gtemp, Com->userx, n) ;
                    /* convert from user variables to variables in pasa */
                    if ( order != NULL )
                    {
                        pasa_convert_to_pproj (g, gtemp, order, nf) ;
                    }
                    df = Com->df = cg_dot (g, d, n) ;
                    if ( (df == df) && (df < CGINF) && (df > -CGINF) )
                    {
                        break ;
                    }
                }
                if ( i == Parm->ninf_tries )
                {
                    return (PASA_FUNCTION_NAN_OR_INF) ;
                }
                *Alpha = alpha ;
            }
            if ( Com->use_penalty == TRUE )
            {
                Com->df += Com->dp + alpha*Com->Ad2 ;
            }
        }
        return (PASA_OK) ;
    }
}

/* ==========================================================================
   === pasa_check_feas ======================================================
   ==========================================================================
   Check feasibility of current iterate
   ========================================================================== */
void pasa_check_feas
(
    PASAcom    *Com /* PASAcom structure */
)
{
    int location, status, use_napheap, use_pproj ;
    PASAFLOAT cost, fp, fl, lo_err, hi_err, bl_err, bu_err, g_err, s, t,
              *a, *Ax, *bl, *bu, *lo, *hi, *gpen, *Gpen, *temp, *x ;
    PASAINT i, j, nrow, ncol, ucol, nf, p, q, *Ai, *Ap, *order ;
    PASAparm *Parm ;
    int const loExists = Com->loExists ;
    int const hiExists = Com->hiExists ;
    int const QP       = Com->QP ;
    use_napheap = Com->use_napheap ;
    use_pproj = Com->use_pproj ;
    Parm = Com->pasaparm ;
    ncol = Com->ncol ;
    ucol = Com->ucol ;
    nrow = Com->nrow ;
    x = Com->userx ;
    nf = Com->nf ;

    location = Com->location ;
    order = NULL ; /* do not reorder the variables */
    if ( (location == PASA_ACTIVE_GP) || (location == PASA_CG_DESCENT) )
    {
        if ( Com->order_with_ifree == TRUE )
        {
            order = Com->ifree ;
        }
        else
        {
            if ( Com->order_with_colperm == TRUE )
            {
                order = Com->colperm ;
            }
        }
    }
    if ( (location == PASA_GRAD_PROJ) || (location == PASA_GRADPROJ_LP) )
    {
        if ( Com->order_with_colperm == TRUE )
        {
            order = Com->colperm ;
        }
    }

    if ( QP )
    {
        order = NULL ;
        if ( (Com->order_with_ifree == TRUE) ||
             (Com->order_with_colperm == TRUE) )
        {
            order = Com->ifree ;
            if ( Com->order_with_ifree == FALSE )
            {
                order = Com->colperm ;
            }
        }
    }

    if ( order != NULL )
    {
        for (j = 0; j < nf; j++)
        {
            x [order [j]] = Com->x [j] ;
        }
    }
#ifndef NDEBUG
    lo = Com->LO ;
    hi = Com->HI ;
#endif
    if ( nrow > 0 )
    {
#ifndef NDEBUG
        bl = Com->BL ;
        bu = Com->BU ;
        if ( use_pproj == TRUE )
        {
            Ax = Com->AX ;
            Ai = Com->AI ;
            Ap = Com->AP ;
        }
        else if ( use_napheap == TRUE )
        {
            a = Com->nap_a ;
        }
#endif
        temp = (PASAFLOAT *) pasa_malloc (&status, nrow, sizeof (PASAFLOAT)) ;
        pasa_initx (temp, PASAZERO, nrow) ;
    }
    lo_err = PASAZERO ;
    hi_err = PASAZERO ;
    p = 0 ;
    for (j = 0; j < ucol; j++)
    {
        s = x [j] ;
        if ( loExists && (s < lo [j]) )
        {
            /* printf ("j: %i x: %e lo: %e err: %e\n",
                        j, s, lo [j], lo [j]-s) ; */
            t = lo [j] - s ;
            lo_err = PASAMAX (t, lo_err) ;
        }
        if ( hiExists && (s > hi [j]) )
        {
            /* printf ("j: %i x: %e hi: %e err: %e\n",
                        j, s, hi [j], s - hi [j]) ; */
            t = s - hi [j] ;
            hi_err = PASAMAX (t, hi_err) ;
        }
        if ( nrow > 0 )
        {
            if ( use_pproj == TRUE )
            {
                q = Ap [j+1] ;
                for (; p < q; p++)
                {
                    temp [Ai [p]] += s*Ax [p] ;
                }
            }
            else if ( use_napheap == TRUE )
            {
                temp [0] += a [j]*s ;
            }
        }
    }
    bl_err = PASAZERO ;
    bu_err = PASAZERO ;
    for (i = 0; i < nrow; i++)
    {
        s = temp [i] ;
        if ( s < bl [i] )
        {
            t = bl [i] - s ;
            /*printf ("i: %i A*x: %e bl: %e err: %e\n",
                       i, s, bl [i], bl [i] - s) ;*/
            bl_err = PASAMAX (t, bl_err) ;
        }
        if ( s > bu [i] )
        {
            t = s - bu [i] ;
            /* printf ("i: %i A*x: %e bu: %e err: %e\n",
                        i, s, bu [i], s - bu [i]) ; */
            bu_err = PASAMAX (t, bu_err) ;
        }
    }
    printf ("feasibility error: lo: %e hi: %e bl: %e bu: %e\n",
             lo_err, hi_err, bl_err, bu_err) ;
    if ( nrow > 0 )
    {
        pasa_free (temp) ;
    }
    temp = (PASAFLOAT *) pasa_malloc (&status, ucol, sizeof (PASAFLOAT)) ;
    /* evaluate func */
    if ( QP )
    {
        Com->hprod (temp, x, NULL, ucol, ucol) ;   /* g = Q*x */
        cost = 0.5*pasa_dot (temp, x, ucol) ;      /* f = .5*x'Qx */
        if ( Com->userc != NULL )
        {
            cost += pasa_dot(x, Com->userc, ucol) ; /*f = .5*x'*Qx + c'*x*/
            pasa_step (temp, temp, Com->userc, PASAONE, ucol) ;/* g = Qx+c */
        }
        /* cost += Parm->fadjust ; */      /* user adjustment moved to wrapup */
    }
    else
    {
        Com->value (temp, x, ucol) ;
        cost = temp [0] ;
        Com->grad (temp, x, ucol) ; /* evaluate grad */
    }

    fp = PASAZERO ;
    fl = PASAZERO ;
    /* add penalty term to objective if it is present */
    gpen = NULL ;
    if ( (Com->location == PASA_CG_DESCENT) && (Com->use_penalty == TRUE)
                                            && (QP == FALSE) )
    {
        PPINT *ATp, *ATi, *ir ;
        PPFLOAT c, *ATx, *b, *lambda ;
        PPwork *Work ;
        if ( use_pproj == TRUE )
        {
            Work = Com->ppcom->Work ;
            b = Com->b ;
            ATp = Work->ATp ;
            ATi = Work->ATi ;
            ATx = Work->ATx ;
            ir = Work->ir ;
        }
        gpen = (PASAFLOAT *) pasa_malloc (&status, ncol, sizeof (PASAFLOAT)) ;
        c = 0.5*Com->penalty ;
        lambda = Com->lambda_pen ;   /* lambda for penalty */
        Gpen =  Com->gpen ;          /* gradient of penalty term */
        pasa_copyx (gpen, Gpen, nf) ;
        for (i = 0; i < nrow; i++)
        {
            if ( use_pproj == TRUE )
            {
                if ( ir [i] == 0 )
                {
                    s = CGZERO ;
                    q = ATp [i+1] ;
                    for (p = ATp [i]; p < q; p++)
                    {
                        s += ATx [p]*Com->x [ATi [p]] ;
                    }
                    t = b [i] - s ;
                    fl += lambda [i]*t ;
/*printf ("i: %i t: %25.15e lambda: %25.15e\n", i, t, lambda [i]) ;*/
                    fp += c*t*t ;
                    s = -(t*Com->penalty + lambda [i]) ;
                    for (p = ATp [i]; p < q; p++)
                    {
                        gpen [ATi [p]] -= ATx [p]*s ;
                    }
                }
            }
            else if ( use_napheap == TRUE )
            {
                s = pasa_dot (a, Com->x, nf) ;
                t = Com->nap_bl - s ;
                fl += lambda [i]*t ;
                fp += c*t*t ;
                s = -(t*Com->penalty + lambda [i]) ;
                for (j = 0; j < nf; j++)
                {
                    gpen [j] -= a [j]*s ;
                }
            }
        }
        cost += fp + fl ;
        printf ("penalty error: %e penalty: %e\n", Com->fp - fp - fl, fp + fl) ;
        printf ("sup-norm penalty grad error: %e, sup norm grad penalty: %e\n",
                 pasa_sup_norm (gpen, nf), pasa_sup_norm (Gpen, nf)) ;
    }
    printf ("function value error: %e function value: %25.15e "
            "penalty: %25.15e lambda term: %25.15e\n",
             cost - Com->f, cost, fp, fl) ;

    /* gradient error */
    g_err = PASAZERO ;
    if ( order == NULL )
    {
        for (j = 0; j < Com->nf; j++)
        {
            t = fabs (Com->g [j] - temp [j]) ;
            g_err = PASAMAX (t, g_err) ;
        }
    }
    else
    {
        for (j = 0; j < Com->nf; j++)
        {
            t = fabs (Com->g [j] - temp [order [j]]) ;
            g_err = PASAMAX (t, g_err) ;
        }
    }
    printf ("gradient error: %e\n", g_err) ;

    /* in CG check that d lies in null space of current active constraints */
    if ( (Com->location == PASA_CG_DESCENT) && (Com->Aexists == TRUE) &&
         (use_pproj == TRUE) )
    {
        PPwork *Work ;
        PPINT *ATp, *ATi, *ir ;
        PPFLOAT *ATx, *d ;
        Work = Com->ppcom->Work ;
        ir = Work->ir ;
        ATp = Work->ATp ;
        ATi = Work->ATi ;
        ATx = Work->ATx ;
        d = Com->d ;
        for (i = 0; i < nrow; i++)
        {
            if ( ir [i] == 0 )
            {
                s = PASAZERO ;
                t = PASAZERO ;
                q = ATp [i+1] ;
                for (p = ATp [i]; p < q; p++)
                {
                    t = PASAMAX (t, fabs(ATx [p]*d [ATi [p]])) ;
                    s += ATx [p]*d [ATi [p]] ;
                }
                if ( t > 1.e-10 )
                {
                    if ( fabs (s/t) > Parm->debugtol )
                    {
                        printf ("d not in null space of active constraints\n") ;
                        printf ("row: %ld A*d: %e abs(A*d): %e\n",
                               (LONG) i, s, t) ;
                        /* pasa_error (-1, __FILE__, __LINE__, "STOP") ;*/
                    }
                }
            }
        }
    }
    else if ( (Com->location == PASA_CG_DESCENT) && (Com->Aexists == TRUE) &&
              (use_napheap == TRUE) )
    {
        PASAFLOAT *d ;
        d = Com->d ;
        s = PASAZERO ;
        t = PASAZERO ;
        for (j = 0; j < nf; j++)
        {
            t = PASAMAX (t, fabs (a [j]*d [j])) ;
            s += a [j]*d [j] ;
        }
        if ( t > 1.e-10 )
        {
            if ( fabs (s/t) > Parm->debugtol )
            {
                printf ("d not in null space of active constraints\n") ;
                printf ("row: %ld A*d: %e abs(A*d): %e\n",
                       (LONG) 0, s, t) ;
            }
        }
    }

    if ( gpen != NULL )
    {
        pasa_free (gpen) ;
    }
    pasa_free (temp) ;
}

double pasa_time (void)
{
    struct timeval tv ;
    gettimeofday (&tv, NULL) ;
    return ((double) tv.tv_sec + ((double) tv.tv_usec)/1.e6) ;
}

void check_zero
(
    PASAFLOAT *x,
    PASAINT    n,
    int        l
)
{
    PASAINT j ;
    for (j = 0; j < n; j++)
    {
        if ( x [j] != PASAZERO )
        {
             printf ("location: %ld component %ld value: %e\n",
                      (LONG) l, (LONG) j, x [j]) ;
             pproj_error (-1, __FILE__, __LINE__, "stop\n") ;
        }
    }
}
