Пример #1
0
GLOBAL Int UMF_extend_front
(
    NumericType *Numeric,
    WorkType *Work
)
{
    /* ---------------------------------------------------------------------- */
    /* local variables */
    /* ---------------------------------------------------------------------- */

    Int j, i, *Frows, row, col, *Wrow, fnr2, fnc2, *Frpos, *Fcpos, *Fcols,
	fnrows_extended, rrdeg, ccdeg, fncols_extended, fnr_curr, fnc_curr,
	fnrows, fncols, pos, fnpiv, *Wm ;
    Entry *Wx, *Wy, *Fu, *Fl ;

    /* ---------------------------------------------------------------------- */
    /* get current frontal matrix and check for frontal growth */
    /* ---------------------------------------------------------------------- */

    fnpiv = Work->fnpiv ;

#ifndef NDEBUG
    DEBUG2 (("EXTEND FRONT\n")) ;
    DEBUG2 (("Work->fnpiv "ID"\n", fnpiv)) ;
    ASSERT (Work->Flblock  == Work->Flublock + Work->nb*Work->nb) ;
    ASSERT (Work->Fublock  == Work->Flblock  + Work->fnr_curr*Work->nb) ;
    ASSERT (Work->Fcblock  == Work->Fublock  + Work->nb*Work->fnc_curr) ;
    DEBUG7 (("C  block: ")) ;
    UMF_dump_dense (Work->Fcblock,  Work->fnr_curr, Work->fnrows, Work->fncols) ;
    DEBUG7 (("L  block: ")) ;
    UMF_dump_dense (Work->Flblock,  Work->fnr_curr, Work->fnrows, fnpiv);
    DEBUG7 (("U' block: ")) ;
    UMF_dump_dense (Work->Fublock,  Work->fnc_curr, Work->fncols, fnpiv) ;
    DEBUG7 (("LU block: ")) ;
    UMF_dump_dense (Work->Flublock, Work->nb, fnpiv, fnpiv) ;
#endif

    if (Work->do_grow)
    {
	fnr2 = UMF_FRONTAL_GROWTH * Work->fnrows_new + 2 ;
	fnc2 = UMF_FRONTAL_GROWTH * Work->fncols_new + 2 ;
	if (!UMF_grow_front (Numeric, fnr2, fnc2, Work, 1))
	{
	    DEBUGm4 (("out of memory: extend front\n")) ;
	    return (FALSE) ;
	}
    }

    fnr_curr = Work->fnr_curr ;
    fnc_curr = Work->fnc_curr ;
    ASSERT (Work->fnrows_new + 1 <= fnr_curr) ;
    ASSERT (Work->fncols_new + 1 <= fnc_curr) ;
    ASSERT (fnr_curr >= 0 && fnr_curr % 2 == 1) ;

    /* ---------------------------------------------------------------------- */
    /* get parameters */
    /* ---------------------------------------------------------------------- */

    Frows = Work->Frows ;
    Frpos = Work->Frpos ;
    Fcols = Work->Fcols ;
    Fcpos = Work->Fcpos ;
    fnrows = Work->fnrows ;
    fncols = Work->fncols ;
    rrdeg = Work->rrdeg ;
    ccdeg = Work->ccdeg ;

    /* scan starts at the first new column in Fcols */
    /* also scan the pivot column if it was not in the front */
    Work->fscan_col = fncols ;
    Work->NewCols = Fcols ;

    /* scan1 starts at the first new row in Frows */
    /* also scan the pivot row if it was not in the front */
    Work->fscan_row = fnrows ;
    Work->NewRows = Frows ;

    /* ---------------------------------------------------------------------- */
    /* extend row pattern of the front with the new pivot column */
    /* ---------------------------------------------------------------------- */

    fnrows_extended = fnrows ;
    fncols_extended = fncols ;

#ifndef NDEBUG
    DEBUG2 (("Pivot col, before extension: "ID"\n", fnrows)) ;
    for (i = 0 ; i < fnrows ; i++)
    {
	DEBUG2 ((" "ID": row "ID"\n", i, Frows [i])) ;
	ASSERT (Frpos [Frows [i]] == i) ;
    }
    DEBUG2 (("Extending pivot column: pivcol_in_front: "ID"\n",
	Work->pivcol_in_front)) ;
#endif

    Fl = Work->Flblock + fnpiv * fnr_curr ;

    if (Work->pivcol_in_front)
    {
	/* extended pattern and position already in Frows, Frpos.  Values above
	 * the diagonal are already in LU block.  Values on and below the
	 * diagonal are in Wy [0 .. fnrows_extended-1].  Copy into the L
	 * block. */
	fnrows_extended += ccdeg ;
	Wy = Work->Wy ;

	for (i = 0 ; i < fnrows_extended ; i++)
	{
	    Fl [i] = Wy [i] ;
#ifndef NDEBUG
	    row = Frows [i] ;
	    DEBUG2 ((" "ID": row "ID" ", i, row)) ;
	    EDEBUG2 (Fl [i]) ;
	    if (row == Work->pivrow) DEBUG2 ((" <- pivrow")) ;
	    DEBUG2 (("\n")) ;
	    if (i == fnrows - 1) DEBUG2 ((" :::::::\n")) ;
	    ASSERT (row >= 0 && row < Work->n_row) ;
	    ASSERT (Frpos [row] == i) ;
#endif
	}

    }
    else
    {
	/* extended pattern,values is in (Wm,Wx), not yet in the front */
	Entry *F ;
	Fu = Work->Flublock + fnpiv * Work->nb ;
	Wm = Work->Wm ;
	Wx = Work->Wx ;
	F = Fu ;
	for (i = 0 ; i < fnpiv ; i++)
	{
	    CLEAR_AND_INCREMENT (F) ;
	}
	F = Fl ;
	for (i = 0 ; i < fnrows ; i++)
	{
	    CLEAR_AND_INCREMENT (F) ;
	}
	for (i = 0 ; i < ccdeg ; i++)
	{
	    row = Wm [i] ;
#ifndef NDEBUG
	    DEBUG2 ((" "ID": row "ID" (ext) ", fnrows_extended, row)) ;
	    EDEBUG2 (Wx [i]) ;
	    if (row == Work->pivrow) DEBUG2 ((" <- pivrow")) ;
	    DEBUG2 (("\n")) ;
	    ASSERT (row >= 0 && row < Work->n_row) ;
#endif
	    pos = Frpos [row] ;
	    if (pos < 0)
	    {
		pos = fnrows_extended++ ;
		Frows [pos] = row ;
		Frpos [row] = pos ;
	    }
	    Fl [pos] = Wx [i] ;
	}
    }

    ASSERT (fnrows_extended <= fnr_curr) ;

    /* ---------------------------------------------------------------------- */
    /* extend the column pattern of the front with the new pivot row */
    /* ---------------------------------------------------------------------- */

#ifndef NDEBUG
    DEBUG6 (("Pivot row, before extension: "ID"\n", fncols)) ;
    for (j = 0 ; j < fncols ; j++)
    {
	DEBUG7 ((" "ID": col "ID"\n", j, Fcols [j])) ;
	ASSERT (Fcpos [Fcols [j]] == j * fnr_curr) ;
    }
    DEBUG6 (("Extending pivot row:\n")) ;
#endif

    if (Work->pivrow_in_front)
    {
	if (Work->pivcol_in_front)
	{
	    ASSERT (Fcols == Work->Wrow) ;
	    for (j = fncols ; j < rrdeg ; j++)
	    {
#ifndef NDEBUG
		col = Fcols [j] ;
		DEBUG2 ((" "ID": col "ID" (ext)\n", j, col)) ;
		ASSERT (col != Work->pivcol) ;
		ASSERT (col >= 0 && col < Work->n_col) ;
		ASSERT (Fcpos [col] < 0) ;
#endif
		Fcpos [Fcols [j]] = j * fnr_curr ;
	    }
	}
	else
	{
	    /* OUT-IN option: pivcol not in front, but pivrow is in front */
	    Wrow = Work->Wrow ;
	    ASSERT (IMPLIES (Work->pivcol_in_front, Wrow == Fcols)) ;
	    if (Wrow == Fcols)
	    {
		/* Wrow and Fcols are equivalenced */
		for (j = fncols ; j < rrdeg ; j++)
		{
		    col = Wrow [j] ;
		    DEBUG2 ((" "ID": col "ID" (ext)\n", j, col)) ;
		    ASSERT (Fcpos [col] < 0) ;
		    /* Fcols [j] = col ;  not needed */
		    Fcpos [col] = j * fnr_curr ;
		}
	    }
	    else
	    {
		for (j = fncols ; j < rrdeg ; j++)
		{
		    col = Wrow [j] ;
		    DEBUG2 ((" "ID": col "ID" (ext)\n", j, col)) ;
		    ASSERT (Fcpos [col] < 0) ;
		    Fcols [j] = col ;
		    Fcpos [col] = j * fnr_curr ;
		}
	    }
	}
	fncols_extended = rrdeg ;
    }
    else
    {
	ASSERT (Fcols != Work->Wrow) ;
	Wrow = Work->Wrow ;
	for (j = 0 ; j < rrdeg ; j++)
	{
	    col = Wrow [j] ;
	    ASSERT (col >= 0 && col < Work->n_col) ;
	    if (Fcpos [col] < 0)
	    {
		DEBUG2 ((" col:: "ID" (ext)\n", col)) ;
		Fcols [fncols_extended] = col ;
		Fcpos [col] = fncols_extended * fnr_curr ;
		fncols_extended++ ;
	    }
	}
    }

    /* ---------------------------------------------------------------------- */
    /* pivot row and column have been extended */
    /* ---------------------------------------------------------------------- */

#ifndef NDEBUG
    ASSERT (fncols_extended <= fnc_curr) ;
    ASSERT (fnrows_extended <= fnr_curr) ;

    DEBUG6 (("Pivot col, after ext: "ID" "ID"\n", fnrows,fnrows_extended)) ;
    for (i = 0 ; i < fnrows_extended ; i++)
    {
	row = Frows [i] ;
	DEBUG7 ((" "ID": row "ID" pos "ID" old: %d", i, row, Frpos [row],
	    i < fnrows)) ;
	if (row == Work->pivrow ) DEBUG7 (("  <-- pivrow")) ;
	DEBUG7 (("\n")) ;
	ASSERT (Frpos [Frows [i]] == i) ;
    }

    DEBUG6 (("Pivot row position: "ID"\n", Frpos [Work->pivrow])) ;
    ASSERT (Frpos [Work->pivrow] >= 0) ;
    ASSERT (Frpos [Work->pivrow] < fnrows_extended) ;

    DEBUG6 (("Pivot row, after ext: "ID" "ID"\n", fncols,fncols_extended)) ;
    for (j = 0 ; j < fncols_extended ; j++)
    {
	col = Fcols [j] ;
	DEBUG7 ((" "ID": col "ID" pos "ID" old: %d", j, col, Fcpos [col],
	    j < fncols)) ;
	if (col == Work->pivcol ) DEBUG7 (("  <-- pivcol")) ;
	DEBUG7 (("\n")) ;
	ASSERT (Fcpos [Fcols [j]] == j * fnr_curr) ;
    }

    DEBUG6 (("Pivot col position: "ID"\n", Fcpos [Work->pivcol])) ;
    ASSERT (Fcpos [Work->pivcol] >= 0) ;
    ASSERT (Fcpos [Work->pivcol] < fncols_extended * fnr_curr) ;

#endif

    /* ---------------------------------------------------------------------- */
    /* Zero the newly extended frontal matrix */
    /* ---------------------------------------------------------------------- */

    zero_front (Work->Flblock, Work->Fublock, Work->Fcblock,
	fnrows, fncols, fnr_curr, fnc_curr,
	fnpiv, fnrows_extended, fncols_extended) ;

    /* ---------------------------------------------------------------------- */
    /* finalize extended row and column pattern of the frontal matrix */
    /* ---------------------------------------------------------------------- */

    Work->fnrows = fnrows_extended ;
    Work->fncols = fncols_extended ;

    ASSERT (fnrows_extended == Work->fnrows_new + 1) ;
    ASSERT (fncols_extended == Work->fncols_new + 1) ;

    return (TRUE) ;

}
GLOBAL Int UMF_init_front
(
    NumericType *Numeric,
    WorkType *Work
)
{
    /* ---------------------------------------------------------------------- */
    /* local variables */
    /* ---------------------------------------------------------------------- */

    Int i, j, fnr_curr, row, col, *Frows, *Fcols,
	*Fcpos, *Frpos, fncols, fnrows, *Wrow, fnr2, fnc2, rrdeg, ccdeg, *Wm,
	fnrows_extended ;
    Entry *Fcblock, *Fl, *Wy, *Wx ;

    /* ---------------------------------------------------------------------- */
    /* get current frontal matrix and check for frontal growth */
    /* ---------------------------------------------------------------------- */

#ifndef NDEBUG
    DEBUG0 (("INIT FRONT\n")) ;
    DEBUG1 (("CURR before init:\n")) ;
    UMF_dump_current_front (Numeric, Work, FALSE) ;
#endif
    if (Work->do_grow)
    {
	fnr2 = UMF_FRONTAL_GROWTH * Work->fnrows_new + 2 ;
	fnc2 = UMF_FRONTAL_GROWTH * Work->fncols_new + 2 ;
	if (!UMF_grow_front (Numeric, fnr2, fnc2, Work,
	    Work->pivrow_in_front ? 2 : 0))
	{
	    /* :: out of memory in umf_init_front :: */
	    DEBUGm4 (("out of memory: init front\n")) ;
	    return (FALSE) ;
	}
    }
#ifndef NDEBUG
    DEBUG1 (("CURR after grow:\n")) ;
    UMF_dump_current_front (Numeric, Work, FALSE) ;
    DEBUG1 (("fnrows new "ID" fncols new "ID"\n",
	Work->fnrows_new, Work->fncols_new)) ;
#endif
    ASSERT (Work->fnpiv == 0) ;
    fnr_curr = Work->fnr_curr ;
    ASSERT (Work->fnrows_new + 1 <= fnr_curr) ;
    ASSERT (Work->fncols_new + 1 <= Work->fnc_curr) ;
    ASSERT (fnr_curr >= 0) ;
    ASSERT (fnr_curr % 2 == 1) ;

    /* ---------------------------------------------------------------------- */
    /* get parameters */
    /* ---------------------------------------------------------------------- */

    /* current front is defined by pivot row and column */

    Frows = Work->Frows ;
    Fcols = Work->Fcols ;
    Frpos = Work->Frpos ;
    Fcpos = Work->Fcpos ;

    Work->fnzeros = 0 ;

    ccdeg = Work->ccdeg ;
    rrdeg = Work->rrdeg ;

    fnrows = Work->fnrows ;
    fncols = Work->fncols ;

    /* if both pivrow and pivcol are in front, then we extend the old one */
    /* in UMF_extend_front, rather than starting a new one here. */
    ASSERT (! (Work->pivrow_in_front && Work->pivcol_in_front)) ;

    /* ---------------------------------------------------------------------- */
    /* place pivot column pattern in frontal matrix */
    /* ---------------------------------------------------------------------- */

    Fl = Work->Flblock ;

    if (Work->pivcol_in_front)
    {
	/* Append the pivot column extension.
	 * Note that all we need to do is increment the size, since the
	 * candidate pivot column pattern is already in place in
	 * Frows [0 ... fnrows-1] (the old pattern), and
	 * Frows [fnrows ... fnrows + Work->ccdeg - 1] (the new
	 * pattern).  Frpos is also properly defined. */
	/* make a list of the new rows to scan */
	Work->fscan_row = fnrows ;	/* only scan the new rows */
	Work->NewRows = Work->Wrp ;
	Wy = Work->Wy ;
	for (i = 0 ; i < fnrows ; i++)
	{
	    Fl [i] = Wy [i] ;
	}
	fnrows_extended = fnrows + ccdeg ;
	for (i = fnrows ; i < fnrows_extended ; i++)
	{
	    Fl [i] = Wy [i] ;
	    /* flip the row, since Wrp must be < 0 */
	    row = Frows [i] ;
	    Work->NewRows [i] = FLIP (row) ;
	}
	fnrows = fnrows_extended ;
    }
    else
    {
	/* this is a completely new column */
	Work->fscan_row = 0 ;			/* scan all the rows */
	Work->NewRows = Frows ;
	Wm = Work->Wm ;
	Wx = Work->Wx ;
	for (i = 0 ; i < ccdeg ; i++)
	{
	    Fl [i] = Wx [i] ;
	    row = Wm [i] ;
	    Frows [i] = row ;
	    Frpos [row] = i ;
	}
	fnrows = ccdeg ;
    }

    Work->fnrows = fnrows ;

#ifndef NDEBUG
    DEBUG3 (("New Pivot col "ID" now in front, length "ID"\n",
	Work->pivcol, fnrows)) ;
    for (i = 0 ; i < fnrows ; i++)
    {
	DEBUG4 ((" "ID": row "ID"\n", i, Frows [i])) ;
	ASSERT (Frpos [Frows [i]] == i) ;
    }
#endif

    /* ---------------------------------------------------------------------- */
    /* place pivot row pattern in frontal matrix */
    /* ---------------------------------------------------------------------- */

    Wrow = Work->Wrow ;
    if (Work->pivrow_in_front)
    {
	/* append the pivot row extension */
	Work->fscan_col = fncols ;	/* only scan the new columns */
	Work->NewCols = Work->Wp ;
#ifndef NDEBUG
	for (j = 0 ; j < fncols ; j++)
	{
	    col = Fcols [j] ;
	    ASSERT (col >= 0 && col < Work->n_col) ;
	    ASSERT (Fcpos [col] == j * fnr_curr) ;
	}
#endif
	/* Wrow == Fcol for the IN_IN case, and for the OUT_IN case when
	 * the pivrow [IN][IN] happens to be the same as pivrow [OUT][IN].
	 * See UMF_local_search for more details. */
	ASSERT (IMPLIES (Work->pivcol_in_front, Wrow == Fcols)) ;
	if (Wrow == Fcols)
	{
	    for (j = fncols ; j < rrdeg ; j++)
	    {
		col = Wrow [j] ;
		/* Fcols [j] = col ; not needed */
		/* flip the col index, since Wp must be < 0 */
		Work->NewCols [j] = FLIP (col) ;
		Fcpos [col] = j * fnr_curr ;
	    }
	}
	else
	{
	    for (j = fncols ; j < rrdeg ; j++)
	    {
		col = Wrow [j] ;
		Fcols [j] = col ;
		/* flip the col index, since Wp must be < 0 */
		Work->NewCols [j] = FLIP (col) ;
		Fcpos [col] = j * fnr_curr ;
	    }
	}
    }
    else
    {
	/* this is a completely new row */
	Work->fscan_col = 0 ;			/* scan all the columns */
	Work->NewCols = Fcols ;
	for (j = 0 ; j < rrdeg ; j++)
	{
	    col = Wrow [j] ;
	    Fcols [j] = col ;
	    Fcpos [col] = j * fnr_curr ;
	}
    }

    DEBUGm1 (("rrdeg "ID" fncols "ID"\n", rrdeg, fncols)) ;
    fncols = rrdeg ;
    Work->fncols = fncols ;

    /* ---------------------------------------------------------------------- */
    /* clear the frontal matrix */
    /* ---------------------------------------------------------------------- */

    ASSERT (fnrows == Work->fnrows_new + 1) ;
    ASSERT (fncols == Work->fncols_new + 1) ;

    Fcblock = Work->Fcblock ;
    ASSERT (Fcblock != (Entry *) NULL) ;

    zero_init_front (fncols, fnrows, Fcblock, fnr_curr) ;

#ifndef NDEBUG
    DEBUG3 (("New Pivot row "ID" now in front, length "ID" fnr_curr "ID"\n",
		Work->pivrow, fncols, fnr_curr)) ;
    for (j = 0 ; j < fncols ; j++)
    {
	DEBUG4 (("col "ID" position "ID"\n", j, Fcols [j])) ;
	ASSERT (Fcpos [Fcols [j]] == j * fnr_curr) ;
    }
#endif

    /* ---------------------------------------------------------------------- */
    /* current workspace usage: */
    /* ---------------------------------------------------------------------- */

    /* Fcblock [0..fnr_curr-1, 0..fnc_curr-1]: space for the new frontal
     * matrix.  Fcblock (i,j) is located at Fcblock [i+j*fnr_curr] */

    return (TRUE) ;

}
Пример #3
0
GLOBAL Int UMF_start_front    /* returns TRUE if successful, FALSE otherwise */
(
    Int chain,
    NumericType *Numeric,
    WorkType *Work,
    SymbolicType *Symbolic
)
{
    Int fnrows_max, fncols_max, fnr2, fnc2, fsize, fcurr_size, maxfrsize,
	overflow, nb, f, cdeg ;
    double maxbytes ;

    nb = Symbolic->nb ;
    fnrows_max = Symbolic->Chain_maxrows [chain] ;
    fncols_max = Symbolic->Chain_maxcols [chain] ;

    DEBUGm2 (("Start Front for chain "ID".  fnrows_max "ID" fncols_max "ID"\n",
	chain, fnrows_max, fncols_max)) ;

    Work->fnrows_max = fnrows_max ;
    Work->fncols_max = fncols_max ;
    Work->any_skip = FALSE ;

    maxbytes = sizeof (Entry) *
	(double) (fnrows_max + nb) * (double) (fncols_max + nb) ;
    fcurr_size = Work->fcurr_size ;

    if (Symbolic->prefer_diagonal)
    {
	/* Get a rough upper bound on the degree of the first pivot column in
	 * this front.  Note that Col_degree is not maintained if diagonal
	 * pivoting is preferred.  For most matrices, the first pivot column
	 * of the first frontal matrix of a new chain has only one tuple in
	 * it anyway, so this bound is exact in that case. */
	Int col, tpi, e, *E, *Col_tuples, *Col_tlen, *Cols ;
	Tuple *tp, *tpend ;
	Unit *Memory, *p ;
	Element *ep ;
	E = Work->E ;
	Memory = Numeric->Memory ;
	Col_tuples = Numeric->Lip ;
	Col_tlen = Numeric->Lilen ;
	col = Work->nextcand ;
	tpi = Col_tuples [col] ;
	tp = (Tuple *) Memory + tpi ;
	tpend = tp + Col_tlen [col] ;
	cdeg = 0 ;
	DEBUGm3 (("\n=============== start front: col "ID" tlen "ID"\n",
		col, Col_tlen [col])) ;
	for ( ; tp < tpend ; tp++)
	{
	    DEBUG1 (("Tuple ("ID","ID")\n", tp->e, tp->f)) ;
	    e = tp->e ;
	    if (!E [e]) continue ;
	    f = tp->f ;
	    p = Memory + E [e] ;
	    ep = (Element *) p ;
	    p += UNITS (Element, 1) ;
	    Cols = (Int *) p ;
	    if (Cols [f] == EMPTY) continue ;
	    DEBUG1 (("  nrowsleft "ID"\n", ep->nrowsleft)) ;
	    cdeg += ep->nrowsleft ;
	}
#ifndef NDEBUG
	DEBUGm3 (("start front cdeg: "ID" col "ID"\n", cdeg, col)) ;
	UMF_dump_rowcol (1, Numeric, Work, col, FALSE) ;
#endif

	/* cdeg is now the rough upper bound on the degree of the next pivot
	 * column. */

	/* If AMD was called, we know the maximum number of nonzeros in any
	 * column of L.  Use this as an upper bound for cdeg, but add 2 to
	 * account for a small amount of off-diagonal pivoting. */
	if (Symbolic->amd_dmax > 0)
	{
	    cdeg = MIN (cdeg, Symbolic->amd_dmax) ;
	}

	/* Increase it to account for larger columns later on.
	 * Also ensure that it's larger than zero. */
	cdeg += 2 ;

	/* cdeg cannot be larger than fnrows_max */
	cdeg = MIN (cdeg, fnrows_max) ;

    }
    else
    {
	/* don't do the above cdeg computation */
	cdeg = 0 ;
    }

    DEBUGm2 (("fnrows max "ID" fncols_max "ID"\n", fnrows_max, fncols_max)) ;

    /* the current frontal matrix is empty */
    ASSERT (Work->fnrows == 0 && Work->fncols == 0 && Work->fnpiv == 0) ;

    /* maximum row dimension is always odd, to avoid bad cache effects */
    ASSERT (fnrows_max >= 0) ;
    ASSERT (fnrows_max % 2 == 1) ;

    /* ----------------------------------------------------------------------
     * allocate working array for current frontal matrix:
     * minimum size: 1-by-1
     * maximum size: fnrows_max-by-fncols_max
     * desired size:
     *
     *   if Numeric->front_alloc_init >= 0:
     *
     *	    for unsymmetric matrices:
     *	    Numeric->front_alloc_init * (fnrows_max-by-fncols_max)
     *
     *	    for symmetric matrices (diagonal pivoting preference, actually):
     *	    Numeric->front_alloc_init * (fnrows_max-by-fncols_max), or
     *	    cdeg*cdeg, whichever is smaller.
     *
     *   if Numeric->front_alloc_init < 0:
     *	    allocate a front of size -Numeric->front_alloc_init.
     *
     * Allocate the whole thing if it's small (less than 2*nb^2).  Make sure the
     * leading dimension of the frontal matrix is odd.
     *
     * Also allocate the nb-by-nb LU block, the dr-by-nb L block, and the
     * nb-by-dc U block.
     * ---------------------------------------------------------------------- */

    /* get the maximum front size, avoiding integer overflow */
    overflow = INT_OVERFLOW (maxbytes) ;
    if (overflow)
    {
	/* :: int overflow, max front size :: */
	maxfrsize = Int_MAX / sizeof (Entry) ;
    }
    else
    {
	maxfrsize = (fnrows_max + nb) * (fncols_max + nb) ;
    }
    ASSERT (!INT_OVERFLOW ((double) maxfrsize * sizeof (Entry))) ;

    if (Numeric->front_alloc_init < 0)
    {
	/* allocate a front of -Numeric->front_alloc_init entries */
	fsize = -Numeric->front_alloc_init ;
	fsize = MAX (1, fsize) ;
    }
    else
    {
	if (INT_OVERFLOW (Numeric->front_alloc_init * maxbytes))
	{
	    /* :: int overflow, requested front size :: */
	    fsize = Int_MAX / sizeof (Entry) ;
	}
	else
	{
	    fsize = Numeric->front_alloc_init * maxfrsize ;
	}

	if (cdeg > 0)
	{
	    /* diagonal pivoting is in use.  cdeg was computed above */
	    Int fsize2 ;

	    /* add the L and U blocks */
	    cdeg += nb ;

	    if (INT_OVERFLOW (((double) cdeg * (double) cdeg) * sizeof (Entry)))
	    {
		/* :: int overflow, symmetric front size :: */
		fsize2 = Int_MAX / sizeof (Entry) ;
	    }
	    else
	    {
		fsize2 = MAX (cdeg * cdeg, fcurr_size) ;
	    }
	    fsize = MIN (fsize, fsize2) ;
	}
    }

    fsize = MAX (fsize, 2*nb*nb) ;

    /* fsize and maxfrsize are now safe from integer overflow.  They both
     * include the size of the pivot blocks. */
    ASSERT (!INT_OVERFLOW ((double) fsize * sizeof (Entry))) ;

    Work->fnrows_new = 0 ;
    Work->fncols_new = 0 ;

    /* desired size is fnr2-by-fnc2 (includes L and U blocks): */
    DEBUGm2 (("    fsize "ID"  fcurr_size "ID"\n", fsize, fcurr_size)) ;
    DEBUGm2 (("    maxfrsize "ID"  fnr_curr "ID" fnc_curr "ID"\n", maxfrsize,
	Work->fnr_curr, Work->fnc_curr)) ;

    if (fsize >= maxfrsize && !overflow)
    {
	/* max working array is small, allocate all of it */
	fnr2 = fnrows_max + nb ;
	fnc2 = fncols_max + nb ;
	fsize = maxfrsize ;
	DEBUGm1 (("   sufficient for ("ID"+"ID")-by-("ID"+"ID")\n",
	    fnrows_max, nb, fncols_max, nb)) ;
    }
    else
    {
	/* allocate a smaller working array */
	if (fnrows_max <= fncols_max)
	{
	    fnr2 = (Int) sqrt ((double) fsize) ;
	    /* make sure fnr2 is odd */
	    fnr2 = MAX (fnr2, 1) ;
	    if (fnr2 % 2 == 0) fnr2++ ;
	    fnr2 = MIN (fnr2, fnrows_max + nb) ;
	    fnc2 = fsize / fnr2 ;
	}
	else
	{
	    fnc2 = (Int) sqrt ((double) fsize) ;
	    fnc2 = MIN (fnc2, fncols_max + nb) ;
	    fnr2 = fsize / fnc2 ;
	    /* make sure fnr2 is odd */
	    fnr2 = MAX (fnr2, 1) ;
	    if (fnr2 % 2 == 0)
	    {
		fnr2++ ;
		fnc2 = fsize / fnr2 ;
	    }
	}
	DEBUGm1 (("   smaller "ID"-by-"ID"\n", fnr2, fnc2)) ;
    }
    fnr2 = MIN (fnr2, fnrows_max + nb) ;
    fnc2 = MIN (fnc2, fncols_max + nb) ;
    ASSERT (fnr2 % 2 == 1) ;
    ASSERT (fnr2 * fnc2 <= fsize) ;

    fnr2 -= nb ;
    fnc2 -= nb ;
    ASSERT (fnr2 >= 0) ;
    ASSERT (fnc2 >= 0) ;

    if (fsize > fcurr_size)
    {
	DEBUGm1 (("   Grow front \n")) ;
	Work->do_grow = TRUE ;
	if (!UMF_grow_front (Numeric, fnr2, fnc2, Work, -1))
	{
	    /* since the minimum front size is 1-by-1, it would be nearly
	     * impossible to run out of memory here. */
	    DEBUGm4 (("out of memory: start front\n")) ;
	    return (FALSE) ;
	}
    }
    else
    {
	/* use the existing front */
	DEBUGm1 (("   existing front ok\n")) ;
	Work->fnr_curr = fnr2 ;
	Work->fnc_curr = fnc2 ;
	Work->Flblock  = Work->Flublock + nb * nb ;
	Work->Fublock  = Work->Flblock  + nb * fnr2 ;
	Work->Fcblock  = Work->Fublock  + nb * fnc2 ;
    }
    ASSERT (Work->Flblock  == Work->Flublock + Work->nb*Work->nb) ;
    ASSERT (Work->Fublock  == Work->Flblock  + Work->fnr_curr*Work->nb) ;
    ASSERT (Work->Fcblock  == Work->Fublock  + Work->nb*Work->fnc_curr) ;
    return (TRUE) ;
}