The R Project SVN R-packages

Rev

Rev 1820 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

/* ========================================================================== */
/* === Modify/t_cholmod_updown_numkr ======================================== */
/* ========================================================================== */

/* -----------------------------------------------------------------------------
 * CHOLMOD/Modify Module.  Version 0.6.  Copyright (C) 2005, Timothy A. Davis
 * and William W. Hager.
 * The CHOLMOD/Modify Module is licensed under Version 2.0 of the GNU
 * General Public License.  See gpl.txt for a text of the license.
 * CHOLMOD is also available under other licenses; contact authors for details.
 * http://www.cise.ufl.edu/research/sparse
 * -------------------------------------------------------------------------- */

/* Supernodal numerical update/downdate of rank K = RANK, along a single path.
 * This routine operates on a simplicial factor, but operates on adjacent
 * columns of L that would fit within a single supernode.  "Adjacent" means
 * along a single path in the elimination tree; they may or may not be
 * adjacent in the matrix L.
 *
 * external defines:  NUMERIC, WDIM, RANK.
 *
 * WDIM is 1, 2, 4, or 8.  RANK can be 1 to WDIM.
 *
 * A simple method is included (#define SIMPLE).  The code works, but is slow.
 * It is meant only to illustrate what this routine is doing.
 *
 * A rank-K update proceeds along a single path, using single-column, dual-
 * column, or quad-column updates of L.  If a column j and the next column
 * in the path (its parent) do not have the same nonzero pattern, a single-
 * column update is used.  If they do, but the 3rd and 4th column from j do
 * not have the same pattern, a dual-column update is used, in which the two
 * columns are treated as if they were a single supernode of two columns.  If
 * there are 4 columns in the path that all have the same nonzero pattern, then
 * a quad-column update is used.  All three kinds of updates can be used along
 * a single path, in a single call to this function.
 *
 * Single-column update:
 *
 *  When updating a single column of L, each iteration of the for loop,
 *  below, processes four rows of W (all columns involved) and one column
 *  of L.  Suppose we have a rank-5 update, and columns 2 through 6 of W
 *  are involved.  In this case, W in this routine is a pointer to column
 *  2 of the matrix W in the caller.  W (in the caller, shown as 'W') is
 *  held in row-major order, and is 8-by-n (a dense matrix storage format),
 *  but shown below in column form to match the column of L.  Suppose there
 *  are 13 nonzero entries in column 27 of L, with row indices 27 (the
 *  diagonal, D), 28, 30, 31, 42, 43, 44, 50, 51, 67, 81, 83, and 84.  This
 *  pattern is held in Li [Lp [27] ... Lp [27 + Lnz [27] - 1], where
 *  Lnz [27] = 13.   The modification of the current column j of L is done
 *  in the following order.  A dot (.) means the entry of W is not accessed.
 *
 *  W0 points to row 27 of W, and G is a 1-by-8 temporary vector.
 *
 *           G[0]        G[4]
 *  G         x  x  x  x  x  .  .  .
 *
 *            W0
 *            |
 *            v
 *  27      .  .  x  x  x  x  x  .   W0 points to W (27,2)
 *
 *
 *  row    'W'    W                      column j = 27
 *  |       |     |                      of L
 *  v       v     v                      |
 *  first iteration of for loop:         v
 *
 *  28      .  .  1  5  9 13 17  .       x
 *  30      .  .  2  6 10 14 18  .       x
 *  31      .  .  3  7 11 15 19  .       x
 *  42      .  .  4  8 12 16 20  .       x
 *
 *  second iteration of for loop:
 *
 *  43      .  .  1  5  9 13 17  .       x
 *  44      .  .  2  6 10 14 18  .       x
 *  50      .  .  3  7 11 15 19  .       x
 *  51      .  .  4  8 12 16 20  .       x
 *
 *  third iteration of for loop:
 *
 *  67      .  .  1  5  9 13 17  .       x
 *  81      .  .  2  6 10 14 18  .       x
 *  83      .  .  3  7 11 15 19  .       x
 *  84      .  .  4  8 12 16 20  .       x
 *
 *  If the number of offdiagonal nonzeros in column j of L is not divisible
 *  by 4, then the switch-statement does the work for the first nz % 4 rows.
 *
 * Dual-column update:
 *
 *  In this case, two columns of L that are adjacent in the path are being
 *  updated, by 1 to 8 columns of W.  Suppose columns j=27 and j=28 are
 *  adjacent columns in the path (they need not be j and j+1).  Two rows
 *  of G and W are used as coefficients during the update: (G0, G1) and
 *  (W0, W1).
 *
 *  G0        x  x  x  x  x  .  .  .
 *  G1        x  x  x  x  x  .  .  .
 *
 *  27      .  .  x  x  x  x  x  .   W0 points to W (27,2)
 *  28      .  .  x  x  x  x  x  .   W1 points to W (28,2)
 *
 *
 *  row    'W'    W0,W1                  column j = 27
 *  |       |     |                      of L
 *  v       v     v                      |
 *                       | |-- column j = 28 of L
 *                       v v
 *  update L (j1,j):
 *
 *  28      .  .  1  2  3  4  5  .       x -    ("-" is not stored in L)
 *
 *  cleanup iteration since length is odd:
 *
 *  30      .  .  1  2  3  4  5  .       x x
 *
 *  then each iteration does two rows of both columns of L:
 *
 *  31      .  .  1  3  5  7  9  .       x x
 *  42      .  .  2  4  6  8 10  .       x x
 *
 *  43      .  .  1  3  5  7  9  .       x x
 *  44      .  .  2  4  6  8 10  .       x x
 *
 *  50      .  .  1  3  5  7  9  .       x x
 *  51      .  .  2  4  6  8 10  .       x x
 *
 *  67      .  .  1  3  5  7  9  .       x x
 *  81      .  .  2  4  6  8 10  .       x x
 *
 *  83      .  .  1  3  5  7  9  .       x x
 *  84      .  .  2  4  6  8 10  .       x x
 *
 *  If the number of offdiagonal nonzeros in column j of L is not even,
 *  then the cleanup iteration does the work for the first row.
 *
 * Quad-column update:
 *
 *  In this case, four columns of L that are adjacent in the path are being
 *  updated, by 1 to 8 columns of W.  Suppose columns j=27, 28, 30, and 31
 *  are adjacent columns in the path (they need not be j, j+1, ...).  Four
 *  rows of G and W are used as coefficients during the update: (G0 through
 *  G3) and (W0 through W3).  j=27, j1=28, j2=30, and j3=31.
 *
 *  G0        x  x  x  x  x  .  .  .
 *  G1        x  x  x  x  x  .  .  .
 *  G3        x  x  x  x  x  .  .  .
 *  G4        x  x  x  x  x  .  .  .
 *
 *  27      .  .  x  x  x  x  x  .   W0 points to W (27,2)
 *  28      .  .  x  x  x  x  x  .   W1 points to W (28,2)
 *  30      .  .  x  x  x  x  x  .   W2 points to W (30,2)
 *  31      .  .  x  x  x  x  x  .   W3 points to W (31,2)
 *
 *
 *  row    'W'    W0,W1,..               column j = 27
 *  |       |     |                      of L
 *  v       v     v                      |
 *                       | |-- column j = 28 of L
 *                       | | |-- column j = 30 of L
 *                       | | | |-- column j =  31 of L
 *                       v v v v
 *  update L (j1,j):
 *  28      .  .  1  2  3  4  5  .       x - - -
 *
 *  update L (j2,j):
 *  30      .  .  1  2  3  4  5  .       # x - -     (# denotes modified)
 *
 *  update L (j2,j1)
 *  30      .  .  1  2  3  4  5  .       x # - -
 *
 *  update L (j3,j)
 *  31      .  .  1  2  3  4  5  .       # x x -
 *
 *  update L (j3,j1)
 *  31      .  .  1  2  3  4  5  .       x # x -
 *
 *  update L (j3,j2)
 *  31      .  .  1  2  3  4  5  .       x x # -
 *
 *  cleanup iteration since length is odd:
 *  42      .  .  1  2  3  4  5  .       x x x x
 *
 *  then each iteration does two rows of all four colummns of L:
 *
 *  43      .  .  1  3  5  7  9  .       x x x x
 *  44      .  .  2  4  6  8 10  .       x x x x
 *
 *  50      .  .  1  3  5  7  9  .       x x x x
 *  51      .  .  2  4  6  8 10  .       x x x x
 *
 *  67      .  .  1  3  5  7  9  .       x x x x
 *  81      .  .  2  4  6  8 10  .       x x x x
 *
 *  83      .  .  1  3  5  7  9  .       x x x x
 *  84      .  .  2  4  6  8 10  .       x x x x
 *
 * This file is included in t_cholmod_updown.c, only.
 * It is not compiled separately.  It contains no user-callable routines.
 *
 * workspace: Xwork (WDIM*nrow)
 */

