/* =========================================================================
   ============================== pproj_phase1 =============================
   =========================================================================
    A starting point is generated by first requiring that lambda lies in
    the domain of the dual function. We then alternate between a dual
    coordinate ascent iteration and an SSOR preconditioned gradient ascent
    iteration. Throughout phase1, the current estimate for lambda is the
    sum of the lambda variable in the code and shift_l. A detailed
    description for the SSOR preconditioned gradient ascent iteration is
    provided at the start of the ssor0 code.
   ========================================================================== */

#include "pproj.h"

int pproj_phase1
(
    PPcom *I
)
{
    int     flag, ibj, prevblk, *ib, *sol_to_blk ;
    PPINT   phase1_its, i, j, jmin, jmax, jsing, k,
            first, last, l, m, kk, p, p0, p1, col, row, maxrow,
            brk, nbrk, nup, it, nr, nf, ineqflag, Rl, Ul, Ll,
           *AFTi, *AFTnz, *AFTp,
           *Br_index, *Br_order, *Heap, *F, *R, *RLinkDn, *RLinkUp,
           *slo, *shi, *uLinkUp, *uLinkDn,
           *lLinkUp, *lLinkDn, *ir, *ns, *worki, *iwork ;

    PPFLOAT ax, b_row, Br, cmin, cmax, sigma, xbound,
            s, t, u, st, st0, fd, fd0, fn, sd, dknew, snew, errdual, yj,
           *AFTx, *b, *c, *lambda, *shift_l,
           *Br_value0, *Br_value, *D, *dk, *pA, *gk, *workd, *x, *absAx ;

    PPprob *Prob ;
    PPparm *Parm ;
    PPstat *Stat ;
    PPwork    *W ;

#ifndef NDEBUG
    char   *where ;
    I->Check->location = 1 ; /* code operates in phase1 */
#endif
    /* extract the parameters, problem, statistics, and work structures from I*/
    Parm = I->Parm ;
    Prob = I->Prob ;
    Stat = I->Stat ;
    W = I->Work ;

    int const debug = Parm->debug ;
    int const PrintLevel = Parm->PrintLevel ;
    int const loExists = I->Work->loExists ;
    int const hiExists = I->Work->hiExists ;

    /* Problem data */
    PPINT   const       *Ap = Prob->Ap ;
    PPINT               *Ai = Prob->Ai ;
    PPFLOAT             *Ax = Prob->Ax ;
    PPFLOAT const       *lo = Prob->lo ;
    PPFLOAT const       *hi = Prob->hi ;
    PPFLOAT const    *singc = Prob->singc ;
    PPINT   const      ncol = Prob->ncol ;
    PPINT   const      nrow = Prob->nrow ;
    PPINT   const        ni = Prob->ni ;
    PPINT   const     nsing = Prob->nsing ;
    PPINT   const   nsingni = nsing + ni ;
    PPINT   const  nsingni1 = nsingni + 1 ;
    PPINT   const  nsingni2 = nsingni + 2 ;
    PPINT   const      ntot = nsingni1 + ncol ;
    PPINT   const *ineq_row = Prob->ineq_row ;
    PPINT   const *row_sing = Prob->row_sing ;
    PPINT   const *row_sing1= row_sing+1 ;
    PPFLOAT const       *bl = (nsing) ? Prob->singlo : Prob->bl ;
    PPFLOAT const       *bu = (nsing) ? Prob->singhi : Prob->bu ;
    PPFLOAT const  grad_tol = Parm->grad_tol ;

    /* Transpose of A */
    PPINT   const *ATp = W->ATp ;
    PPINT   const *ATi = W->ATi ;
    PPFLOAT const *ATx = W->ATx ;

    /* Transpose of AF */
    AFTp  = W->AFTp ;
    AFTnz = W->AFTnz ;
    AFTi  = W->AFTi ;
    AFTx  = W->AFTx ;

    /* Links point to active rows */
    RLinkUp = W->RLinkUp ;
    RLinkDn = W->RLinkDn ;

    /* Links point to active singletons. lLinkUp points to the strict
       inequalities with b_i = bl_i while uLinkUp points to the strict
       inequalities with b_i = bu_i. Since only one of these can hold for
       each i, lLinkUp and uLinkUp can be stored in the same array */
    lLinkUp = W->SLinkUp ; 
    lLinkDn = W->SLinkDn ;
    uLinkUp = W->SLinkUp ;
    uLinkDn = W->SLinkDn ;
    slo     = W->slo ;
    shi     = W->shi ;

    /* working arrays */
    lambda  = W->lambda ;
    shift_l = W->shift_l ;
    x       = W->x ;
    ib      = W->ib ;
    ir      = W->ir ;
    b       = W->b ; /* the part of grad L associated with bound variables */
    c       = W->c ; /* y + A'lambda */
    F       = W->F ;
    D       = W->D ;
    ns      = W->ns ;
    sigma   = W->sigma ;
    maxrow  = Prob->maxrow ; /* max number of nonzeros in a row of A */

    /* initializations */
    pproj_initi (AFTnz, (PPINT) 0, nrow) ;
    pproj_copyx (b, Prob->b, nrow) ;
    pproj_copyx (c, Prob->y, ncol) ;
    pproj_initx (D, sigma, nrow) ;

    /* ======== start allocations ========
    -> work arrays in coordinate ascent step

       Br_index  - int maxrow
       Br_order  - int maxrow
       iwork     - int maxrow
       Br_value0 - double maxrow (maxrow <= ncol)

    -> work arrays in ssor step:

       Heap      - int    ntot, line search
       pA        - double ncol, A'lambda, share same space with Br_value0
       dk        - double nrow, search direction
       Br_value  - double ncol + nsing + ni + 1
       gk        - double nrow, used in ssor iteration

    -> work arrays after ssor step in constraint updates:

       R         - int    nrow, AFT bookkeeping after the line search
       pA        - double ncol */

    /* coordinate ascent step before ssor step */
    worki = W->arrayi ;
    Br_index  = worki ; worki += maxrow ;
    Br_order  = worki ; worki += maxrow ;
    iwork     = worki ; worki += maxrow ;

    workd = W->arrayd ;
    Br_value0 = workd ; workd += maxrow ;

    /* ssor step */
    worki = W->arrayi ;
    Heap      = worki ; worki += ntot ;

    workd = W->arrayd ;
    pA       = workd ; workd += ncol ; /* if pA is moved, tweek check_line */
    dk       = workd ; workd += nrow ;
    Br_value = workd ; workd += ncol + ni + 1 ;
    gk       = workd ; workd += nrow ; /* grad L (lambda) */

    /* constraint update */
    worki = W->arrayi ;
    R         = worki ; worki += nrow ;

    /* ======== end of allocations ======== */

    /* ---------------------------------------------------------------------- */
    /* Setup for the dual coordinate ascent and SSOR iterations */
    /* 1. Check that starting guess is feasible.
       2. Set b.
       3. Set lLink and uLink (inequalities active at lower/upper bounds) */
    /* ---------------------------------------------------------------------- */
    Ll = nsingni1 ;
    Ul = nsingni2 ;
    Rl = nrow ;
    int start_guess = Parm->start_guess ;
    if ( ni )
    {
        if ( (start_guess == 1) || (start_guess == 3) )
        {
            /* start_guess = 1 => lambda  = 0, shift_l = start guess
                           = 3 => shift_l = 0, lambda  = start guess */
            PPFLOAT *Lambda = (start_guess == 1) ? shift_l : lambda ;
            for (i = 1; i <= ni; i++)
            {
                row = ineq_row [i] ;
                t = Lambda [row] ;
                if ( t > PPZERO )
                {
                    /* if Lambda infeasible, set it to zero and drop the row */
                    if ( bl [i] == -PPINF )
                    {
                        Lambda [row] = PPZERO ;
                    }
                    else
                    {
                        b [row] = bl [i] ;
                        ir [row] = -i ;
                        lLinkDn [i] = Ll ;
                        lLinkUp [Ll] = i ;
                        Ll = i ;
                    }
                }
                else if ( t < PPZERO )
                {
                    /* if Lambda infeasible, set it to zero and drop the row */
                    if ( bu [i] == PPINF )
                    {
                        Lambda [row] = PPZERO ;
                    }
                    else
                    {
                        b [row] = bu [i] ;
                        ir [row] = i ;
                        uLinkDn [i] = Ul ;
                        uLinkUp [Ul] = i ;
                        Ul = i ;
                    }
                }
                if ( Lambda [row] == PPZERO )
                {
                    ir [row] = i + ni ;
                }
            }
        }
        else /* start_guess == 0 => lambda = shift_l = 0 */
        {
            /* make all inequalities inactive */
            for (i = 1; i <= ni; i++)
            {
                ir [ineq_row [i]] = i + ni ;
                ASSERT (lambda [ineq_row [i]] == PPZERO) ;
                ASSERT (shift_l [ineq_row [i]] == PPZERO) ;
            }
        }
    }
    else if ( nsing )
    {
        /* The elements of c are sorted in increasing order for the columns
           singletons in each row. lLink is used to keep track of the index
           of the c component that is first hit when lambda [i] decreases while
           uLink points to the c component that is first hit when lambda [i]
           increases. slo [i] stores the component j associated with lLink
           while shi [i] stores the component j associated with uLink.
           slo and shi are indexed by row numbers, while lLink and uLink
           are indexed by singleton number */

        /* if start_guess = 0 or 3, use lambda to adjust for feasibility,
           for start_guess = 1, we use shift_l */
        PPFLOAT *Lambda = (start_guess == 1) ? shift_l : lambda ;

        for (j = 1; j <= nsing; )
        {
            PPINT const j0 = j ;
            PPINT  j_of_c0 = 0 ;
            PPINT j_switch = 0 ;
            cmin = PPINF ;
            cmax =-PPINF;
            int notfound = TRUE ;
            row = ineq_row [j] ;
            PPINT const rowend = row_sing1 [row] ;
            PPFLOAT const lambda_row = Lambda [row] ;
            /* find range of lambda [row] for which the dual function finite */
            for (; j < rowend; j++)
            {
                PPFLOAT const cj = singc [j] ;
                /* NOTE: bu = singhi, bl = singlo */
                if ( bu [j] == PPINF )
                {
                    /* need lambda >= singc or else dual val = -INF, hence,
                       store maximum (cmax) of singc, need lambda >= cmax,
                       since the cj are increasing, always store cj  */
                    jmax = j ;
                    cmax = cj ;
                }
                /* since cj's increase, cmin corresponds to the first j
                   where bl [j] = -infinity */
                if ( notfound )
                {
                    if ( bl [j] == -PPINF )
                    {
                        /* need lambda <= singc or else dual val = -INF, hence,
                           store minimum cmin of singc, need lambda <= cmin */
                        jmin = j ;
                        cmin = cj ;
                        notfound = FALSE ;
                    }
                }
                if ( lambda_row <= cj )
                {
                    j_switch = j ;
                    if ( lambda_row == cj ) j_of_c0 = j ;
                    break ;
                }
            }
            for (j++; j < rowend; j++)
            {
                PPFLOAT const cj = singc [j] ;
                /* NOTE: bu = singhi, bl = singlo */
                if ( bu [j] == PPINF )
                {
                    /* need lambda >= singc or else dual val = -INF, hence,
                       store maximum cmax of singc, need lambda >= cmax,
                       since the cj are increasing, always store cj  */
                    jmax = j ;
                    cmax = cj ;
                }
                if ( notfound )
                {
                    if ( bl [j] == -PPINF )
                    {
                        /* need lambda <= singc or else dual val = -INF, hence,
                           store minimum cmin of singc, need lambda <= cmin */
                        jmin = j ;
                        cmin = cj ;
                        /* since cj's increase, we have found the cmin */
                        notfound = FALSE ;
                    }
                }
            }
            /* need cmax <= lambda <= cmin for dual feasibility */
            if ( cmin < cmax )
            {
                return (PPROJ_OPTIMAL_COST_IS_MINUS_INFINITY) ;
            }
            jsing = j_of_c0 ; /* 0 unless cj = lambda_row for some j */
            /* if current lambda value is infeasible in dual problem then
               adjust its value to achieve feasibility */
            if ( lambda_row < cmax )
            {
                jsing = jmax ;
                Lambda [row] = cmax ;
            }
            else if ( lambda_row > cmin )
            {
                jsing = jmin ;
                Lambda [row] = cmin ;
            }
            b_row = PPZERO ;
            if ( jsing ) /* if jsing != 0, then row is dropped */
            {
                PPFLOAT const lambdai = Lambda [row] ;
                ir [row] = jsing + nsing ;
                for (j = j0; j < jsing;  j++) b_row += bl [j] ;
                for (j++;    j < rowend; j++) b_row += bu [j] ;
                /* if lambdai is nonzero, also update c */
                if ( lambdai )
                {
                    PPINT const q = ATp [row+1] ;
                    for (p = ATp [row]; p < q; p++)
                    {
                        c [ATi [p]] += lambdai*ATx [p] ;
                    }
                }
            }
            else /* the row is active and it contains singletons */
            {
                ir [row] = 1 ;
                if ( j_switch == 0 ) /* lambda_row - singc always positive*/
                {
                    for (j = j0; j < rowend; j++) b_row += bl [j] ;
                    k = rowend - 1 ;
                    slo [row] = k ;
                    shi [row] = 0 ;
                    lLinkUp [Ll] = k ;
                    lLinkDn [k] = Ll ;
                    Ll = k ;
                }
                else
                {
                    uLinkUp [Ul] = j_switch ;
                    uLinkDn [j_switch] = Ul ;
                    Ul = j_switch ;
                    if ( j_switch > j0 )
                    {
                        for (j = j0; j < j_switch; j++) b_row += bl [j];
                        for (      ; j < rowend;   j++) b_row += bu [j];
                        shi [row] = j_switch ;
                        k = j_switch - 1 ;
                        slo [row] = k ;
                        lLinkUp [Ll] = k ;
                        lLinkDn [k] = Ll ;
                        Ll = k ;
                    }
                    else /* lambda_row - singc always negative */
                    {
                        for (j = j0; j < rowend; j++) b_row += bu [j] ;
                        slo [row] = 0 ;
                        shi [row] = j0 ;
                    }
                }
            }
            /* b = Prob->b initially, now add in the contribution from
               column singletons */
            b [row] += b_row ;
        }
    }
    lLinkUp [Ll]       = nsingni1 ;
    uLinkUp [Ul]       = nsingni2 ;
    lLinkDn [nsingni1] = Ll ;
    uLinkDn [nsingni2] = Ul ;

    /* when start_guess is 0, the only changes to c are done above in
       the column singleton section */
    if ( start_guess == 0 )
    {
        W->shiftl_is_zero = TRUE ;
        if ( nsingni == 0 ) /* all the rows are active */
        {
            for (i = 0; i < nrow; i++)
            {
                RLinkDn [i] = Rl ;
                RLinkUp [Rl] = i ;
                Rl = i ;
            }
        }
        else /* need to see which rows are active */
        {
            for (i = 0; i < nrow; i++)
            {
                if ( ir [i] <= nsingni ) /* row is active */
                {
                    RLinkDn [i] = Rl ;
                    RLinkUp [Rl] = i ;
                    Rl = i ;
                }
            }
        }
    }
    else /* start_guess == 1 or 3 */
    {
        PPFLOAT *Lambda = (start_guess == 1) ? shift_l : lambda ;
        if ( nsingni == 0 ) /* all the rows are active, no need to check ir */
        {
            for (i = 0; i < nrow; i++)
            {
                RLinkDn [i] = Rl ;
                RLinkUp [Rl] = i ;
                Rl = i ;
                PPFLOAT const lambdai = lambda [i] ;
                if ( lambdai != PPZERO )
                {
                    PPINT const q = ATp [i+1] ;
                    for (p = ATp [i]; p < q; p++)
                    {
                        c [ATi [p]] += lambdai*ATx [p] ;
                    }
                }
            }
        }
        else /* not all rows are active, need to check ir */
        {
            for (i = 0; i < nrow; i++)
            {
                if ( ir [i] <= nsingni ) /* row is active */
                {
                    RLinkDn [i] = Rl ;
                    RLinkUp [Rl] = i ;
                    Rl = i ;

                    /* always need to update c when row is active */
                    PPFLOAT const lambdai = Lambda [i] ;
                    if ( lambdai != PPZERO )
                    {
                        PPINT const q = ATp [i+1] ;
                        for (p = ATp [i]; p < q; p++)
                        {
                            c [ATi [p]] += lambdai*ATx [p] ;
                        }
                    }
                }
            }
        }
    }

    RLinkDn [nrow]     = Rl ;
    RLinkUp [Rl]       = nrow ;

    /* 1. evaluate b - A_B * x_B
       2. set ib [j] = +1 if x_j at upper bound
              ib [j] = -1 if x_j at lower bound
       3. evaluate AFT, diag of AF*AF', and F */
    nf = 0 ;
    p = 0 ;
    for (j = 0; j < ncol; j++)
    {
        PPINT const q = Ap [j+1] ;
        t = c [j] ;
        if ( hiExists && (t > hi [j]) )
        {
            ib [j] = 1 ;
            t = hi [j] ;
#ifndef NDEBUG
            x [j] = t ;
#endif
            if ( t != PPZERO )
            {
                for (; p < q; p++)
                {
                    b [Ai [p]] -= t*Ax [p] ;
                }
            }
            else
            {
                p = q ;
            }
        }
        else if ( loExists && (t < lo [j]) )
        {
            ib [j] =-1 ;
            t = lo [j] ;
#ifndef NDEBUG
            x [j] = t ;
#endif
            if ( t != PPZERO )
            {
                for (; p < q; p++)
                {
                    b [Ai [p]] -= t*Ax [p] ;
                }
            }
            else
            {
                p = q ;
            }
        }
        else
        {
            ib [j] = 0 ;
            F [nf++] = j ;
#ifndef NDEBUG
            x [j] = PPZERO ;
#endif
            for (; p < q; p++)
            {
                i = Ai [p] ;
                k = AFTp [i] + AFTnz [i]++ ;
                AFTi [k] = j ;
                ax = Ax [p] ;
                AFTx [k] = ax ;
                D [i] += ax*ax ;
            }
        }
    }

    /* ----------------------------------------------------------------------
       If there are no phase1 iterations and inequalities are present,
       then test whether any inactive inequalities should be activated.
       ---------------------------------------------------------------------- */
    if ( Parm->phase1 < 0 )
    {
        phase1_its = 0 ; /* no phase1 iterations */
        /* all rows were initialized to be inactive, if we skip phase1,
           then we need to locate the active rows */
        Ll = nsingni1 ;
        Ul = nsingni2 ;
        Rl = nrow ;
        for (i = 0 ; i < nrow ; i++)
        {
            j = ir [i] ;
            if ( j <= nsingni ) /* equality row */
            {
                Rl = i ;
                if ( j < 0 )
                {
                    Ll = -j ;
                }
                else if ( j > 0 )
                {
                    Ul = j ;
                }
            }
            else          /* inactive inequality row */
            {
                t = b [i] ; /* contains bound variables */
                p = AFTp [i] ;
                PPINT const q = p + AFTnz [i] ;
                for (; p < q; p++)
                {
                    t -= c [AFTi [p]]*AFTx [p] ; /* (b-A*x(lambda))_i */
                }
                j -= nsingni ;
                /* check if lambda_i > 0 increases L */
                if ( t + bl [j] > PPZERO )
                {
                    if ( PrintLevel > 1 )
                    {
                        printf ("add row: %ld (lower bind)\n", (LONG) i) ;
                    }
                    /* add row to RLink, add sing to lLink (both are active) */
                    ADD_IN_R_AND_LLINKS(i,j) ;
                }
                /* check if lambda_i < 0 increases L */
                else if ( t + bu [j] < PPZERO )
                {
                    if ( PrintLevel > 1 )
                    {
                        printf ("add row: %ld (upper bind)\n", (LONG) i) ;
                    }
                    /* row is active, add it to RLink */
                    /* add row to RLink, add sing to uLink (both are active) */
                    ADD_IN_R_AND_ULINKS(i,j) ;
                }
            }
        }
    }
    else /* determine the number of phase1 iterations */
    {
        k = pow ((double) nrow, Parm->phase1) ;
        phase1_its = PPMAX (k, 5) ; /* number of iterations in phase 1 */
    }
#ifndef NDEBUG
    where = "at end of initialization in phase 1" ;
    pproj_checkc (I, where) ;
    /* save dual objective but do not check for increase */
    pproj_check_dual (I, NULL, where, TRUE, FALSE) ;
#endif
    if ( PrintLevel )
    {
        printf ("perform %i iterations in phase1\n", phase1_its) ;
    }

    for (it = 0; it < phase1_its ; )
    {
        /* printf ("================ it %d\n", it) ; */

        /* ------------------------------------------------------------------ */
        /* for each active row, do: */
        /* ------------------------------------------------------------------ */

#ifndef NDEBUG
        pproj_checkb (I, "start of phase1 iteration") ;
#endif
        ineqflag = 0 ;
        for (row = RLinkUp [nrow]; row < nrow; row = RLinkUp [row])
        {
            fd = b [row] - sigma*lambda [row] ;
            ASSERT (ir [row] <= nsingni) ;
            PPINT const ir_row = ir [row] ;

            p = AFTp [row] ;
            p1 = p + AFTnz [row] ;
            for (; p < p1; p++)
            {
                fd -= c [AFTi [p]]*AFTx [p] ;
            }

            /* -------------------------------------------------------------- */
            /* Evaluate break points as we search in direction of + deriv. */
            /* -------------------------------------------------------------- */

            nbrk = 0 ;
            fd0 = fd ;
            sd = D [row] ;
            st = fd/sd ;
            fd = fabs (fd) ;
            if ( PrintLevel > 2 )
            {
                printf ("active row: %ld initial st: %e fd0: %e\n",
                         (LONG) row, st, fd0) ;
            }

            if ( fd0 > PPZERO ) /* lambda [row] increases */
            {
                if ( ni )
                {
                    /* ir_row > 0 => row may drop when lambda grows */
                    if ( ir_row > 0 )
                    {
                        if ( W->shiftl_is_zero )
                        {
                            t = -lambda [row] ;
                        }
                        else
                        {
                            t = -(lambda [row] + shift_l [row]) ;
                        }
                        if ( t < st )
                        {
                            ineqflag = ir_row ;
                            st = t ;
                        }
                    }
                }
                else if ( nsing )
                {
                    /* when lambda increase, hi could switch to lo */
                    if ( ir_row && (j = shi [row]) ) /* lambda < max singc*/
                    {
                        if ( W->shiftl_is_zero )
                        {
                            t = singc [j] - lambda [row] ;
                        }
                        else
                        {
                            t = singc [j] - (lambda [row] + shift_l [row]) ;
                        }
                        ASSERT (t >= PPZERO) ;
                        if ( t < st )
                        {
                            ineqflag = j ;
                            st = t ;
                        }
                    }
                }
                st0 = st ;

                /* find all break points < st */
                PPINT const q = ATp [row+1] ;
                for (p = ATp [row] ; p < q; p++)
                {
                    j = ATi [p] ;
                    if ( ib [j] )
                    {
                        ax = ATx [p] ;
                        if ( ax  > PPZERO )
                        {
                            if ( ib [j] < 0 )
                            {
                                t = (lo [j] - c [j])/ax ;
                                if ( t < st )
                                {
                                    Br_value0 [nbrk] = t ;
                                    Br_index [nbrk] = p ;
                                    nbrk++ ;
                                }
                            }
                        }
                        else
                        {
                            if ( ib [j] > 0 )
                            {
                                t = (hi [j] - c [j])/ax ;
                                if ( t < st )
                                {
                                    Br_value0 [nbrk] = t ;
                                    Br_index [nbrk] = p ;
                                    nbrk++ ;
                                }
                            }
                        }
                    }
                }
            }
            else if ( fd0 < 0 ) /* lambda [row] decreases */
            {
                if ( ni )
                {
                    /* ir_row < 0 => row may drop when lambda decreases */
                    if ( ir_row < 0 )
                    {
                        if ( W->shiftl_is_zero )
                        {
                            t = -lambda [row] ; /* t < 0 */
                        }
                        else
                        {
                            t = -(lambda [row] + shift_l [row]) ;
                        }
                        if ( t > st )   /* 0 > t > st */
                        {
                            ineqflag = ir_row ;
                            st = t ;
                        }
                    }
                }
                else if ( nsing )
                {
                    /* when lambda decreases, lo could switch to hi */
                    if ( ir_row && (j = slo [row]) ) /* lambda < max singc*/
                    {
                        if ( W->shiftl_is_zero )
                        {
                            t = singc [j] - lambda [row] ;
                        }
                        else
                        {
                            t = singc [j] - (lambda [row] + shift_l [row]) ;
                        }
                        ASSERT (t <= PPZERO) ;
                        if ( t > st ) /* 0 > t > st */
                        {
                            ineqflag = -j ;
                            st = t ;
                        }
                    }
                }
                st0 = st = -st ;  /* convert st < 0 to st > 0 */

                /* find all break points < st */
                PPINT const q = ATp [row+1] ;
                for (p = ATp [row] ; p < q; p++)
                {
                    j = ATi [p] ;
                    if ( ib [j] )
                    {
                        ax = ATx [p] ;
                        if ( ax  > PPZERO )
                        {
                            if ( ib [j] > 0 )
                            {
                                t = (c [j] - hi [j])/ax ;
                                if ( t < st )
                                {
                                    Br_value0 [nbrk] = t ;
                                    Br_index [nbrk] = p ;
                                    nbrk++ ;
                                }
                            }
                        }
                        else
                        {
                            if ( ib [j] < 0 )
                            {
                                t = (c [j] - lo [j])/ax ;
                                if ( t < st )
                                {
                                    Br_value0 [nbrk] = t ;
                                    Br_index [nbrk] = p ;
                                    nbrk++ ;
                                }
                            }
                        }
                    } /* bound variable, check for break */
                }     /* end of loop over the row */
            }         /* find breaks */

            if ( nbrk > 0 )
            {
                pproj_xminsort (Br_order, Br_value0, iwork, nbrk) ;

#ifndef NDEBUG
                if ( debug > 1 )
                {
                    pproj_check_order (Br_value0, Br_order, nbrk,
                                      "phase1, coordinate ascent") ;
                }
#endif

                st = 0 ;
                for (brk = 0; brk < nbrk; brk++)
                {
                    kk = Br_order [brk] ;
                    Br = PPMAX (PPZERO, Br_value0 [kk]) ;
                    fn = fd - sd * (Br - st) ;
                    p = Br_index [kk] ;
                    s = ATx [p] ;
                    j = ATi [p] ;

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

                    /* x_j changes from bound to free */
#ifndef NDEBUG
                    x [j] = PPZERO ;
#endif
                    if ( ib [j] > 0 ) t = hi [j] ;
                    else              t = lo [j] ;
                    p1 = Ap [j+1] ;
                    for (p = Ap [j]; p < p1; p++)
                    {
                        i = Ai [p] ;
                        b [i] += t*(ax = Ax [p]) ;
                        D [i] += ax*ax ;
                        l = AFTp [i] + AFTnz [i]++ ;
                        AFTx [l] = ax ;
                        AFTi [l] = j ;
                    }
                    sd += s*s ;

#ifndef NDEBUG
                    if ( PrintLevel > 2 )
                    {
                        if ( ib [j] > 0 )
                        {
                            PRINTF("row: %ld free: %ld hi: %e\n",
                                 (LONG) row, (LONG) j, hi [j]);
                        }
                        else
                        {
                            PRINTF("row: %ld free: %ld lo: %e\n",
                                 (LONG) row, (LONG) j, lo [j]);
                        }
                    }
#endif

                    ib [j] = 0 ;
                    F [nf++] = j ;
                    st = Br ;
                }

                st += fd/sd ;

                if ( st >= st0 )
                {
                    st = st0 ;
                }
                else
                {
                    ineqflag = 0 ; /* the row did not drop */
                }
            }

            /* when slope < 0, take a negative step along lambda */
            if ( fd0 < 0 )
            {
                st = -st ;
            }

            if ( ineqflag ) /* the row dropped */
            {
                /* drop row */
                k = RLinkDn [row] ;
                j = RLinkUp [k] = RLinkUp [row] ;
                RLinkDn [j] = k ;
                ASSERT (ir [row] <= nsingni) ;
                if ( ineqflag > 0 ) /* remove from uLink */
                {
                    k = uLinkDn [ineqflag] ;
                    j = uLinkUp [k] = uLinkUp [ineqflag] ;
                    uLinkDn [j] = k ;
                    b [row] -= bu [ineqflag] ;
                    if ( nsing )
                    {
                        lambda [row] = singc [ineqflag] - shift_l [row] ;
                        shi [row] = 0 ;
                        /* also remove from lLink is it exists */
                        PPINT const sloj = slo [row] ;
                        if ( sloj )
                        {
                            slo [row] = 0 ;
                            k = lLinkDn [sloj] ;
                            j = lLinkUp [k] = lLinkUp [sloj] ;
                            lLinkDn [j] = k ;
                        }
                    }
                    else /* ni */
                    {
                        lambda [row] = -shift_l [row] ;
                    }
                }
                else                /* remove from lLink */
                {
                    ineqflag = -ineqflag ;
                    k = lLinkDn [ineqflag] ;
                    j = lLinkUp [k] = lLinkUp [ineqflag] ;
                    lLinkDn [j] = k ;
                    b [row] -= bl [ineqflag] ;
                    if ( nsing )
                    {
                        lambda [row] = singc [ineqflag] - shift_l [row] ;
                        slo [row] = 0 ;
                        /* also remove from uLink is it exists */
                        PPINT const shij = shi [row] ;
                        if ( shij )
                        {
                            shi [row] = 0 ;
                            k = uLinkDn [shij] ;
                            j = uLinkUp [k] = uLinkUp [shij] ;
                            uLinkDn [j] = k ;
                        }
                    }
                    else /* ni */
                    {
                        lambda [row] = -shift_l [row] ;
                    }
                }
                ir [row] = ineqflag + nsingni ;  /* Drop row */
                ineqflag = 0 ;

#ifndef NDEBUG
                if ( PrintLevel > 2 )
                {
                    PRINTF ("phase1 coordinate ascent drop row: %ld\n",
                            (LONG) row);
                }
#endif
            }                /* end of row drop */
            else
            {
                Rl = row ;
                lambda [row] += st ;
            }
            if ( st != PPZERO )
            {
                PPINT const q = ATp [row+1] ;
                for (p = ATp [row] ; p < q; p++)
                {
                    c [ATi [p]] += ATx [p] * st ;
                }
            }
            if ( PrintLevel > 2 )
            {
                printf ("row: %ld final st: %e\n", (LONG) row, st) ;
            }

#ifndef NDEBUG
            if ( debug > 1 )
            {
                where = "at end of coordinate ascent iteration in phase 1" ;
                pproj_check_link (I, (int *) NULL, 0, where) ;
                /* check that dual objective increases */
                pproj_check_dual (I, NULL, where, TRUE, TRUE) ;
                pproj_checkb (I, where) ;
                pproj_checkc (I, where) ;
            }
#endif

        } /* end of coordinate ascent iteration */

        /* ------------------------------------------------------------------ */
        /* right side for ssor step */
        /* ------------------------------------------------------------------ */

        first = RLinkUp [nrow] ;
        /* b = part of grad L (lambda) associated with bound variables.
           gk = grad L (lambda) for active rows + proximal term */

        if ( first < nrow )
        {
            for (i = first; i < nrow; i = RLinkUp [i])
            {
                t = b [i] ;
                ASSERT (ir [i] <= nsingni) ;
                p = AFTp [i] ;
                PPINT const q = p + AFTnz [i] ;
                for ( ; p < q ; p++)
                {
                    t -= AFTx [p]*c [AFTi [p]] ;
                }
                gk [i] = t - sigma*lambda [i] ;
            }

            /* -------------------------------------------------------------- */
            /* SSOR step */
            /* -------------------------------------------------------------- */

            for (k = 0; k < nf; k++)
            {
                pA [F [k]] = PPZERO ;
            }

            i = first ;
            t = dk [i] = gk [i]/D [i] ;

            last = RLinkDn [nrow] ;

            while (i < last)
            {

                ASSERT (ir [i] <= nsingni) ;

                p = AFTp [i] ;
                p1 = p + AFTnz [i] ;
                for ( ; p < p1 ; p++)
                {
                    pA [AFTi [p]] += AFTx [p]*t ;
                }

                i = RLinkUp [i] ;

                ASSERT (ir [i] <= nsingni) ;

                t = 0 ;

                p = AFTp [i] ;
                p1 = p + AFTnz [i] ;
                for ( ; p < p1 ; p++)
                {
                    t += AFTx [p]*pA [AFTi [p]] ;
                }
                t = dk [i] = (gk [i] - t)/D [i] ;
            }

            pproj_initx (pA, PPZERO, ncol) ;
            fd = t*gk [i] ;
            sd = t*t ;

            for (; i > first;)
            {
                p1 = ATp [i+1] ;
                for (p = ATp [i] ; p < p1 ; p++)
                {
                    pA [ATi [p]] += ATx [p]*t ;
                }

                i = RLinkDn [i] ;

                ASSERT (ir [i] <= nsingni) ;

                t = PPZERO ;

                p = AFTp [i] ;
                p1 = p + AFTnz [i] ;
                for ( ; p < p1 ; p++)
                {
                    t += AFTx [p]*pA [AFTi [p]] ;
                }
                t = dk [i] = dk [i] - t/D [i] ;
                fd += t*gk [i] ;
                sd += t*t ;
            }

            p1 = ATp [i+1] ;
            for (p = ATp [i] ; p < p1 ; p++)
            {
                pA [ATi [p]] += ATx [p]*t ;
            }

            sd *= sigma ;
            for (k = 0; k < nf; k++)
            {
                j = F [k] ;
                sd += pA [j]*pA [j] ;
            }

            if ( sd > PPZERO )
            {
                st0 = st = fd/sd ;
            }
            else
            {
                st = PPZERO ;
            }
        }
        else /* all equations have been dropped */
        {
            st = PPZERO ;
            if ( it == 0 )
            {
                pproj_initx (pA, PPZERO, ncol) ;
            }
        }
        if ( PrintLevel > 1 )
        {
            printf ("initial ssor st: %25.15e\n", st) ;
        }

        nup = 0 ;
        if ( st != PPZERO )
        {
            nbrk = 0 ;
            for (j = 0; j < ncol; j++)
            {
                ibj = ib [j] ;
                if ( ibj == 0 )
                {
                    continue ;
                }
                else if ( ibj < 0 )
                {
                    if ( pA [j] > 0 )
                    {
                        s = (lo [j] - c [j])/pA [j] ;
                        if ( s < st )
                        {
                            Br_value [j] = s ;
                            nbrk++ ;
                            Heap [nbrk] = j ;        /* add to heap  */
                        }
                    }
                }
                else
                {
                    if ( pA [j] < 0 )
                    {
                        s = (hi [j] - c [j])/pA [j] ;
                        if ( s < st )
                        {
                            Br_value [j] = s ;
                            nbrk++ ;
                            Heap [nbrk] = j ;        /* add to heap */
                        }
                    }
                }
            }

            if ( W->shiftl_is_zero )
            {
                for (j = lLinkUp [nsingni1]; j <= nsingni; j = lLinkUp [j])
                {
                    row = ineq_row [j] ;
                    if ( (t = dk [row]) < PPZERO )
                    {
                        /* lambda > 0 for lLinkUp */
                        if ( ni ) s = -lambda [row]/t ;
                        else      s = (singc [j] - lambda [row])/t ;
                        if ( s < st )
                        {
                            k = j + ncol ;
                            Br_value [k] = s ;
                            nbrk++ ;
                            Heap [nbrk] = k ; /* add to heap  */
                        }
                    }
                }
                for (j = uLinkUp [nsingni2]; j <= nsingni; j = uLinkUp [j])
                {
                    row = ineq_row [j] ;
                    if ( (t = dk [row]) > PPZERO )
                    {
                        /* lambda < 0 for uLinkUp */
                        if ( ni ) s = -lambda [row]/t ;
                        else      s = (singc [j] - lambda [row])/t ;
                        if ( s < st )
                        {
                            k = j + ncol ;
                            Br_value [k] = s ;
                            nbrk++ ;
                            Heap [nbrk] = k ; /* add to heap  */
                        }
                    }
                }
            }
            else /* shift_l != 0 */
            {
                for (j = lLinkUp [nsingni1]; j <= nsingni; j = lLinkUp [j])
                {
                    row = ineq_row [j] ;
                    if ( (t = dk [row]) < PPZERO )
                    {
                        /* lambda > 0 for lLinkUp */
                        if ( ni ) s = -(lambda [row]+shift_l [row])/t ;
                        else      s = (singc[j]-(lambda [row]+shift_l [row]))/t;
                        if ( s < st )
                        {
                            k = j + ncol ;
                            Br_value [k] = s ;
                            nbrk++ ;
                            Heap [nbrk] = k ; /* add to heap  */
                        }
                    }
                }
                for (j = uLinkUp [nsingni2]; j <= nsingni; j = uLinkUp [j])
                {
                    row = ineq_row [j] ;
                    if ( (t = dk [row]) > PPZERO )
                    {
                        /* lambda < 0 for uLinkUp */
                        if ( ni ) s = -(lambda [row]+shift_l [row])/t ;
                        else      s = (singc[j]-(lambda [row]+shift_l [row]))/t;
                        if ( s < st )
                        {
                            k = j + ncol ;
                            Br_value [k] = s ;
                            nbrk++ ;
                            Heap [nbrk] = k ; /* add to heap  */
                        }
                    }
                }
            }

            /* ============================================================== */
            /* sort break points, do a line search */
            /* ============================================================== */

            flag = 0 ;
            if ( nbrk > 0 )
            {
                pproj_minheap_build (Heap, Br_value, nbrk) ;
                for (k = 1; k <= nbrk; k++)
                {
                    ns [Heap [k]] = k ;
                }

#ifndef NDEBUG
                pproj_check_minheap (Heap, Br_value, ns, nbrk, ntot,
                                    "build Heap, phase 1, ssor step") ;
#endif

                st = PPZERO ;
                while ( nbrk > 0 )
                {
                    nup++ ;
                    if ( sd > PPZERO )
                    {
                        col = Heap [1] ;
                        pproj_minheap_delete (Heap, ns, Br_value, &nbrk, 1) ;
                        Br = PPMAX (Br_value [col], PPZERO) ;
                        fn = fd - sd * (Br - st) ;

#ifndef NDEBUG
                        pproj_check_minheap (Heap, Br_value, ns, nbrk, ntot,
                                            "Heap delete, phase 1") ;
                        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);
                        }
#endif

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

                    st = Br ;

                    if ( col < ncol ) /* free column */
                    {
#ifndef NDEBUG
                        x [col] = PPZERO ;
#endif
                        F [nf++] = col ;
                        s = pA [col] ;
                        sd += s*s ;
                        if ( ib [col] > 0 ) t = hi [col] ;
                        else                t = lo [col] ;
                        PPINT const q = Ap [col+1] ;
                        for (p = Ap [col]; p < q; p++)
                        {
                            i = Ai [p] ;
                            ax = Ax [p] ;
                            b [i] += t*ax ;
                            D [i] += ax*ax ;
                            l = AFTp [i] + AFTnz [i]++ ;
                            AFTx [l] = ax ;
                            AFTi [l] = col ;
                        }
                        ib [col] = 0 ;
                    }
                    else              /* drop row */
                    {
                        PPINT const sing = col - ncol ;
                        row = ineq_row [sing] ;

#ifndef NDEBUG
                        if ( PrintLevel > 2 )
                        {
                            PRINTF("    jsing: %ld row: %ld ir: %ld\n",
                              (LONG) col - ncol, (LONG) row, (LONG) ir [row]) ;
                        }
                        if ( ir [row] > nsingni )
                        {
                            PRINTF ("row: %ld ir: %ld was already deleted "
                                  "in phase 1\n", (LONG) row, (LONG) ir [row]) ;
                            pproj_error (-1, __FILE__, __LINE__, "stop") ;
                        }
#endif
                        /* previously bound was subtracted from b, now restore*/
                        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 ( W->shiftl_is_zero )
                        {
                            if ( ni )
                            {
                                dknew = -lambda [row] ;
                                lambda [row] = PPZERO ;
                            }
                            else /* nsing */
                            {
                                dknew = singc [sing] - lambda [row] ;
                                lambda [row] = singc [sing] ;
                            }
                        }
                        else
                        {
                            if ( ni )
                            {
                                dknew = -(lambda [row]+shift_l [row]) ;
                                lambda [row] = -shift_l [row] ;
                            }
                            else /* nsing */
                            {
                                t = singc [sing] - shift_l [row] ;
                                dknew = t - lambda [row] ;
                                lambda [row] = t ;
                            }
                        }
                        PPFLOAT const dk_row = dk [row] ;
                        /* fd += dk_row*(Dsigma*dknew-b [row]) ;*/
                        fd -= dk_row*b [row] ;
                        sd -= sigma*dk_row*dk_row ;
                        if ( dk_row > PPZERO ) /* drop upper bound bu */
                        {
                            b [row] -= bu [sing] ;
                            m = uLinkUp [sing] ;
                            l = uLinkDn [sing] ;
                            uLinkUp [l] = m ;
                            uLinkDn [m] = l ;
                            if ( nsing )
                            {
                                shi [row] = 0 ;
                                PPINT const sloj = slo [row] ;
                                if ( sloj )
                                {
                                    m = lLinkUp [sloj] ;
                                    l = lLinkDn [sloj] ;
                                    lLinkUp [l] = m ;
                                    lLinkDn [m] = l ;
                                    slo [row] = 0 ;
                                }
                            }
                        }
                        else /* drop lower bound bl */
                        {
                            b [row] -= bl [sing] ;
                            m = lLinkUp [sing] ;
                            l = lLinkDn [sing] ;
                            lLinkUp [l] = m ;
                            lLinkDn [m] = l ;
                            if ( nsing )
                            {
                                slo [row] = 0 ;
                                PPINT const shij = shi [row] ;
                                if ( shij )
                                {
                                    m = uLinkUp [shij] ;
                                    l = uLinkDn [shij] ;
                                    uLinkUp [l] = m ;
                                    uLinkDn [m] = l ;
                                    shi [row] = 0 ;
                                }
                            }
                        }

                        PPINT const q = ATp [row+1] ;
                        for (p = ATp [row] ; p < q ; p++)
                        {
                            j = ATi [p] ;
                            ax = ATx [p] ;
                            s = pA [j] ;
                            /* cj = c after taking the step */
                            PPFLOAT const cj = c [j] + st*s ;
                            /* Modify c to account for later update. That is,
                               when we drop row i, the contribution of the
                               elements in row i to c need to be removed.
                               The previous contribution of row i to c was
                               a_{ij}lambda_i. So now we need to subtract this
                               term from c. Note that dknew = -lambda_i. */
                            c [j] += dknew*ax ;
                            t = dk_row*ax ;
                            snew = pA [j] = s - t ;
                            /* update c, pA, sd, fd */
                            if ( !ib [j] )
                            {
                                fd += cj*t ;
                                sd -= t*(s + snew) ;
                            }
                            else
                            {
                                if ( ib [j] < 0 )
                                {
                                    if ( snew > PPZERO )
                                    {
                                        Br_value [j] = 
                                            st + (lo [j]-cj)/snew ;
                                        if ( ns [j] != EMPTY )
                                        {
                                            pproj_minheap_update (Heap,
                                               ns, Br_value, nbrk, ns [j]) ;
                                        }
                                        else
                                        {
                                           pproj_minheap_add (j, Heap,
                                               ns, Br_value, &nbrk) ;
                                        }
                                    }
                                    else /* this variable remains bound */
                                    {
                                        if ( ns [j] != EMPTY )
                                        {
                                            pproj_minheap_delete (Heap,
                                               ns, Br_value, &nbrk, ns [j]);
                                        }
                                    }
                                }
                                else /* ib [j] > 0 and cj > 0 */
                                {
                                    if ( snew < PPZERO )
                                    {
                                        Br_value [j] =
                                        st + (hi [j]-cj)/snew ;
                                        if ( ns [j] != EMPTY )
                                        {
                                            pproj_minheap_update (Heap,
                                               ns, Br_value, nbrk, ns [j]) ;
                                        }
                                        else
                                        {
                                            pproj_minheap_add (j, Heap,
                                                ns, Br_value, &nbrk) ;
                                        }
                                    }
                                    else /* this variable remains bound */
                                    {
                                        if ( ns [j] != EMPTY )
                                        {
                                            pproj_minheap_delete (Heap,
                                               ns, Br_value, &nbrk, ns [j]);
                                        }
                                    }
                                }
                            }
                        }
                    }

#ifndef NDEBUG
                    if ( PrintLevel > 2 )
                    {
                        if ( col < ncol )
                        {
                            PRINTF ("    free: %ld\n", (LONG) col);
                        }
                        else
                        {
                            PRINTF ("    drop row: %ld\n", (LONG) row) ;
                        }
                    }
                    pproj_check_minheap (Heap, Br_value, ns, nbrk, ntot,
                                        "line search of ssor, phase 1") ;
#endif

                    if ( fd <= PPZERO )
                    {

#ifndef NDEBUG
                        if ( PrintLevel > 2 )
                        {
                            PRINTF("    premature break from line search\n") ;
                        }
#endif

                        flag = 1 ;
                        fd = PPZERO ;
                        break ;
                    }
                }
                if ( sd > PPZERO )
                {
                    st += fd/sd ;
                }
                if ( st > st0 )
                {
                    st = st0 ;
                    flag = -1 ;
                }

                for (k = 1; k <= nbrk; k++)
                {
                    ns [Heap [k]] = EMPTY ;
                }
            }
        }
#ifndef NDEBUG
        where = "end of phase1 line search" ;
        /* check_phase1line (I, dk, st, where) ;*/
        if ( st != PPZERO )
        {
            pproj_check_line (I, flag, W->blks - 1, nup, dk, st) ;
        }
        pproj_checkb (I, where) ;
        pproj_check_const (NULL, PPZERO, ns, EMPTY, ntot, where) ;
#endif

        /* ------------------------------------------------------------------ */
        /* finish c update and check for binding columns */
        /* ------------------------------------------------------------------ */

        it++ ;

        xbound = PPZERO ;
        nf = nr = 0 ;
        for (j = 0; j < ncol; j++)
        {
            c [j] += st*pA [j] ;
            PPFLOAT const cj = c [j] ;
            ibj = ib [j] ;
            if ( ibj == 0 )
            {
                if ( (loExists == TRUE) && (cj < lo [j]) )
                {
                    if ( PrintLevel > 2 )
                    {
                        printf ("lower bind %ld\n", (LONG) j) ;
                    }
                    ib [j] = ibj = -1 ;
                    t = lo [j] ;
                }
                else if ( (hiExists == TRUE) && (cj > hi [j]) )
                {
                    if ( PrintLevel > 2 )
                    {
                        printf ("upper bind %ld\n", (LONG) j) ;
                    }
                    ib [j] = ibj = +1 ;
                    t = hi [j] ;
                }
                else
                {
                    if ( PrintLevel > 2 )
                    {
                        printf ("%ld continues free\n", (LONG) j) ;
                    }
                    F [nf++] = j ;
                }
                if ( ibj )
                {
#ifndef NDEBUG
                    x [j] = t ;
#endif
                    PPINT const q = Ap [j+1] ;
                    if ( t != PPZERO )
                    {
                        xbound += fabs (t) ;
                        for (p = Ap [j]; p < q; p++)
                        {
                            i = Ai [p] ;
                            ax = Ax [p] ;
                            b [i] -= t*ax ;
                            D [i] -= ax*ax ;
                            /* save rows associated with new bound columns */
                            if ( ns [i] == EMPTY )
                            {
                                ns [i] = 1 ;
                                R [nr++] = i ;
                            }
                        }
                    }
                    else /* update D, record rows where free columns bound */
                    {
                        for (p = Ap [j]; p < q; p++)
                        {
                            i = Ai [p] ;
                            D [i] -= Ax [p]*Ax [p] ;
                            if ( ns [i] == EMPTY )
                            {
                                ns [i] = 1 ;
                                R [nr++] = i ;
                            }
                        }
                    }
                }
                else /* x_j remains free */
                {
                     xbound += fabs (cj) ;
                }
            }
            else /* variable is bound */
            {
                if ( ibj > 0 )
                {
                     xbound += fabs (hi [j]) ;
                }
                else
                {
                     xbound += fabs (lo [j]) ;
                }
            }
        }
#ifndef NDEBUG
        where = "end of variable rebind in phase 1" ;
        pproj_checkb (I, where) ;
#endif

        /* ------------------------------------------------------------------ */
        /* update AFT by removing the bound columns */
        /* ------------------------------------------------------------------ */
        for (k = 0; k < nr; k++)
        {
            i = R [k] ;
            ns [i] = EMPTY ;
            D [i] = PPMAX (D [i], sigma) ;
            p0 = l = p = AFTp [i] ;
            PPINT const q = p + AFTnz [i] ;
            for (; p < q; p++)
            {
                if ( !ib [AFTi [p]] )
                {
                    AFTi [l] = AFTi [p] ;
                    AFTx [l] = AFTx [p] ;
                    l++ ;
                }
            }
            AFTnz [i] = l - p0 ;
        }

        /* ------------------------------------------------------------------ */
        /* Look for rows to add. Also, augment the grad L (lambda) associated
           with the active row (computed at start of SSOR step), with
           grad L (lambda) for the inactive (dropped) rows. */
        /* ------------------------------------------------------------------ */

        if ( PrintLevel > 1 )
        {
            printf ("final ssor st: %25.15e\n", st) ;
        }
        if ( !nsingni ) /* no strict inequalities and no column singletons */
        {
            pproj_saxpy (lambda, dk, st, nrow) ;
        }
        else /* either strict inequalities or column singletons exist */
        {
            Ll = nsingni1 ;
            Ul = nsingni2 ;
            Rl = nrow ;
            for (i = 0 ; i < nrow ; i++)
            {
                j = ir [i] ;
                if ( j <= nsingni ) /* row i is active */
                {
                    Rl = i ;
                    lambda [i] += st*dk [i] ;
                    if ( j == 0 ) continue ;
                    if ( ni )  /* a strict inequality */
                    {
                        if ( j < 0 )
                        {
                            Ll = -j ;
                        }
                        else /* j > 0 */
                        {
                            Ul = j ;
                        }
                    }
                    else /* contains column singletons */
                    {
                        if ( (j = slo [i]) ) /* lower bound exists */
                        {
                            Ll = j ;
                        }
                        if ( (j = shi [i]) ) /* upper bound exists */
                        {
                            Ul = j ;
                        }
                    }
                }
                else
                {
                    /* row i is inactive */
                    t = b [i] ;
                    ASSERT (ir [i] > nsingni) ;
                    p = AFTp [i] ;
                    PPINT const q = p + AFTnz [i] ;
                    for (; p < q; p++)
                    {
                        t -= c [AFTi [p]]*AFTx [p] ;
                    }
                    j -= nsingni ; /* singleton associated with row */
                    /* Include the proximal term when deciding whether to
                       activate the row. */
                    t -= sigma*lambda [i] ;
                    if ( t + bl [j]  > PPZERO )
                    {   /* lambda_i > 0 increases L */
                        if ( PrintLevel > 1 )
                        {
                            printf ("add row: %ld (lower bind)\n", (LONG) i) ;
                        }
    
                        /* add row to RLink, add sing to lLink */
                        ADD_IN_R_AND_LLINKS(i,j) ;
                    }
                    else if ( t + bu [j] < PPZERO )
                    {   /* lambda_i < 0 improves L */
                        if ( PrintLevel > 1 )
                        {
                            printf ("add row: %ld (upper bind)\n", (LONG) i) ;
                        }
    
                        /* add row to RLink, add sing to uLink */
                        ADD_IN_R_AND_ULINKS(i,j) ;
                    }
                }
            }
        } /* end of case with either strict inequalities or column singletons */

#ifndef NDEBUG
        where = "at tail of phase 1" ;
        /* FALSE: also check deleted rows */
        pproj_check_AFT (I, FALSE, where) ;
        pproj_checkb (I, where) ;
        pproj_checkc (I, where) ;
        /* check that dual objective increases */
        pproj_check_dual (I, NULL, where, TRUE, TRUE) ;
        pproj_check_link (I, (int *) NULL, 0, where) ;
#endif
    }

#ifndef NDEBUG
    if ( PrintLevel > 0 )
    {
        PRINTF("phase1 iterations complete\n") ;
    }
#endif
    W->nf = nf ;             /* return number of free indices */
    Stat->phase1_its  = it ; /* record number of iterations */

    /* Check for convergence and perform the first part of the change
       of variables described in pproj. Recompute b from scratch.
       Current estimate of the projection is x. */
    pproj_initx (W->dlambda, PPZERO, nrow) ;
    j = 1 ;
    for (i = 0; i < nrow; i++)
    {
        if ( k = ir [i] )
        {
            if ( ni )
            {
                if ( k < 0 )
                {
                    b [i] = Prob->bl [-k] ;
                }
                else if ( k <= ni )
                {
                    b [i] = Prob->bu [k] ;
                }
                else /* dropped row */
                {
                    b [i] = PPZERO ;
                }
            }
            else /* nsing */
            {
                t = Prob->b [i] ;
                if ( k <= nsing ) /* row is active */
                {
                    /* NOTE: bu = singhi, bl = singlo */
                    PPINT const sing = slo [i] ;
                    for (; j <= sing; j++)
                    {
                        t += bl [j] ;
                    }
                    PPINT const rowend = row_sing1 [i] ;
                    for (; j < rowend; j++)
                    {
                        t += bu [j] ;
                    }
                }
                else /* row is inactive, include all bounds in b except sing */
                {
                    PPINT const sing = k - nsing ;
                    for (; j < sing; j++)
                    {
                        t += bl [j] ;
                    }
                    j++ ; /* skip sing */
                    PPINT const rowend = row_sing1 [i] ;
                    for (; j < rowend; j++)
                    {
                        t += bu [j] ;
                    }
                }
                b [i] = t ;
            }
        }
        else
        {
            b [i] = Prob->b [i] ;
        }
    }
    p = 0 ;
    s = PPZERO ;
    absAx = W->arrayd ;
    pproj_initx (absAx, PPZERO, nrow) ;
    for (j = 0; j < ncol; j++)
    {
        if ( ib [j] )         /* variable is bound */
        {
            if ( ib [j] > 0 ) /* variable at upper bound */
            {
                t = hi [j] ;
                W->hi [j] = PPZERO ;
                if ( loExists == TRUE )
                {
                    W->lo [j] = lo [j] - t ;
                }
            }
            else              /* variable at lower bound */
            {
                t = lo [j] ;
                W->lo [j] = PPZERO ;
                if ( hiExists == TRUE )
                {
                    W->hi [j] = hi [j] - t ;
                }
            }
            c [j] -= t ;
        }
        else                 /* variable is free */
        {
            t = c [j] ;
            c [j] = PPZERO ;
            if ( loExists == TRUE )
            {
                W->lo [j] = lo [j] - t ;
            }
            if ( hiExists == TRUE )
            {
                W->hi [j] = hi [j] - t ;
            }
        }
        x [j] = t ;
        s += fabs (t) ;
        if ( t != PPZERO )
        {
            PPINT const q = Ap [j+1] ;
            if ( ib [j] )
            {
                for (p = Ap [j]; p < q; p++)
                {
                    PPINT ai ;
                    ai = Ai [p] ;
                    u = t*Ax [p] ;
                    b [ai] -= u ;
                    absAx [ai] += fabs (u) ;
                }
            }
            else
            {
                yj = fabs (Prob->y [j]) + fabs (t-Prob->y [j]) ;
                for (p = Ap [j]; p < q; p++)
                {
                    PPINT ai ;
                    ai = Ai [p] ;
                    ax = Ax [p] ;
                    u = t*ax ;
                    b [ai] -= u ;
                    absAx [ai] += fabs (ax*yj) ;
                }
            }
        }
    }