/* ========================================================================== */
/* === loop unrolling macros ================================================ */
/* ========================================================================== */

#undef RANK1
#undef RANK2
#undef RANK3
#undef RANK4
#undef RANK5
#undef RANK6
#undef RANK7
#undef RANK8

#define RANK1(s) s

#if RANK < 2
#define RANK2(s)
#else
#define RANK2(s) s
#endif

#if RANK < 3
#define RANK3(s)
#else
#define RANK3(s) s
#endif

#if RANK < 4
#define RANK4(s)
#else
#define RANK4(s) s
#endif

#if RANK < 5
#define RANK5(s)
#else
#define RANK5(s) s
#endif

#if RANK < 6
#define RANK6(s)
#else
#define RANK6(s) s
#endif

#if RANK < 7
#define RANK7(s)
#else
#define RANK7(s) s
#endif

#if RANK < 8
#define RANK8(s)
#else
#define RANK8(s) s
#endif

#define FOR_ALL_K \
    RANK1 (DO (0)) \
    RANK2 (DO (1)) \
    RANK3 (DO (2)) \
    RANK4 (DO (3)) \
    RANK5 (DO (4)) \
    RANK6 (DO (5)) \
    RANK7 (DO (6)) \
    RANK8 (DO (7))

/* ========================================================================== */
/* === alpha/gamma ========================================================== */
/* ========================================================================== */