#ifndef NDEBUG
    where = "end of phase1" ;
    pproj_checkb (I, where) ;
    pproj_check_dual (I, NULL, where, TRUE, TRUE) ;
#endif

    W->normx = s ; /* L1 norm of x */
    /* Since the dropped rows all have 0 in their subdifferential set,
       errdual is the absolute max of b [i] for the active rows */
    errdual = PPZERO ;
    s = PPZERO ;
    for (i = RLinkUp [nrow]; i < nrow; i = RLinkUp [i])
    {
        if ( errdual < fabs (b [i]) )
        {
            errdual = fabs (b [i]) ;
        }
        if ( s < absAx [i] )
        {
            s = absAx [i] ;
        }
    }
    if ( s == PPZERO )
    {
        s = PPONE ;
    }
    /*recompute absAx when ||x||_1 changes by factor >= 1.5*/
    W->absAx = s ;

    /* in SpaRSA, only monitor undecided index set when gradient <= grad0 */
    W->grad0 = errdual*Parm->grad_decay ;

    if ( Parm->stop_condition == 0 )
    {
        errdual /= s ;
    }
    else if ( Parm->stop_condition == 2 )
    {
        errdual /= (s + W->ymax) ;
    }
    if ( PrintLevel > 0 )
    {
        printf ("phase1 errdual: %e absAx: %e\n", errdual, s) ;
    }

    /* only return when the error tolerance is satisfied if the user
       does not want PPcom (and hence the factorization) */
    if ( (errdual <= grad_tol) &&
         ((W->return_data == FALSE) || !(Parm->cholmod)) )
    {
        Stat->errdual = errdual ;
        return (PPROJ_SOLUTION_FOUND) ;
    }

    /* for dasa, replace b by b - sigma*lambda */
    pproj_saxpy (b, lambda, -sigma, nrow) ;

    if ( !Parm->cholmod ) /* return if purely iterative method is used */
    {
        pproj_copyx (W->lambda_tot, lambda, nrow) ;
        if ( !W->shiftl_is_zero )
        {
            pproj_saxpy (W->lambda_tot, shift_l, PPONE, nrow) ;
        }
        return (PPROJ_TOLERANCE_NOT_MET) ;
    }

    /* otherwise CHOLMOD and update/downdate are used */
    {
        int blk, blks ;
        PPINT nactive, Annz, ATnz, Lnnz,
              *Anz, *lstart, *ustart, *DropP ;
        cholmod_common *cmm ;

        cmm = W->cmm ;

        blks = W->blks ;
        lstart = W->lstart ;
        ustart = W->ustart ;

        /* lstart and ustart are associated with the inequalities that are
           strict.  For each block in the multilevel decomposition,
           lstart points to the first singleton at its lower bound while ustart
           points to the first singleton at it upper bound */
        for (k = 0; k < blks; k++)
        {
            lstart [k] = nsingni1 ;
            ustart [k] = nsingni2 ;
        }

        sol_to_blk = W->sol_to_blk ;
        prevblk = EMPTY ;
        for (j = lLinkUp [nsingni1]; j <= nsingni; j = lLinkUp [j])
        {
            blk = sol_to_blk [j] ;
            if ( blk > prevblk )
            {
                lstart [blk] = j ;
                prevblk = blk ;
            }
        }

        prevblk = EMPTY ;
        for (j = uLinkUp [nsingni2]; j <= nsingni; j = uLinkUp [j])
        {
            blk = sol_to_blk [j] ;
            if ( blk > prevblk )
            {
                ustart [blk] = j ;
                prevblk = blk ;
            }
        }

        /* number of active rows in column */
        Anz = Prob->Anz ;
        pproj_initi (Anz, (PPINT) 0, ncol) ;

        /* pointer to first dropped row in column */
        DropP = W->arrayi ;
        pproj_copyi (DropP, Ap+1, ncol) ;

        Annz = 0 ; /* number of nonzeros in active rows and free columns of A */
        ATnz = 0 ; /* number of nonzeros in active rows of full matrix */
        nactive = 0 ; /* number of active rows */
        p = 0 ;
        for (i = 0; i < nrow; i++)
        {
            PPINT const q = ATp [i+1] ;
            if ( ir [i] <= nsingni ) /* active row */
            {
                nactive++ ;
                ATnz += q - p ;
                Annz += AFTnz [i] ;
                for (; p < q; p++)
                {
                    j = ATi [p] ;
                    k = Ap [j] + Anz [j] ;
                    Anz [j]++ ;
                    Ax [k] = ATx [p] ;
                    Ai [k] = i ;
                }
            }
            else                      /* inactive (dropped) row */
            {
                AFTnz [i] = 0 ;       /* phase1 (full AFT) ascent (active AFT)*/
                for (; p < q; p++)
                {
                    j = ATi [p] ;
                    k = --DropP [j] ; /* put index at bottom of column */
                    Ax [k] = ATx [p] ;
                    Ai [k] = i ;
                }
            }
        }
        W->ATnz = ATnz ;
        W->Annz = Annz ;
        W->nactive = nactive ;

        /* If SSOR is used in conjunction with the update and downdate,
           then we evaluate nnz (chol (AF*AF')) since this determines
           how many SSOR iterations will be performed */
        if ( (Parm->use_ssor0 == TRUE) || (Parm->use_ssor1 == TRUE) )
        {
            PPINT *Iwork, *Parent, *Post, *First, *Level, *ColCount ;

            if ( Annz > 0 )
            {
                Iwork    = W->arrayi ;
                Parent   = Iwork ;  Iwork += nrow ;
                Post     = Iwork ;  Iwork += nrow ;
                First    = Iwork ;  Iwork += nrow ;
                Level    = Iwork ;  Iwork += nrow ;
                ColCount = Iwork ;  Iwork += nrow ;

                /* compute the etree of A(:,F)*A(:,F)' */
                /* uses Iwork (0..nrow+ncol-1) */
                CHOLMOD (etree) (W->AFT, Parent, cmm) ;

                /* postorder the etree */
                /* uses Iwork (0..2*nrow-1) */
                CHOLMOD (postorder) (Parent, nrow, NULL, Post, cmm) ;

                /* compute the row/col counts, first, and level */
                /* uses Iwork (0..2*nrow+ncol-1) */
                CHOLMOD (rowcolcounts) (W->A, F, nf,
                    Parent, Post, NULL, ColCount, First, Level, cmm) ;

                /* sum up the ColCounts for just the active rows */
                Lnnz = 0 ;
                for (row = RLinkUp [nrow]; row < nrow; row = RLinkUp [row])
                {
                    /* ColCount includes the diagonal */
                    Lnnz += (ColCount [row] - 1) ;
                }
                W->Lnnz = Lnnz ;
            }
            else
            {
                W->Lnnz = 0 ;
            }
        }
    }

    if ( errdual <= grad_tol )
    {
        Stat->errdual = errdual ;
        /* if Parm->getfactor is true, then do not stop, we need the final
           chol related updates below */
        if ( !Parm->cholmod || !Parm->getfactor || (nrow == RLinkUp [nrow]) )
        {
            if ( Parm->LP == TRUE )
            {
                PPFLOAT cerr = PPZERO ;
                for (i = 0; i < nf; i++)
                {
                    j = F [i] ;
                    t = fabs (x [j]) ;
                    if ( t > cerr ) cerr = t ;
                }
                W->cerr = t ;
                W->norm_l = pproj_sup_norm (lambda, nrow) ;
                W->errdual = errdual ;
            }
            return (PPROJ_SOLUTION_FOUND) ;
        }
    }
    return (PPROJ_TOLERANCE_NOT_MET) ;
}