#undef ALPHA_GAMMA

#define ALPHA_GAMMA(Dj,Alpha,Gamma,W) \
{ \
    double dj = Dj ; \
    if (update) \
    { \
    for (k = 0 ; k < RANK ; k++) \
    { \
        double w = W [k] ; \
        double alpha = Alpha [k] ; \
        double a = alpha + (w * w) / dj ; \
        dj *= a ; \
        Alpha [k] = a ; \
        Gamma [k] = (- w / dj) ; \
        dj /= alpha ; \
    } \
    } \
    else \
    { \
    for (k = 0 ; k < RANK ; k++) \
    { \
        double w = W [k] ; \
        double alpha = Alpha [k] ; \
        double a = alpha - (w * w) / dj ; \
        dj *= a ; \
        Alpha [k] = a ; \
        Gamma [k] = w / dj ; \
        dj /= alpha ; \
    } \
    } \
    Dj = ((use_dbound) ? (CHOLMOD(dbound) (dj, Common)) : (dj)) ; \
}

/* ========================================================================== */
/* === numeric update/downdate along one path =============================== */
/* ========================================================================== */

static void NUMERIC (WDIM, RANK)
(
    int update,     /* TRUE for update, FALSE for downdate */
    Int j,      /* first column in the path */
    Int e,      /* last column in the path */
    double Alpha [ ],   /* alpha, for each column of W */
    double W [ ],   /* W is an n-by-WDIM array, stored in row-major order */
    cholmod_factor *L,  /* with unit diagonal (diagonal not stored) */
    cholmod_common *Common
)
{

#ifdef SIMPLE
#define w(row,col) W [WDIM*(row) + (col)]

    /* ---------------------------------------------------------------------- */
    /* concise but slow version for illustration only */
    /* ---------------------------------------------------------------------- */

    double Gamma [WDIM] ;
    double *Lx ;
    Int *Li, *Lp, *Lnz ;
    Int p, k ;
    Int use_dbound = IS_GT_ZERO (Common->dbound) ;

    Li = L->i ;
    Lx = L->x ;
    Lp = L->p ;
    Lnz = L->nz ;

    /* walk up the etree from node j to its ancestor e */
    for ( ; j <= e ; j = (Lnz [j] > 1) ? (Li [Lp [j] + 1]) : Int_max)
    {
    /* update the diagonal entry D (j,j) with each column of W */
    ALPHA_GAMMA (Lx [Lp [j]], Alpha, Gamma, (&(w (j,0)))) ;
    /* update column j of L */
    for (p = Lp [j] + 1 ; p < Lp [j] + Lnz [j] ; p++)
    {
        /* update row Li [p] of column j of L with each column of W */
        Int i = Li [p] ;
        for (k = 0 ; k < RANK ; k++)
        {
        w (i,k) -= w (j,k) * Lx [p] ;
        Lx [p] -= Gamma [k] * w (i,k) ;
        }
    }
    /* clear workspace W */
    for (k = 0 ; k < RANK ; k++)
    {
        w (j,k) = 0 ;
    }
    }

#else

    /* ---------------------------------------------------------------------- */
    /* dynamic supernodal version: supernodes detected dynamically */
    /* ---------------------------------------------------------------------- */

    double G0 [WDIM], G1 [WDIM], G2 [WDIM], G3 [WDIM] ;
    double *W0, *W1, *W2, *W3, *Lx ;
    Int *Li, *Lp, *Lnz ;
    Int j1, j2, j3, parent, lnz, p, q, r, s, pend, k ;
    Int use_dbound = IS_GT_ZERO (Common->dbound) ;

    Li = L->i ;
    Lx = L->x ;
    Lp = L->p ;
    Lnz = L->nz ;

    /* walk up the etree from node j to its ancestor e */
    for ( ; j <= e ; j = parent)
    {

    p = Lp [j] ;        /* col j is Li,Lx [p ... p+lnz-1] */
    lnz = Lnz [j] ;

    W0 = W + WDIM * j ; /* pointer to row j of W */
    pend = p + lnz ;

    /* update D (j,j) */
    ALPHA_GAMMA (Lx [p], Alpha, G0, W0) ;
    p++ ;

    /* determine how many columns of L to update at the same time */
    parent = (lnz > 1) ? (Li [p]) : Int_max ;
    if (parent <= e && lnz == Lnz [parent] + 1)
    {

        /* -------------------------------------------------------------- */
        /* node j and its parent j1 can be updated at the same time */
        /* -------------------------------------------------------------- */

        j1 = parent ;
        j2 = (lnz > 2) ? (Li [p+1]) : Int_max ;
        j3 = (lnz > 3) ? (Li [p+2]) : Int_max ;
        W1 = W + WDIM * j1 ;    /* pointer to row j1 of W */
        q = Lp [j1] ;

        /* update L (j1,j) */
        {
        double lx = Lx [p] ;

        /* for k = 0 to RANK-1 do: */
        #define DO(k) \
            W1 [k] -= W0 [k] * lx ; \
            lx -= G0 [k] * W1 [k] ;
        FOR_ALL_K
        #undef DO

        Lx [p++] = lx ;
        }

        /* update D (j1,j1) */
        ALPHA_GAMMA (Lx [q], Alpha, G1, W1) ;
        q++ ;

        /* -------------------------------------------------------------- */
        /* update 2 or 4 columns of L */
        /* -------------------------------------------------------------- */

        if ((j2 <= e) &&            /* j2 in the current path */
        (j3 <= e) &&            /* j3 in the current path */
        (lnz == Lnz [j2] + 2) &&    /* column j2 matches */
        (lnz == Lnz [j3] + 3))      /* column j3 matches */
        {

        /* ---------------------------------------------------------- */
        /* update 4 columns of L */
        /* ---------------------------------------------------------- */

        /* p and q currently points to row j2 in cols j and j1 of L */

        parent = (lnz > 4) ? (Li [p+2]) : Int_max ;
        W2 = W + WDIM * j2 ;        /* pointer to row j2 of W */
        W3 = W + WDIM * j3 ;        /* pointer to row j3 of W */
        r = Lp [j2] ;
        s = Lp [j3] ;

        /* update L (j2,j) */
        {
            double lx = Lx [p] ;

            /* for k = 0 to RANK-1 do: */
            #define DO(k) \
            W2 [k] -= W0 [k] * lx ; \
            lx -= G0 [k] * W2 [k] ;
            FOR_ALL_K
            #undef DO

            Lx [p++] = lx ;
        }

        /* update L (j2,j1) */
        {
            double lx = Lx [q] ;

            /* for k = 0 to RANK-1 do: */
            #define DO(k) \
            W2 [k] -= W1 [k] * lx ; \
            lx -= G1 [k] * W2 [k] ;
            FOR_ALL_K
            #undef DO

            Lx [q++] = lx ;
        }

        /* update L (j3,j) */
        {
            double lx = Lx [p] ;

            /* for k = 0 to RANK-1 do: */
            #define DO(k) \
            W3 [k] -= W0 [k] * lx ; \
            lx -= G0 [k] * W3 [k] ;
            FOR_ALL_K
            #undef DO

            Lx [p++] = lx ;
        }

        /* update L (j3,j1) */
        {
            double lx = Lx [q] ;

            /* for k = 0 to RANK-1 do: */
            #define DO(k) \
            W3 [k] -= W1 [k] * lx ; \
            lx -= G1 [k] * W3 [k] ;
            FOR_ALL_K
            #undef DO

            Lx [q++] = lx ;
        }

        /* update D (j2,j2) */
        ALPHA_GAMMA (Lx [r], Alpha, G2, W2) ;
        r++ ;

        /* update L (j3,j2) */
        {
            double lx = Lx [r] ;

            /* for k = 0 to RANK-1 do: */
            #define DO(k) \
            W3 [k] -= W2 [k] * lx ; \
            lx -= G2 [k] * W3 [k] ;
            FOR_ALL_K
            #undef DO

            Lx [r++] = lx ;
        }

        /* update D (j3,j3) */
        ALPHA_GAMMA (Lx [s], Alpha, G3, W3) ;
        s++ ;

        /* cleanup iteration if length is odd */
        if ((lnz - 2) % 2)
        {
            double lx [4] , *w0 ;
            lx [0] = Lx [p] ;
            lx [1] = Lx [q] ;
            lx [2] = Lx [r] ;
            lx [3] = Lx [s] ;
            w0 = W + WDIM * Li [p] ;

            /* for k = 0 to RANK-1 do: */
            #define DO(k) \
            w0 [k] -= W0 [k] * lx [0] ; \
            lx [0] -= G0 [k] * w0 [k] ; \
            w0 [k] -= W1 [k] * lx [1] ; \
            lx [1] -= G1 [k] * w0 [k] ; \
            w0 [k] -= W2 [k] * lx [2] ; \
            lx [2] -= G2 [k] * w0 [k] ; \
            w0 [k] -= W3 [k] * lx [3] ; \
            lx [3] -= G3 [k] * w0 [k] ;
            FOR_ALL_K
            #undef DO

            Lx [p++] = lx [0] ;
            Lx [q++] = lx [1] ;
            Lx [r++] = lx [2] ;
            Lx [s++] = lx [3] ;
        }

        for ( ; p < pend ; p += 2, q += 2, r += 2, s += 2)
        {
            double lx [2][4], w [2], *w0, *w1 ;
            lx [0][0] = Lx [p  ] ;
            lx [1][0] = Lx [p+1] ;
            lx [0][1] = Lx [q  ] ;
            lx [1][1] = Lx [q+1] ;
            lx [0][2] = Lx [r  ] ;
            lx [1][2] = Lx [r+1] ;
            lx [0][3] = Lx [s  ] ;
            lx [1][3] = Lx [s+1] ;
            w0 = W + WDIM * Li [p  ] ;
            w1 = W + WDIM * Li [p+1] ;

            /* for k = 0 to RANK-1 do: */
            #define DO(k) \
            w [0] = w0 [k] ; \
            w [1] = w1 [k] ; \
            w [0] -= W0 [k] * lx [0][0] ; \
            w [1] -= W0 [k] * lx [1][0] ; \
            lx [0][0] -= G0 [k] * w [0] ; \
            lx [1][0] -= G0 [k] * w [1] ; \
            w [0] -= W1 [k] * lx [0][1] ; \
            w [1] -= W1 [k] * lx [1][1] ; \
            lx [0][1] -= G1 [k] * w [0] ; \
            lx [1][1] -= G1 [k] * w [1] ; \
            w [0] -= W2 [k] * lx [0][2] ; \
            w [1] -= W2 [k] * lx [1][2] ; \
            lx [0][2] -= G2 [k] * w [0] ; \
            lx [1][2] -= G2 [k] * w [1] ; \
            w [0] -= W3 [k] * lx [0][3] ; \
            w [1] -= W3 [k] * lx [1][3] ; \
            lx [0][3] -= G3 [k] * w [0] ; \
            lx [1][3] -= G3 [k] * w [1] ; \
            w0 [k] = w [0] ; \
            w1 [k] = w [1] ;
            FOR_ALL_K
            #undef DO

            Lx [p  ] = lx [0][0] ;
            Lx [p+1] = lx [1][0] ;
            Lx [q  ] = lx [0][1] ;
            Lx [q+1] = lx [1][1] ;
            Lx [r  ] = lx [0][2] ;
            Lx [r+1] = lx [1][2] ;
            Lx [s  ] = lx [0][3] ;
            Lx [s+1] = lx [1][3] ;
        }

        /* for k = 0 to RANK-1 do: */
        #define DO(k) W0 [k] = 0 ;
        FOR_ALL_K
        #undef DO

        /* for k = 0 to RANK-1 do: */
        #define DO(k) W1 [k] = 0 ;
        FOR_ALL_K
        #undef DO

        /* for k = 0 to RANK-1 do: */
        #define DO(k) W2 [k] = 0 ;
        FOR_ALL_K
        #undef DO

        /* for k = 0 to RANK-1 do: */
        #define DO(k) W3 [k] = 0 ;
        FOR_ALL_K
        #undef DO

        }
        else
        {

        /* ---------------------------------------------------------- */
        /* update 2 columns of L */
        /* ---------------------------------------------------------- */

        parent = j2 ;

        /* cleanup iteration if length is odd */
        if ((lnz - 2) % 2)
        {
            double lx [2] , *w0 ;
            lx [0] = Lx [p] ;
            lx [1] = Lx [q] ;
            w0 = W + WDIM * Li [p] ;

            /* for k = 0 to RANK-1 do: */
            #define DO(k) \
            w0 [k] -= W0 [k] * lx [0] ; \
            lx [0] -= G0 [k] * w0 [k] ; \
            w0 [k] -= W1 [k] * lx [1] ; \
            lx [1] -= G1 [k] * w0 [k] ;
            FOR_ALL_K
            #undef DO

            Lx [p++] = lx [0] ;
            Lx [q++] = lx [1] ;
        }

        for ( ; p < pend ; p += 2, q += 2)
        {
            double lx [2][2], w [2], *w0, *w1 ;
            lx [0][0] = Lx [p  ] ;
            lx [1][0] = Lx [p+1] ;
            lx [0][1] = Lx [q  ] ;
            lx [1][1] = Lx [q+1] ;
            w0 = W + WDIM * Li [p  ] ;
            w1 = W + WDIM * Li [p+1] ;

            /* for k = 0 to RANK-1 do: */
            #define DO(k) \
            w [0] = w0 [k] ; \
            w [1] = w1 [k] ; \
            w [0] -= W0 [k] * lx [0][0] ; \
            w [1] -= W0 [k] * lx [1][0] ; \
            lx [0][0] -= G0 [k] * w [0] ; \
            lx [1][0] -= G0 [k] * w [1] ; \
            w [0] -= W1 [k] * lx [0][1] ; \
            w [1] -= W1 [k] * lx [1][1] ; \
            lx [0][1] -= G1 [k] * w [0] ; \
            lx [1][1] -= G1 [k] * w [1] ; \
            w0 [k] = w [0] ; \
            w1 [k] = w [1] ;
            FOR_ALL_K
            #undef DO

            Lx [p  ] = lx [0][0] ;
            Lx [p+1] = lx [1][0] ;
            Lx [q  ] = lx [0][1] ;
            Lx [q+1] = lx [1][1] ;
        }

        /* for k = 0 to RANK-1 do: */
        #define DO(k) W0 [k] = 0 ;
        FOR_ALL_K
        #undef DO

        /* for k = 0 to RANK-1 do: */
        #define DO(k) W1 [k] = 0 ;
        FOR_ALL_K
        #undef DO
        }

    }
    else
    {

        /* -------------------------------------------------------------- */
        /* update one column of L */
        /* -------------------------------------------------------------- */

        /* cleanup iteration if length is not a multiple of 4 */
        switch ((lnz - 1) % 4)
        {
        case 1:
        {
            double lx , *w0 ;
            lx = Lx [p] ;
            w0 = W + WDIM * Li [p] ;

            /* for k = 0 to RANK-1 do: */
            #define DO(k) \
            w0 [k] -= W0 [k] * lx ; \
            lx -= G0 [k] * w0 [k] ;
            FOR_ALL_K
            #undef DO

            Lx [p++] = lx ;
        }
        break ;

        case 2:
        {
            double lx [2], *w0, *w1 ;
            lx [0] = Lx [p  ] ;
            lx [1] = Lx [p+1] ;
            w0 = W + WDIM * Li [p  ] ;
            w1 = W + WDIM * Li [p+1] ;

            /* for k = 0 to RANK-1 do: */
            #define DO(k) \
            w0 [k] -= W0 [k] * lx [0] ; \
            w1 [k] -= W0 [k] * lx [1] ; \
            lx [0] -= G0 [k] * w0 [k] ; \
            lx [1] -= G0 [k] * w1 [k] ;
            FOR_ALL_K
            #undef DO

            Lx [p++] = lx [0] ;
            Lx [p++] = lx [1] ;
        }
        break ;

        case 3:
        {
            double lx [3], *w0, *w1, *w2 ;
            lx [0] = Lx [p  ] ;
            lx [1] = Lx [p+1] ;
            lx [2] = Lx [p+2] ;
            w0 = W + WDIM * Li [p  ] ;
            w1 = W + WDIM * Li [p+1] ;
            w2 = W + WDIM * Li [p+2] ;

            /* for k = 0 to RANK-1 do: */
            #define DO(k) \
            w0 [k] -= W0 [k] * lx [0] ; \
            w1 [k] -= W0 [k] * lx [1] ; \
            w2 [k] -= W0 [k] * lx [2] ; \
            lx [0] -= G0 [k] * w0 [k] ; \
            lx [1] -= G0 [k] * w1 [k] ; \
            lx [2] -= G0 [k] * w2 [k] ;
            FOR_ALL_K
            #undef DO

            Lx [p++] = lx [0] ;
            Lx [p++] = lx [1] ;
            Lx [p++] = lx [2] ;
        }
        }

        for ( ; p < pend ; p += 4)
        {
        double lx [4], *w0, *w1, *w2, *w3 ;
        lx [0] = Lx [p  ] ;
        lx [1] = Lx [p+1] ;
        lx [2] = Lx [p+2] ;
        lx [3] = Lx [p+3] ;
        w0 = W + WDIM * Li [p  ] ;
        w1 = W + WDIM * Li [p+1] ;
        w2 = W + WDIM * Li [p+2] ;
        w3 = W + WDIM * Li [p+3] ;

        /* for k = 0 to RANK-1 do: */
        #define DO(k) \
            w0 [k] -= W0 [k] * lx [0] ; \
            w1 [k] -= W0 [k] * lx [1] ; \
            w2 [k] -= W0 [k] * lx [2] ; \
            w3 [k] -= W0 [k] * lx [3] ; \
            lx [0] -= G0 [k] * w0 [k] ; \
            lx [1] -= G0 [k] * w1 [k] ; \
            lx [2] -= G0 [k] * w2 [k] ; \
            lx [3] -= G0 [k] * w3 [k] ;
        FOR_ALL_K
        #undef DO

        Lx [p  ] = lx [0] ;
        Lx [p+1] = lx [1] ;
        Lx [p+2] = lx [2] ;
        Lx [p+3] = lx [3] ;
        }

        /* for k = 0 to RANK-1 do: */
        #define DO(k) \
        W0 [k] = 0 ;
        FOR_ALL_K
        #undef DO

    }
    }
#endif
}
/* prepare this file for another inclusion in t_cholmod_updown.c: */
#undef RANK