diff liboctave/UMFPACK/UMFPACK/Source/umf_local_search.c @ 5164:57077d0ddc8e

[project @ 2005-02-25 19:55:24 by jwe]
author jwe
date Fri, 25 Feb 2005 19:55:28 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/liboctave/UMFPACK/UMFPACK/Source/umf_local_search.c	Fri Feb 25 19:55:28 2005 +0000
@@ -0,0 +1,1947 @@
+/* ========================================================================== */
+/* === UMF_local_search ===================================================== */
+/* ========================================================================== */
+
+/* -------------------------------------------------------------------------- */
+/* UMFPACK Version 4.4, Copyright (c) 2005 by Timothy A. Davis.  CISE Dept,   */
+/* Univ. of Florida.  All Rights Reserved.  See ../Doc/License for License.   */
+/* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
+/* -------------------------------------------------------------------------- */
+
+/*
+    Perform pivot search to find pivot row and pivot column.
+    The pivot column is selected from the candidate set.  The candidate set
+    corresponds to a supercolumn from colamd or UMF_analyze.  The pivot column
+    is then removed from that set.  Constructs the pivot column pattern and
+    values.  Called by umf_kernel.  Returns UMFPACK_OK if successful, or
+    UMFPACK_WARNING_singular_matrix or UMFPACK_ERROR_different_pattern if not.
+*/
+
+#include "umf_internal.h"
+#include "umf_row_search.h"
+#include "umf_mem_free_tail_block.h"
+
+/* relaxed amalgamation control parameters are fixed at compile time */
+#define RELAX1 0.25
+#define SYM_RELAX1 0.0
+#define RELAX2 0.1
+#define RELAX3 0.125
+
+/* ========================================================================== */
+/* === remove_candidate ===================================================== */
+/* ========================================================================== */
+
+/* Remove a column from the set of candidate pivot columns. */
+
+PRIVATE void remove_candidate (Int jj, WorkType *Work, SymbolicType *Symbolic)
+{
+
+#ifndef NDEBUG
+    Int j ;
+    DEBUGm2 (("pivot column Candidates before remove: nCand "ID" ncand "ID
+	" lo "ID" hi "ID" jj "ID"\n", Work->nCandidates, Work->ncand,
+	Work->lo, Work->hi, jj)) ;
+    for (j = 0 ; j < Work->nCandidates ; j++)
+    {
+	Int col = Work->Candidates [j] ;
+	DEBUGm2 ((ID" ", col));
+	ASSERT (col >= 0 && col < Work->n_col) ;
+	/* ASSERT (NON_PIVOTAL_COL (col)) ; */
+	ASSERT (col >= Work->lo && col <= Work->hi) ;
+    }
+    DEBUGm2 (("\n")) ;
+#endif
+
+    if (Symbolic->fixQ)
+    {
+	DEBUGm2 (("FixQ\n")) ;
+	/* do not modify the column ordering */
+	ASSERT (Work->nCandidates == 1) ;
+	ASSERT (jj == 0) ;
+	if (Work->ncand > 1)
+	{
+	    Work->Candidates [0] = Work->nextcand++ ;
+	}
+	else
+	{
+	    Work->nCandidates = 0 ;
+	}
+    }
+    else
+    {
+	/* place the next candidate in the set */
+	if (Work->ncand > MAX_CANDIDATES)
+	{
+	    Work->Candidates [jj] = Work->nextcand++ ;
+	}
+	else
+	{
+	    ASSERT (Work->nCandidates == Work->ncand) ;
+	    Work->Candidates [jj] = Work->Candidates [Work->ncand - 1] ;
+	    Work->Candidates [Work->ncand - 1] = EMPTY ;
+	    Work->nCandidates-- ;
+	}
+    }
+    Work->ncand-- ;
+
+#ifndef NDEBUG
+    DEBUGm2 (("pivot column Candidates after remove: nCand "ID" ncand "ID
+	" lo "ID" hi "ID" jj "ID"\n", Work->nCandidates, Work->ncand, Work->lo,
+	Work->hi, jj)) ;
+    for (j = 0 ; j < Work->nCandidates ; j++)
+    {
+	Int col = Work->Candidates [j] ;
+	DEBUGm2 ((ID" ", col));
+	ASSERT (col >= 0 && col < Work->n_col) ;
+	/* ASSERT (NON_PIVOTAL_COL (col)) ; */
+	ASSERT (col >= Work->lo && col <= Work->hi) ;
+    }
+    DEBUGm2 (("\n")) ;
+    ASSERT (Work->ncand >= 0) ;
+    ASSERT (Work->nCandidates <= Work->ncand) ;
+#endif
+}
+
+/* ========================================================================== */
+/* === UMF_local_search ===================================================== */
+/* ========================================================================== */
+
+GLOBAL Int UMF_local_search
+(
+    NumericType *Numeric,
+    WorkType *Work,
+    SymbolicType *Symbolic
+)
+{
+    /* ---------------------------------------------------------------------- */
+    /* local variables */
+    /* ---------------------------------------------------------------------- */
+
+    double relax1 ;
+    Entry *Flblock, *Fublock, *Fs, *Fcblock, *C, *Wx, *Wy, *Fu, *Flublock,
+	*Flu ;
+    Int pos, nrows, *Cols, *Rows, e, f, status, max_cdeg, fnzeros, nb, j, col,
+	i, row, cdeg_in, rdeg [2][2], fnpiv, nothing [2], new_LUsize,
+	pivrow [2][2], pivcol [2], *Wp, *Fcpos, *Frpos, new_fnzeros, cdeg_out,
+	*Wm, *Wio, *Woi, *Woo, *Frows, *Fcols, fnrows, fncols, *E, deg, nr_in,
+	nc, thiscost, bestcost, nr_out, do_update, extra_cols, extra_rows,
+	extra_zeros, relaxed_front, do_extend, fnr_curr, fnc_curr, tpi,
+	*Col_tuples, *Col_degree, *Col_tlen, jj, jcand [2], freebie [2],
+	did_rowmerge, fnrows_new [2][2], fncols_new [2][2], search_pivcol_out,
+	*Diagonal_map, *Diagonal_imap, row2, col2 ;
+    Unit *Memory, *p ;
+    Tuple *tp, *tpend, *tp1, *tp2 ;
+    Element *ep ;
+
+#ifndef NDEBUG
+    Int debug_ok, n_row, n_col, *Row_degree ;
+    Row_degree = Numeric->Rperm ;	/* for NON_PIVOTAL_ROW macro only */
+#endif
+
+    /* ---------------------------------------------------------------------- */
+    /* get parameters */
+    /* ---------------------------------------------------------------------- */
+
+    Memory = Numeric->Memory ;
+    E = Work->E ;
+    Col_degree = Numeric->Cperm ;
+
+    Col_tuples = Numeric->Lip ;
+    Col_tlen   = Numeric->Lilen ;
+
+    Wx = Work->Wx ;
+    Wy = Work->Wy ;
+    Wp = Work->Wp ;
+    Wm = Work->Wm ;
+    Woi = Work->Woi ;
+    Wio = Work->Wio ;
+    Woo = Work->Woo ;
+    Fcpos = Work->Fcpos ;
+    Frpos = Work->Frpos ;
+    Frows = Work->Frows ;
+    Fcols = Work->Fcols ;
+    fnrows = Work->fnrows ;
+    fncols = Work->fncols ;
+    nb = Work->nb ;
+    fnr_curr = Work->fnr_curr ;
+    fnc_curr = Work->fnc_curr ;
+    fnpiv = Work->fnpiv ;
+    nothing [0] = EMPTY ;
+    nothing [1] = EMPTY ;
+    relax1 = (Symbolic->prefer_diagonal) ? SYM_RELAX1 : RELAX1 ;
+    fnzeros = Work->fnzeros ;
+    new_fnzeros = fnzeros ;
+    jj = EMPTY ;
+
+    Fcblock = Work->Fcblock ;	    /* current contribution block */
+    Flblock = Work->Flblock ;	    /* current L block */
+    Fublock = Work->Fublock ;	    /* current U block */
+    Flublock = Work->Flublock ;	    /* current LU block */
+
+    /* The pivot column degree cannot exceed max_cdeg */
+    max_cdeg = Work->fnrows_max ;
+    ASSERT (Work->fnrows_max <= Symbolic->maxnrows) ;
+    ASSERT (Work->fncols_max <= Symbolic->maxncols) ;
+
+    if (fnrows == 0 && fncols == 0)
+    {
+	/* frontal matrix is empty */
+	Work->firstsuper = Work->ksuper ;
+    }
+
+#ifndef NDEBUG
+    n_row = Work->n_row ;
+    n_col = Work->n_col ;
+    DEBUG2 (("\n========LOCAL SEARCH:  current frontal matrix: ========= \n")) ;
+    UMF_dump_current_front (Numeric, Work, TRUE) ;
+    if (UMF_debug > 0 || MAX (n_row, n_col) < 1000)
+    {
+	for (i = 0 ; i < MAX (n_row, n_col) ; i++)
+	{
+	    ASSERT (Wp [i] < 0) ;
+	}
+    }
+
+    DEBUGm2 ((ID" pivot column Candidates: lo "ID" hi "ID"\n",
+	Work->nCandidates, Work->lo, Work->hi)) ;
+    for (j = 0 ; j < Work->nCandidates ; j++)
+    {
+	col = Work->Candidates [j] ;
+	DEBUGm2 ((ID" ", col));
+	ASSERT (col >= 0 && col < n_col) ;
+	ASSERT (NON_PIVOTAL_COL (col)) ;
+	ASSERT (col >= Work->lo && col <= Work->hi) ;
+    }
+
+    DEBUGm2 (("\n")) ;
+    /* there are no 0-by-c or r-by-0 fronts, where c and r are > 0 */
+    /* a front is either 0-by-0, or r-by-c */
+    DEBUG2 (("\n\n::: "ID" : Npiv: "ID" + fnpiv "ID" = "ID". "
+	"size "ID"-by-"ID"\n", Work->frontid,
+	Work->npiv, Work->fnpiv, Work->npiv + Work->fnpiv, fnrows, fncols)) ;
+    ASSERT ((fnrows == 0 && fncols == 0) ||(fnrows != 0 && fncols != 0)) ;
+#endif
+
+    /* ====================================================================== */
+    /* === PIVOT SEARCH ===================================================== */
+    /* ====================================================================== */
+
+    /* initialize */
+
+    pivcol [IN] = EMPTY ;
+    pivcol [OUT] = EMPTY ;
+
+    cdeg_in = Int_MAX ;
+    cdeg_out = Int_MAX ;
+
+    pivrow [IN][IN] = EMPTY ;
+    pivrow [IN][OUT] = EMPTY ;
+    pivrow [OUT][IN] = EMPTY ;
+    pivrow [OUT][OUT] = EMPTY ;
+
+    rdeg [IN][IN] = Int_MAX ;
+    rdeg [IN][OUT] = Int_MAX ;
+    rdeg [OUT][IN] = Int_MAX ;
+    rdeg [OUT][OUT] = Int_MAX ;
+
+    freebie [IN] = FALSE ;
+    freebie [OUT] = FALSE ;
+
+    Work->pivot_case = EMPTY ;
+    bestcost = EMPTY ;
+
+    nr_out = EMPTY ;
+    nr_in = EMPTY ;
+
+    jcand [IN] = EMPTY ;
+    jcand [OUT] = EMPTY ;
+
+    fnrows_new [IN][IN] = EMPTY ;
+    fnrows_new [IN][OUT] = EMPTY ;
+    fnrows_new [OUT][IN] = EMPTY ;
+    fnrows_new [OUT][OUT] = EMPTY ;
+
+    fncols_new [IN][IN] = EMPTY ;
+    fncols_new [IN][OUT] = EMPTY ;
+    fncols_new [OUT][IN] = EMPTY ;
+    fncols_new [OUT][OUT] = EMPTY ;
+
+#ifndef NDEBUG
+	/* check Frpos */
+	DEBUG4 (("Check Frpos : fnrows "ID" col "ID" maxcdeg "ID"\n",
+		fnrows, pivcol [IN], max_cdeg)) ;
+	for (i = 0 ; i < fnrows ; i++)
+	{
+	    row = Frows [i] ;
+	    DEBUG4 (("  row: "ID"\n", row)) ;
+	    ASSERT (row >= 0 && row < n_row) ;
+	    ASSERT (Frpos [row] == i) ;
+	}
+	DEBUG4 (("All:\n")) ;
+	if (UMF_debug > 0 || n_row < 1000)
+	{
+	    Int cnt = fnrows ;
+	    for (row = 0 ; row < n_row ; row++)
+	    {
+		if (Frpos [row] == EMPTY)
+		{
+		    cnt++ ;
+		}
+		else
+		{
+		    DEBUG4 (("  row: "ID" pos "ID"\n", row, Frpos [row])) ;
+		}
+	    }
+	    ASSERT (cnt == n_row) ;
+	}
+#endif
+
+    /* ---------------------------------------------------------------------- */
+    /* find shortest column in the front, and shortest column not in the */
+    /* front, from the candidate pivot column set */
+    /* ---------------------------------------------------------------------- */
+
+    /* If there are too many candidates, then only look at the first */
+    /* MAX_CANDIDATES of them.   Otherwise, if there are O(n) candidates, */
+    /* this code could take O(n^2) time. */
+
+    /* ------------------------------------------------------------------ */
+    /* look in the candidate set for the best column */
+    /* ------------------------------------------------------------------ */
+
+    DEBUG2 (("Max candidates %d, Work->ncand "ID" jmax "ID"\n",
+	MAX_CANDIDATES, Work->ncand, Work->nCandidates)) ;
+    col = Work->Candidates [0] ;
+    ASSERT (Work->nCandidates > 0) ;
+    DEBUG3 (("Pivot column candidate: "ID" j = "ID"\n", col, j)) ;
+    ASSERT (col >= 0 && col < n_col) ;
+
+    /* there is no Col_degree if fixQ is true */
+    deg = Symbolic->fixQ ? EMPTY : Col_degree [col] ;
+
+#ifndef NDEBUG
+    DEBUG3 (("Pivot column candidate: "ID" cost: "ID"  Fcpos[col] "ID"\n",
+	col, deg, Fcpos [col])) ;
+    UMF_dump_rowcol (1, Numeric, Work, col, !Symbolic->fixQ) ;
+    if (Symbolic->fixQ)
+    {
+	DEBUG1 (("FIXQ: Candidates "ID" pivcol "ID" npiv "ID" fnpiv "ID
+	    " ndiscard "ID "\n", Work->nCandidates, col, Work->npiv,
+	    Work->fnpiv, Work->ndiscard)) ;
+	ASSERT (Work->nCandidates == 1) ;
+	ASSERT (col == Work->npiv + Work->fnpiv + Work->ndiscard) ;
+    }
+#endif
+
+    if (Fcpos [col] >= 0)
+    {
+	/* best column in front, so far */
+	pivcol [IN] = col ;
+	cdeg_in = deg ;		/* ignored, if fixQ is true */
+	jcand [IN] = 0 ;
+    }
+    else
+    {
+	/* best column not in front, so far */
+	pivcol [OUT] = col ;
+	cdeg_out = deg ;	/* ignored, if fixQ is true */
+	jcand [OUT] = 0 ;
+    }
+
+    /* look at the rest of the candidates */
+    for (j = 1 ; j < Work->nCandidates ; j++)
+    {
+	col = Work->Candidates [j] ;
+
+	DEBUG3 (("Pivot col candidate: "ID" j = "ID"\n", col, j)) ;
+	ASSERT (col >= 0 && col < n_col) ;
+	ASSERT (!Symbolic->fixQ) ;
+	deg = Col_degree [col] ;
+#ifndef NDEBUG
+	DEBUG3 (("Pivot col candidate: "ID" cost: "ID" Fcpos[col] "ID"\n",
+		col, deg, Fcpos [col])) ;
+	UMF_dump_rowcol (1, Numeric, Work, col, !Symbolic->fixQ) ;
+#endif
+	if (Fcpos [col] >= 0)
+	{
+#ifndef NDEBUG
+	    Int fs ;
+	    fs = Fcpos [col] / fnr_curr ;
+	    ASSERT (fs >= 0 && fs < fncols) ;
+#endif
+	    if (deg < cdeg_in || (deg == cdeg_in && col < pivcol [IN]))
+	    {
+		/* best column in front, so far */
+		pivcol [IN] = col ;
+		cdeg_in = deg ;
+		jcand [IN] = j ;
+	    }
+	}
+	else
+	{
+	    if (deg < cdeg_out || (deg == cdeg_out && col < pivcol [OUT]))
+	    {
+		/* best column not in front, so far */
+		pivcol [OUT] = col ;
+		cdeg_out = deg ;
+		jcand [OUT] = j ;
+	    }
+	}
+    }
+
+    DEBUG2 (("Pivcol in "ID" out "ID"\n", pivcol [IN], pivcol [OUT])) ;
+    ASSERT ((pivcol [IN] >= 0 && pivcol [IN] < n_col)
+	|| (pivcol [OUT] >= 0 && pivcol [OUT] < n_col)) ;
+
+    cdeg_in = EMPTY ;
+    cdeg_out = EMPTY ;
+
+    /* ---------------------------------------------------------------------- */
+    /* construct candidate column in front, and search for pivot rows */
+    /* ---------------------------------------------------------------------- */
+
+#ifndef NDEBUG
+    /* check Frpos */
+    DEBUG4 (("Prior to col update: fnrows "ID" col "ID" maxcdeg "ID"\n",
+	    fnrows, pivcol [IN], max_cdeg)) ;
+    for (i = 0 ; i < fnrows ; i++)
+    {
+	row = Frows [i] ;
+	DEBUG4 (("  row: "ID"\n", row)) ;
+	ASSERT (row >= 0 && row < n_row) ;
+	ASSERT (Frpos [row] == i) ;
+    }
+    DEBUG4 (("All:\n")) ;
+    if (UMF_debug > 0 || n_row < 1000)
+    {
+	Int cnt = fnrows ;
+	for (row = 0 ; row < n_row ; row++)
+	{
+	    if (Frpos [row] == EMPTY)
+	    {
+		cnt++ ;
+	    }
+	    else
+	    {
+		DEBUG4 (("  row: "ID" pos "ID"\n", row, Frpos [row])) ;
+	    }
+	}
+	ASSERT (cnt == n_row) ;
+    }
+#endif
+
+    if (pivcol [IN] != EMPTY)
+    {
+
+#ifndef NDEBUG
+	DEBUG2 (("col[IN] column "ID" in front at position = "ID"\n",
+		pivcol [IN], Fcpos [pivcol [IN]])) ;
+	UMF_dump_rowcol (1, Numeric, Work, pivcol [IN], !Symbolic->fixQ) ;
+#endif
+
+	/* the only way we can have a pivcol[IN] is if the front is not empty */
+	ASSERT (fnrows > 0 && fncols > 0) ;
+
+	DEBUG4 (("Update pivot column:\n")) ;
+	Fs  = Fcblock  +  Fcpos [pivcol [IN]] ;
+	Fu  = Fublock  + (Fcpos [pivcol [IN]] / fnr_curr) ;
+	Flu = Flublock + fnpiv * nb ;
+
+	/* ------------------------------------------------------------------ */
+	/* copy the pivot column from the U block into the LU block */
+	/* ------------------------------------------------------------------ */
+
+	/* This copy is permanent if the pivcol [IN] is chosen. */
+	for (i = 0 ; i < fnpiv ; i++)
+	{
+	    Flu [i] = Fu [i*fnc_curr] ;
+	}
+
+	/* ------------------------------------------------------------------ */
+	/* update the pivot column in the LU block using a triangular solve */
+	/* ------------------------------------------------------------------ */
+
+	/* This work will be discarded if the pivcol [OUT] is chosen instead.
+	 * It is permanent if the pivcol [IN] is chosen. */
+
+	if (fnpiv > 1)
+	{
+	    /* solve Lx=b, where b = U (:,k), stored in the LU block */
+
+#ifdef USE_NO_BLAS
+
+	    /* no BLAS available - use plain C code instead */
+	    Entry *Flub = Flublock ;
+	    for (j = 0 ; j < fnpiv ; j++)
+	    {
+		Entry Fuj = Flu [j] ;
+#pragma ivdep
+		for (i = j+1 ; i < fnpiv ; i++)
+		{
+		    /* Flu [i] -= Flublock [i + j*nb] * Flu [j] ; */
+		    MULT_SUB (Flu [i], Flub [i], Fuj) ;
+		}
+		Flub += nb ;
+	    }
+
+#else
+	    BLAS_TRSV (fnpiv, Flublock, Flu, nb) ;
+#endif
+
+	}
+
+	/* ------------------------------------------------------------------ */
+	/* copy the pivot column from the C block into Wy */
+	/* ------------------------------------------------------------------ */
+
+	for (i = 0 ; i < fnrows ; i++)
+	{
+	    Wy [i] = Fs [i] ;
+	}
+
+	/* ------------------------------------------------------------------ */
+	/* update the pivot column of L using a matrix-vector multiply */
+	/* ------------------------------------------------------------------ */
+
+	/* this work will be discarded if the pivcol [OUT] is chosen instead */
+
+#ifdef USE_NO_BLAS
+	/* no BLAS available - use plain C code instead */
+	for (j = 0 ; j < fnpiv ; j++)
+	{
+	    Entry Fuj, *Flub = Flblock + j * fnr_curr ;
+	    Fuj = Flu [j] ;
+	    if (IS_NONZERO (Fuj))
+	    {
+#pragma ivdep
+		for (i = 0 ; i < fnrows ; i++)
+		{
+		    /* Wy [i] -= Flblock [i+j*fnr_curr] * Fuj ; */
+		    MULT_SUB (Wy [i], Flub [i], Fuj) ;
+		}
+	    }
+	    /* Flblock += fnr_curr ; */
+	}
+#else
+	/* Using 1-based notation:
+	 * Wy (1:fnrows) -= Flblock (1:fnrows,1:fnpiv) * Flu (1:fnpiv) */
+	BLAS_GEMV (fnrows, fnpiv, Flblock, Flu, Wy, fnr_curr) ;
+#endif
+
+	/* ------------------------------------------------------------------ */
+
+#ifndef NDEBUG
+	DEBUG2 (("Wy after update: fnrows="ID"\n", fnrows)) ;
+	DEBUG4 ((" fnpiv="ID" \n", fnpiv)) ;
+	for (i = 0 ; i < fnrows ; i++)
+	{
+	    DEBUG4 ((ID" "ID" "ID, i, Frows [i], Frpos [Frows [i]])) ;
+	    EDEBUG4 (Wy [i]) ;
+	    DEBUG4 (("\n")) ;
+	}
+#endif
+
+	/* ------------------------------------------------------------------ */
+	/* construct the candidate column */
+	/* ------------------------------------------------------------------ */
+
+	cdeg_in = fnrows ;
+
+#ifndef NDEBUG
+	/* check Frpos */
+	DEBUG4 (("After col update: fnrows "ID" col "ID" maxcdeg "ID"\n",
+		fnrows, pivcol [IN], max_cdeg)) ;
+	for (i = 0 ; i < fnrows ; i++)
+	{
+	    row = Frows [i] ;
+	    DEBUG4 (("  row: "ID"\n", row)) ;
+	    ASSERT (row >= 0 && row < n_row) ;
+	    ASSERT (Frpos [row] == i) ;
+	}
+	DEBUG4 (("All:\n")) ;
+	if (UMF_debug > 0 || n_row < 1000)
+	{
+	    Int cnt = fnrows ;
+	    for (row = 0 ; row < n_row ; row++)
+	    {
+		if (Frpos [row] == EMPTY)
+		{
+		    cnt++ ;
+		}
+		else
+		{
+		    DEBUG4 (("  row: "ID" pos "ID"\n", row, Frpos [row])) ;
+		}
+	    }
+	    ASSERT (cnt == n_row) ;
+	}
+#endif
+
+#ifndef NDEBUG
+	/* check Frpos */
+	DEBUG4 (("COL ASSEMBLE: cdeg "ID"\nREDUCE COL in "ID" max_cdeg "ID"\n",
+		cdeg_in, pivcol [IN], max_cdeg)) ;
+	for (i = 0 ; i < cdeg_in ; i++)
+	{
+	    row = Frows [i] ;
+	    ASSERT (row >= 0 && row < n_row) ;
+	    ASSERT (Frpos [row] == i) ;
+	}
+	if (UMF_debug > 0 || n_row < 1000)
+	{
+	    Int cnt = cdeg_in ;
+	    for (row = 0 ; row < n_row ; row++)
+	    {
+		if (Frpos [row] == EMPTY) cnt++ ;
+	    }
+	    ASSERT (cnt == n_row) ;
+	}
+#endif
+
+	/* assemble column into Wy */
+
+	ASSERT (pivcol [IN] >= 0 && pivcol [IN] < n_col) ;
+	ASSERT (NON_PIVOTAL_COL (pivcol [IN])) ;
+
+	tpi = Col_tuples [pivcol [IN]] ;
+	if (tpi)
+	{
+	    tp = (Tuple *) (Memory + tpi) ;
+	    tp1 = tp ;
+	    tp2 = tp ;
+	    tpend = tp + Col_tlen [pivcol [IN]] ;
+	    for ( ; tp < tpend ; tp++)
+	    {
+		e = tp->e ;
+		ASSERT (e > 0 && e <= Work->nel) ;
+		if (!E [e]) continue ;	/* element already deallocated */
+		f = tp->f ;
+		p = Memory + E [e] ;
+		ep = (Element *) p ;
+		p += UNITS (Element, 1) ;
+		Cols = (Int *) p ;
+		if (Cols [f] == EMPTY) continue ; /* column already assembled */
+		ASSERT (pivcol [IN] == Cols [f]) ;
+
+		Rows = Cols + ep->ncols ;
+		nrows = ep->nrows ;
+		p += UNITS (Int, ep->ncols + nrows) ;
+		C = ((Entry *) p) + f * nrows ;
+
+		for (i = 0 ; i < nrows ; i++)
+		{
+		    row = Rows [i] ;
+		    if (row >= 0) /* skip this if already gone from element */
+		    {
+			ASSERT (row < n_row) ;
+			pos = Frpos [row] ;
+			if (pos < 0)
+			{
+			    /* new entry in the pattern - save Frpos */
+			    ASSERT (cdeg_in < n_row) ;
+			    if (cdeg_in >= max_cdeg)
+			    {
+				/* :: pattern change (cdeg in failure) :: */
+				DEBUGm4 (("cdeg_in failure\n")) ;
+				return (UMFPACK_ERROR_different_pattern) ;
+			    }
+			    Frpos [row] = cdeg_in ;
+			    Frows [cdeg_in] = row ;
+			    Wy [cdeg_in++] = C [i] ;
+			}
+			else
+			{
+			    /* entry already in pattern - sum values in Wy */
+			    /* Wy [pos] += C [i] ; */
+			    ASSERT (pos < max_cdeg) ;
+			    ASSEMBLE (Wy [pos], C [i]) ;
+			}
+		    }
+		}
+		*tp2++ = *tp ;	/* leave the tuple in the list */
+	    }
+	    Col_tlen [pivcol [IN]] = tp2 - tp1 ;
+	}
+
+	/* ------------------------------------------------------------------ */
+
+#ifndef NDEBUG
+	/* check Frpos again */
+	DEBUG4 (("COL DONE: cdeg "ID"\nREDUCE COL in "ID" max_cdeg "ID"\n",
+		cdeg_in, pivcol [IN], max_cdeg)) ;
+	for (i = 0 ; i < cdeg_in ; i++)
+	{
+	    row = Frows [i] ;
+	    ASSERT (row >= 0 && row < n_row) ;
+	    ASSERT (Frpos [row] == i) ;
+	}
+	if (UMF_debug > 0 || n_row < 1000)
+	{
+	    Int cnt = cdeg_in ;
+	    for (row = 0 ; row < n_row ; row++)
+	    {
+		if (Frpos [row] == EMPTY) cnt++ ;
+	    }
+	    ASSERT (cnt == n_row) ;
+	}
+#endif
+
+#ifndef NDEBUG
+	DEBUG4 (("Reduced column: cdeg in  "ID" fnrows_max "ID"\n",
+	    cdeg_in, Work->fnrows_max)) ;
+	for (i = 0 ; i < cdeg_in ; i++)
+	{
+	    DEBUG4 ((" "ID" "ID" "ID, i, Frows [i], Frpos [Frows [i]])) ;
+	    EDEBUG4 (Wy [i]) ;
+	    DEBUG4 (("\n")) ;
+	    ASSERT (i == Frpos [Frows [i]]) ;
+	}
+	ASSERT (cdeg_in <= Work->fnrows_max) ;
+#endif
+
+	/* ------------------------------------------------------------------ */
+	/* cdeg_in is now the exact degree of this column */
+	/* ------------------------------------------------------------------ */
+
+	nr_in = cdeg_in - fnrows ;
+
+	/* since there are no 0-by-x fronts, if there is a pivcol [IN] the */
+	/* front must have at least one row. */
+	ASSERT (cdeg_in > 0) ;
+
+	/* new degree of pivcol [IN], excluding current front is nr_in */
+	/* column expands by nr_in rows */
+
+	/* ------------------------------------------------------------------ */
+	/* search for two candidate pivot rows */
+	/* ------------------------------------------------------------------ */
+
+	/* for the IN_IN pivot row (if any), */
+	/* extend the pattern in place, using Fcols */
+	status = UMF_row_search (Numeric, Work, Symbolic,
+	    fnrows, cdeg_in, Frows, Frpos,   /* pattern of column to search */
+	    pivrow [IN], rdeg [IN], Fcols, Wio, nothing, Wy,
+	    pivcol [IN], freebie) ;
+	ASSERT (!freebie [IN] && !freebie [OUT]) ;
+
+	/* ------------------------------------------------------------------ */
+	/* fatal error if matrix pattern has changed since symbolic analysis */
+	/* ------------------------------------------------------------------ */
+
+	if (status == UMFPACK_ERROR_different_pattern)
+	{
+	    /* :: pattern change (row search IN failure) :: */
+	    DEBUGm4 (("row search IN failure\n")) ;
+	    return (UMFPACK_ERROR_different_pattern) ;
+	}
+
+	/* ------------------------------------------------------------------ */
+	/* we now must have a structural pivot */
+	/* ------------------------------------------------------------------ */
+
+	/* Since the pivcol[IN] exists, there must be at least one row in the */
+	/* current frontal matrix, and so we must have found a structural */
+	/* pivot.  The numerical value might be zero, of course. */
+
+	ASSERT (status != UMFPACK_WARNING_singular_matrix) ;
+
+	/* ------------------------------------------------------------------ */
+	/* evaluate IN_IN option */
+	/* ------------------------------------------------------------------ */
+
+	if (pivrow [IN][IN] != EMPTY)
+	{
+	    /* The current front would become an (implicit) LUson.
+	     * Both candidate pivot row and column are in the current front.
+	     * Cost is how much the current front would expand */
+
+	    /* pivrow[IN][IN] candidates are not found via row merge search */
+
+	    ASSERT (fnrows >= 0 && fncols >= 0) ;
+
+	    ASSERT (cdeg_in > 0) ;
+	    nc = rdeg [IN][IN] - fncols ;
+
+	    thiscost =
+		/* each column in front (except pivot column) grows by nr_in: */
+		(nr_in * (fncols - 1)) +
+		/* new columns not in old front: */
+		(nc * (cdeg_in - 1)) ;
+
+	    /* no extra cost to relaxed amalgamation */
+
+	    ASSERT (fnrows + nr_in == cdeg_in) ;
+	    ASSERT (fncols + nc == rdeg [IN][IN]) ;
+
+	    /* size of relaxed front (after pivot row column removed): */
+	    fnrows_new [IN][IN] = (fnrows-1) + nr_in ;
+	    fncols_new [IN][IN] = (fncols-1) + nc ;
+	    /* relaxed_front = fnrows_new [IN][IN] * fncols_new [IN][IN] ; */
+
+	    do_extend = TRUE ;
+
+	    DEBUG2 (("Evaluating option IN-IN:\n")) ;
+	    DEBUG2 (("Work->fnzeros "ID" fnpiv "ID" nr_in "ID" nc "ID"\n",
+		Work->fnzeros, fnpiv, nr_in, nc)) ;
+	    DEBUG2 (("fncols "ID" fnrows "ID"\n", fncols, fnrows)) ;
+
+	    /* determine if BLAS-3 update should be applied before extending. */
+	    /* update if too many zero entries accumulate in the LU block */
+	    fnzeros = Work->fnzeros + fnpiv * (nr_in + nc) ;
+
+	    DEBUG2 (("fnzeros "ID"\n", fnzeros)) ;
+
+	    new_LUsize = (fnpiv+1) * (fnrows + nr_in + fncols + nc) ;
+
+	    DEBUG2 (("new_LUsize "ID"\n", new_LUsize)) ;
+
+	    /* There are fnpiv pivots currently in the front.  This one
+	     * will be the (fnpiv+1)st pivot, if it is extended. */
+
+	    /* RELAX2 parameter uses a double relop, but ignore NaN case: */
+	    do_update = fnpiv > 0 &&
+		(((double) fnzeros) / ((double) new_LUsize)) > RELAX2 ;
+
+	    DEBUG2 (("do_update "ID"\n", do_update))
+
+	    DEBUG2 (("option IN  IN : nr "ID" nc "ID" cost "ID"(0) relax "ID
+		"\n", nr_in, nc, thiscost, do_extend)) ;
+
+	    /* this is the best option seen so far */
+	    Work->pivot_case = IN_IN ;
+	    bestcost = thiscost ;
+
+	    /* do the amalgamation and extend the front */
+	    Work->do_extend = do_extend ;
+	    Work->do_update = do_update ;
+	    new_fnzeros = fnzeros ;
+
+	}
+
+	/* ------------------------------------------------------------------ */
+	/* evaluate IN_OUT option */
+	/* ------------------------------------------------------------------ */
+
+	if (pivrow [IN][OUT] != EMPTY)
+	{
+	    /* The current front would become a Uson of the new front.
+	     * Candidate pivot column is in the current front, but the
+	     * candidate pivot row is not. */
+
+	    ASSERT (fnrows >= 0 && fncols > 0) ;
+	    ASSERT (cdeg_in > 0) ;
+
+	    /* must be at least one row outside the front */
+	    /* (the pivrow [IN][OUT] itself) */
+	    ASSERT (nr_in >= 1) ;
+
+	    /* count columns not in current front */
+	    nc = 0 ;
+#ifndef NDEBUG
+	    debug_ok = FALSE ;
+#endif
+	    for (i = 0 ; i < rdeg [IN][OUT] ; i++)
+	    {
+		col = Wio [i] ;
+		DEBUG4 (("counting col "ID" Fcpos[] = "ID"\n", col,
+		    Fcpos [col])) ;
+		ASSERT (col >= 0 && col < n_col && NON_PIVOTAL_COL (col)) ;
+		if (Fcpos [col] < 0) nc++ ;
+#ifndef NDEBUG
+		/* we must see the pivot column somewhere */
+		if (col == pivcol [IN])
+		{
+		    ASSERT (Fcpos [col] >= 0) ;
+		    debug_ok = TRUE ;
+		}
+#endif
+	    }
+	    ASSERT (debug_ok) ;
+
+	    thiscost =
+		/* each row in front grows by nc: */
+		(nc * fnrows) +
+		/* new rows not affected by front: */
+		((nr_in-1) * (rdeg [IN][OUT]-1)) ;
+
+	    /* check the cost of relaxed IN_OUT amalgamation */
+
+	    extra_cols = ((fncols-1) + nc ) - (rdeg [IN][OUT] - 1) ;
+	    ASSERT (extra_cols >= 0) ;
+	    ASSERT (fncols + nc == extra_cols + rdeg [IN][OUT]) ;
+	    extra_zeros = (nr_in-1) * extra_cols ;	/* symbolic fill-in */
+
+	    ASSERT (fnrows + nr_in == cdeg_in) ;
+	    ASSERT (fncols + nc == rdeg [IN][OUT] + extra_cols) ;
+
+	    /* size of relaxed front (after pivot column removed): */
+	    fnrows_new [IN][OUT] = fnrows + (nr_in-1) ;
+	    fncols_new [IN][OUT] = (fncols-1) + nc ;
+	    relaxed_front = fnrows_new [IN][OUT] * fncols_new [IN][OUT] ;
+
+	    /* do relaxed amalgamation if the extra zeros are no more */
+	    /* than a fraction (default 0.25) of the relaxed front */
+	    /* if relax = 0: no extra zeros allowed */
+	    /* if relax = +inf: always amalgamate */
+
+	    /* relax parameter uses a double relop, but ignore NaN case: */
+	    if (extra_zeros == 0)
+	    {
+		do_extend = TRUE ;
+	    }
+	    else
+	    {
+		do_extend = ((double) extra_zeros) <
+		   (relax1 * (double) relaxed_front) ;
+	    }
+
+	    if (do_extend)
+	    {
+		/* count the cost of relaxed amalgamation */
+		thiscost += extra_zeros ;
+
+		DEBUG2 (("Evaluating option IN-OUT:\n")) ;
+		DEBUG2 (("Work->fnzeros "ID" fnpiv "ID" nr_in "ID" nc "ID"\n",
+		    Work->fnzeros, fnpiv, nr_in, nc)) ;
+		DEBUG2 (("fncols "ID" fnrows "ID"\n", fncols, fnrows)) ;
+
+		/* determine if BLAS-3 update to be applied before extending. */
+		/* update if too many zero entries accumulate in the LU block */
+		fnzeros = Work->fnzeros + fnpiv * (nr_in + nc) ;
+
+		DEBUG2 (("fnzeros "ID"\n", fnzeros)) ;
+
+		new_LUsize = (fnpiv+1) * (fnrows + nr_in + fncols + nc) ;
+
+		DEBUG2 (("new_LUsize "ID"\n", new_LUsize)) ;
+
+		/* RELAX3 parameter uses a double relop, ignore NaN case: */
+		do_update = fnpiv > 0 &&
+		    (((double) fnzeros) / ((double) new_LUsize)) > RELAX3 ;
+		DEBUG2 (("do_update "ID"\n", do_update))
+
+	    }
+	    else
+	    {
+		/* the current front would not be extended */
+		do_update = fnpiv > 0 ;
+		fnzeros = 0 ;
+		DEBUG2 (("IN-OUT do_update forced true: "ID"\n", do_update)) ;
+
+		/* The new front would be just big enough to hold the new
+		 * pivot row and column. */
+		fnrows_new [IN][OUT] = cdeg_in - 1 ;
+		fncols_new [IN][OUT] = rdeg [IN][OUT] - 1 ;
+
+	    }
+
+	    DEBUG2 (("option IN  OUT: nr "ID" nc "ID" cost "ID"("ID") relax "ID
+		"\n", nr_in, nc, thiscost, extra_zeros, do_extend)) ;
+
+	    if (bestcost == EMPTY || thiscost < bestcost)
+	    {
+		/* this is the best option seen so far */
+		Work->pivot_case = IN_OUT ;
+		bestcost = thiscost ;
+		Work->do_extend = do_extend ;
+		Work->do_update = do_update ;
+		new_fnzeros = fnzeros ;
+	    }
+	}
+    }
+
+    /* ---------------------------------------------------------------------- */
+    /* construct candidate column not in front, and search for pivot rows */
+    /* ---------------------------------------------------------------------- */
+
+    search_pivcol_out = (bestcost != 0 && pivcol [OUT] != EMPTY) ;
+    if (Symbolic->prefer_diagonal)
+    {
+	search_pivcol_out = search_pivcol_out && (pivrow [IN][IN] == EMPTY) ;
+    }
+
+    if (search_pivcol_out)
+    {
+
+#ifndef NDEBUG
+	DEBUG2 (("out_col column "ID" NOT in front at position = "ID"\n",
+		pivcol [OUT], Fcpos [pivcol [OUT]])) ;
+	UMF_dump_rowcol (1, Numeric, Work, pivcol [OUT], !Symbolic->fixQ) ;
+	DEBUG2 (("fncols "ID" fncols_max "ID"\n", fncols, Work->fncols_max)) ;
+	ASSERT (fncols < Work->fncols_max) ;
+#endif
+
+	/* Use Wx as temporary workspace to construct the pivcol [OUT] */
+
+
+	/* ------------------------------------------------------------------ */
+	/* construct the candidate column (currently not in the front) */
+	/* ------------------------------------------------------------------ */
+
+	/* Construct the column in Wx, Wm, using Wp for the positions: */
+	/* Wm [0..cdeg_out-1]	list of row indices in the column */
+	/* Wx [0..cdeg_out-1]	list of corresponding numerical values */
+	/* Wp [0..n-1] starts as all negative, and ends that way too. */
+
+	cdeg_out = 0 ;
+
+#ifndef NDEBUG
+	/* check Wp */
+	DEBUG4 (("COL ASSEMBLE: cdeg 0\nREDUCE COL out "ID"\n", pivcol [OUT])) ;
+	if (UMF_debug > 0 || MAX (n_row, n_col) < 1000)
+	{
+	    for (i = 0 ; i < MAX (n_row, n_col) ; i++)
+	    {
+		ASSERT (Wp [i] < 0) ;
+	    }
+	}
+	DEBUG4 (("max_cdeg: "ID"\n", max_cdeg)) ;
+#endif
+
+	ASSERT (pivcol [OUT] >= 0 && pivcol [OUT] < n_col) ;
+	ASSERT (NON_PIVOTAL_COL (pivcol [OUT])) ;
+
+	tpi = Col_tuples [pivcol [OUT]] ;
+	if (tpi)
+	{
+	    tp = (Tuple *) (Memory + tpi) ;
+	    tp1 = tp ;
+	    tp2 = tp ;
+	    tpend = tp + Col_tlen [pivcol [OUT]] ;
+	    for ( ; tp < tpend ; tp++)
+	    {
+		e = tp->e ;
+		ASSERT (e > 0 && e <= Work->nel) ;
+		if (!E [e]) continue ;	/* element already deallocated */
+		f = tp->f ;
+		p = Memory + E [e] ;
+		ep = (Element *) p ;
+		p += UNITS (Element, 1) ;
+		Cols = (Int *) p ;
+		if (Cols [f] == EMPTY) continue ; /* column already assembled */
+		ASSERT (pivcol [OUT] == Cols [f]) ;
+
+		Rows = Cols + ep->ncols ;
+		nrows = ep->nrows ;
+		p += UNITS (Int, ep->ncols + nrows) ;
+		C = ((Entry *) p) + f * nrows ;
+
+		for (i = 0 ; i < nrows ; i++)
+		{
+		    row = Rows [i] ;
+		    if (row >= 0) /* skip this if already gone from element */
+		    {
+			ASSERT (row < n_row) ;
+			pos = Wp [row] ;
+			if (pos < 0)
+			{
+			    /* new entry in the pattern - save Wp */
+			    ASSERT (cdeg_out < n_row) ;
+			    if (cdeg_out >= max_cdeg)
+			    {
+				/* :: pattern change (cdeg out failure) :: */
+				DEBUGm4 (("cdeg out failure\n")) ;
+				return (UMFPACK_ERROR_different_pattern) ;
+			    }
+			    Wp [row] = cdeg_out ;
+			    Wm [cdeg_out] = row ;
+			    Wx [cdeg_out++] = C [i] ;
+			}
+			else
+			{
+			    /* entry already in pattern - sum the values */
+			    /* Wx [pos] += C [i] ; */
+			    ASSEMBLE (Wx [pos], C [i]) ;
+			}
+		    }
+		}
+		*tp2++ = *tp ;	/* leave the tuple in the list */
+	    }
+	    Col_tlen [pivcol [OUT]] = tp2 - tp1 ;
+	}
+
+	/* ------------------------------------------------------------------ */
+
+#ifndef NDEBUG
+	DEBUG4 (("Reduced column: cdeg out "ID"\n", cdeg_out)) ;
+	for (i = 0 ; i < cdeg_out ; i++)
+	{
+	    DEBUG4 ((" "ID" "ID" "ID, i, Wm [i], Wp [Wm [i]])) ;
+	    EDEBUG4 (Wx [i]) ;
+	    DEBUG4 (("\n")) ;
+	    ASSERT (i == Wp [Wm [i]]) ;
+	}
+#endif
+
+	/* ------------------------------------------------------------------ */
+	/* new degree of pivcol [OUT] is cdeg_out */
+	/* ------------------------------------------------------------------ */
+
+	/* search for two candidate pivot rows */
+	status = UMF_row_search (Numeric, Work, Symbolic,
+	    0, cdeg_out, Wm, Wp, /* pattern of column to search */
+	    pivrow [OUT], rdeg [OUT], Woi, Woo, pivrow [IN], Wx,
+	    pivcol [OUT], freebie) ;
+
+	/* ------------------------------------------------------------------ */
+	/* fatal error if matrix pattern has changed since symbolic analysis */
+	/* ------------------------------------------------------------------ */
+
+	if (status == UMFPACK_ERROR_different_pattern)
+	{
+	    /* :: pattern change detected in umf_local_search :: */
+	    return (UMFPACK_ERROR_different_pattern) ;
+	}
+
+	/* ------------------------------------------------------------------ */
+	/* Clear Wp */
+	/* ------------------------------------------------------------------ */
+
+	for (i = 0 ; i < cdeg_out ; i++)
+	{
+	    Wp [Wm [i]] = EMPTY ;	/* clear Wp */
+	}
+
+	/* ------------------------------------------------------------------ */
+	/* check for rectangular, singular matrix */
+	/* ------------------------------------------------------------------ */
+
+	if (status == UMFPACK_WARNING_singular_matrix)
+	{
+	    /* Pivot column is empty, and row-merge set is empty too.  The
+	     * matrix is structurally singular.  The current frontal matrix must
+	     * be empty, too.  It it weren't, and pivcol [OUT] exists, then
+	     * there would be at least one row that could be selected.  Since
+	     * the current front is empty, pivcol [IN] must also be EMPTY.
+	     */
+
+	    DEBUGm4 (("Note: pivcol [OUT]: "ID" discard\n", pivcol [OUT])) ;
+	    ASSERT ((Work->fnrows == 0 && Work->fncols == 0)) ;
+	    ASSERT (pivcol [IN] == EMPTY) ;
+
+	    /* remove the failed pivcol [OUT] from candidate set */
+	    ASSERT (pivcol [OUT] == Work->Candidates [jcand [OUT]]) ;
+	    remove_candidate (jcand [OUT], Work, Symbolic) ;
+	    Work->ndiscard++ ;
+
+	    /* delete all of the tuples, and all contributions to this column */
+	    DEBUG1 (("Prune tuples of dead outcol: "ID"\n", pivcol [OUT])) ;
+	    Col_tlen [pivcol [OUT]] = 0 ;
+	    UMF_mem_free_tail_block (Numeric, Col_tuples [pivcol [OUT]]) ;
+	    Col_tuples [pivcol [OUT]] = 0 ;
+
+	    /* no pivot found at all */
+	    return (UMFPACK_WARNING_singular_matrix) ;
+	}
+
+	/* ------------------------------------------------------------------ */
+
+	if (freebie [IN])
+	{
+	    /* the "in" row is the same as the "in" row for the "in" column */
+	    Woi = Fcols ;
+	    rdeg [OUT][IN] = rdeg [IN][IN] ;
+	    DEBUG4 (("Freebie in, row "ID"\n", pivrow [IN][IN])) ;
+	}
+
+	if (freebie [OUT])
+	{
+	    /* the "out" row is the same as the "out" row for the "in" column */
+	    Woo = Wio ;
+	    rdeg [OUT][OUT] = rdeg [IN][OUT] ;
+	    DEBUG4 (("Freebie out, row "ID"\n", pivrow [IN][OUT])) ;
+	}
+
+	/* ------------------------------------------------------------------ */
+	/* evaluate OUT_IN option */
+	/* ------------------------------------------------------------------ */
+
+	if (pivrow [OUT][IN] != EMPTY)
+	{
+	    /* The current front would become an Lson of the new front.
+	     * The candidate pivot row is in the current front, but the
+	     * candidate pivot column is not. */
+
+	    ASSERT (fnrows > 0 && fncols >= 0) ;
+
+	    did_rowmerge = (cdeg_out == 0) ;
+	    if (did_rowmerge)
+	    {
+		/* pivrow [OUT][IN] was found via row merge search */
+		/* it is not (yet) in the pivot column pattern (add it now) */
+		DEBUGm4 (("did row merge OUT col, IN row\n")) ;
+		Wm [0] = pivrow [OUT][IN] ;
+		CLEAR (Wx [0]) ;
+		cdeg_out = 1 ;
+		ASSERT (nr_out == EMPTY) ;
+	    }
+
+	    nc = rdeg [OUT][IN] - fncols ;
+	    ASSERT (nc >= 1) ;
+
+	    /* count rows not in current front */
+	    nr_out = 0 ;
+#ifndef NDEBUG
+	    debug_ok = FALSE ;
+#endif
+	    for (i = 0 ; i < cdeg_out ; i++)
+	    {
+		row = Wm [i] ;
+		ASSERT (row >= 0 && row < n_row && NON_PIVOTAL_ROW (row)) ;
+		if (Frpos [row] < 0 || Frpos [row] >= fnrows) nr_out++ ;
+#ifndef NDEBUG
+		/* we must see the pivot row somewhere */
+		if (row == pivrow [OUT][IN])
+		{
+		    ASSERT (Frpos [row] >= 0) ;
+		    debug_ok = TRUE ;
+		}
+#endif
+	    }
+	    ASSERT (debug_ok) ;
+
+	    thiscost =
+		/* each column in front grows by nr_out: */
+		(nr_out * fncols) +
+		/* new cols not affected by front: */
+		((nc-1) * (cdeg_out-1)) ;
+
+	    /* check the cost of relaxed OUT_IN amalgamation */
+
+	    extra_rows = ((fnrows-1) + nr_out) - (cdeg_out - 1) ;
+	    ASSERT (extra_rows >= 0) ;
+	    ASSERT (fnrows + nr_out == extra_rows + cdeg_out) ;
+	    extra_zeros = (nc-1) * extra_rows ;	/* symbolic fill-in */
+
+	    ASSERT (fnrows + nr_out == cdeg_out + extra_rows) ;
+	    ASSERT (fncols + nc == rdeg [OUT][IN]) ;
+
+	    /* size of relaxed front (after pivot row removed): */
+	    fnrows_new [OUT][IN] = (fnrows-1) + nr_out ;
+	    fncols_new [OUT][IN] = fncols + (nc-1) ;
+	    relaxed_front = fnrows_new [OUT][IN] * fncols_new [OUT][IN] ;
+
+	    /* do relaxed amalgamation if the extra zeros are no more */
+	    /* than a fraction (default 0.25) of the relaxed front */
+	    /* if relax = 0: no extra zeros allowed */
+	    /* if relax = +inf: always amalgamate */
+	    if (did_rowmerge)
+	    {
+		do_extend = FALSE ;
+	    }
+	    else
+	    {
+		/* relax parameter uses a double relop, but ignore NaN case: */
+		if (extra_zeros == 0)
+		{
+		    do_extend = TRUE ;
+		}
+		else
+		{
+		    do_extend = ((double) extra_zeros) <
+		       (relax1 * (double) relaxed_front) ;
+		}
+	    }
+
+	    if (do_extend)
+	    {
+		/* count the cost of relaxed amalgamation */
+		thiscost += extra_zeros ;
+
+		DEBUG2 (("Evaluating option OUT-IN:\n")) ;
+		DEBUG2 ((" Work->fnzeros "ID" fnpiv "ID" nr_out "ID" nc "ID"\n",
+		Work->fnzeros, fnpiv, nr_out, nc)) ;
+		DEBUG2 (("fncols "ID" fnrows "ID"\n", fncols, fnrows)) ;
+
+		/* determine if BLAS-3 update to be applied before extending. */
+		/* update if too many zero entries accumulate in the LU block */
+		fnzeros = Work->fnzeros + fnpiv * (nr_out + nc) ;
+
+		DEBUG2 (("fnzeros "ID"\n", fnzeros)) ;
+
+		new_LUsize = (fnpiv+1) * (fnrows + nr_out + fncols + nc) ;
+
+		DEBUG2 (("new_LUsize "ID"\n", new_LUsize)) ;
+
+		/* RELAX3 parameter uses a double relop, ignore NaN case: */
+		do_update = fnpiv > 0 &&
+		    (((double) fnzeros) / ((double) new_LUsize)) > RELAX3 ;
+		DEBUG2 (("do_update "ID"\n", do_update))
+	    }
+	    else
+	    {
+		/* the current front would not be extended */
+		do_update = fnpiv > 0 ;
+		fnzeros = 0 ;
+		DEBUG2 (("OUT-IN do_update forced true: "ID"\n", do_update)) ;
+
+		/* The new front would be just big enough to hold the new
+		 * pivot row and column. */
+		fnrows_new [OUT][IN] = cdeg_out - 1 ;
+		fncols_new [OUT][IN] = rdeg [OUT][IN] - 1 ;
+	    }
+
+	    DEBUG2 (("option OUT IN : nr "ID" nc "ID" cost "ID"("ID") relax "ID
+		"\n", nr_out, nc, thiscost, extra_zeros, do_extend)) ;
+
+	    if (bestcost == EMPTY || thiscost < bestcost)
+	    {
+		/* this is the best option seen so far */
+		Work->pivot_case = OUT_IN ;
+		bestcost = thiscost ;
+		Work->do_extend = do_extend ;
+		Work->do_update = do_update ;
+		new_fnzeros = fnzeros ;
+	    }
+	}
+
+	/* ------------------------------------------------------------------ */
+	/* evaluate OUT_OUT option */
+	/* ------------------------------------------------------------------ */
+
+	if (pivrow [OUT][OUT] != EMPTY)
+	{
+	    /* Neither the candidate pivot row nor the candidate pivot column
+	     * are in the current front. */
+
+	    ASSERT (fnrows >= 0 && fncols >= 0) ;
+
+	    did_rowmerge = (cdeg_out == 0) ;
+	    if (did_rowmerge)
+	    {
+		/* pivrow [OUT][OUT] was found via row merge search */
+		/* it is not (yet) in the pivot column pattern (add it now) */
+		DEBUGm4 (("did row merge OUT col, OUT row\n")) ;
+		Wm [0] = pivrow [OUT][OUT] ;
+		CLEAR (Wx [0]) ;
+		cdeg_out = 1 ;
+		ASSERT (nr_out == EMPTY) ;
+		nr_out = 1 ;
+	    }
+
+	    if (fnrows == 0 && fncols == 0)
+	    {
+		/* the current front is completely empty */
+		ASSERT (fnpiv == 0) ;
+		nc = rdeg [OUT][OUT] ;
+		extra_cols = 0 ;
+		nr_out = cdeg_out ;
+		extra_rows = 0 ;
+		extra_zeros = 0 ;
+
+		thiscost = (nc-1) * (cdeg_out-1) ; /* new columns only */
+
+		/* size of new front: */
+		fnrows_new [OUT][OUT] = nr_out-1 ;
+		fncols_new [OUT][OUT] = nc-1 ;
+		relaxed_front = fnrows_new [OUT][OUT] * fncols_new [OUT][OUT] ;
+	    }
+	    else
+	    {
+
+		/* count rows not in current front */
+		if (nr_out == EMPTY)
+		{
+		    nr_out = 0 ;
+#ifndef NDEBUG
+		    debug_ok = FALSE ;
+#endif
+		    for (i = 0 ; i < cdeg_out ; i++)
+		    {
+			row = Wm [i] ;
+			ASSERT (row >= 0 && row < n_row) ;
+			ASSERT (NON_PIVOTAL_ROW (row)) ;
+			if (Frpos [row] < 0 || Frpos [row] >= fnrows) nr_out++ ;
+#ifndef NDEBUG
+			/* we must see the pivot row somewhere */
+			if (row == pivrow [OUT][OUT])
+			{
+			    ASSERT (Frpos [row] < 0 || Frpos [row] >= fnrows) ;
+			    debug_ok = TRUE ;
+			}
+#endif
+		    }
+		    ASSERT (debug_ok) ;
+		}
+
+		/* count columns not in current front */
+		nc = 0 ;
+#ifndef NDEBUG
+		debug_ok = FALSE ;
+#endif
+		for (i = 0 ; i < rdeg [OUT][OUT] ; i++)
+		{
+		    col = Woo [i] ;
+		    ASSERT (col >= 0 && col < n_col && NON_PIVOTAL_COL (col)) ;
+		    if (Fcpos [col] < 0) nc++ ;
+#ifndef NDEBUG
+		    /* we must see the pivot column somewhere */
+		    if (col == pivcol [OUT])
+		    {
+			ASSERT (Fcpos [col] < 0) ;
+			debug_ok = TRUE ;
+		    }
+#endif
+		}
+		ASSERT (debug_ok) ;
+
+		extra_cols = (fncols + (nc-1)) - (rdeg [OUT][OUT] - 1) ;
+		extra_rows = (fnrows + (nr_out-1)) - (cdeg_out - 1) ;
+		ASSERT (extra_rows >= 0) ;
+		ASSERT (extra_cols >= 0) ;
+		extra_zeros = ((nc-1) * extra_rows) + ((nr_out-1) * extra_cols);
+
+		ASSERT (fnrows + nr_out == cdeg_out + extra_rows) ;
+		ASSERT (fncols + nc == rdeg [OUT][OUT] + extra_cols) ;
+
+		thiscost =
+		    /* new columns: */
+		    ((nc-1) * (cdeg_out-1)) +
+		    /* old columns in front grow by nr_out-1: */
+		    ((nr_out-1) * (fncols - extra_cols)) ;
+
+		/* size of relaxed front: */
+		fnrows_new [OUT][OUT] = fnrows + (nr_out-1) ;
+		fncols_new [OUT][OUT] = fncols + (nc-1) ;
+		relaxed_front = fnrows_new [OUT][OUT] * fncols_new [OUT][OUT] ;
+
+	    }
+
+	    /* do relaxed amalgamation if the extra zeros are no more */
+	    /* than a fraction (default 0.25) of the relaxed front */
+	    /* if relax = 0: no extra zeros allowed */
+	    /* if relax = +inf: always amalgamate */
+	    if (did_rowmerge)
+	    {
+		do_extend = FALSE ;
+	    }
+	    else
+	    {
+		/* relax parameter uses a double relop, but ignore NaN case: */
+		if (extra_zeros == 0)
+		{
+		    do_extend = TRUE ;
+		}
+		else
+		{
+		    do_extend = ((double) extra_zeros) <
+		       (relax1 * (double) relaxed_front) ;
+		}
+	    }
+
+	    if (do_extend)
+	    {
+		/* count the cost of relaxed amalgamation */
+		thiscost += extra_zeros ;
+
+		DEBUG2 (("Evaluating option OUT-OUT:\n")) ;
+		DEBUG2 (("Work->fnzeros "ID" fnpiv "ID" nr_out "ID" nc "ID"\n",
+		    Work->fnzeros, fnpiv, nr_out, nc)) ;
+		DEBUG2 (("fncols "ID" fnrows "ID"\n", fncols, fnrows)) ;
+
+		/* determine if BLAS-3 update to be applied before extending. */
+		/* update if too many zero entries accumulate in the LU block */
+		fnzeros = Work->fnzeros + fnpiv * (nr_out + nc) ;
+
+		DEBUG2 (("fnzeros "ID"\n", fnzeros)) ;
+
+		new_LUsize = (fnpiv+1) * (fnrows + nr_out + fncols + nc) ;
+
+		DEBUG2 (("new_LUsize "ID"\n", new_LUsize)) ;
+
+		/* RELAX3 parameter uses a double relop, ignore NaN case: */
+		do_update = fnpiv > 0 &&
+		    (((double) fnzeros) / ((double) new_LUsize)) > RELAX3 ;
+		DEBUG2 (("do_update "ID"\n", do_update))
+	    }
+	    else
+	    {
+		/* the current front would not be extended */
+		do_update = fnpiv > 0 ;
+		fnzeros = 0 ;
+		DEBUG2 (("OUT-OUT do_update forced true: "ID"\n", do_update)) ;
+
+		/* The new front would be just big enough to hold the new
+		 * pivot row and column. */
+		fnrows_new [OUT][OUT] = cdeg_out - 1 ;
+		fncols_new [OUT][OUT] = rdeg [OUT][OUT] - 1 ;
+	    }
+
+	    DEBUG2 (("option OUT OUT: nr "ID" nc "ID" cost "ID"\n",
+		rdeg [OUT][OUT], cdeg_out, thiscost)) ;
+
+	    if (bestcost == EMPTY || thiscost < bestcost)
+	    {
+		/* this is the best option seen so far */
+		Work->pivot_case = OUT_OUT ;
+		bestcost = thiscost ;
+		Work->do_extend = do_extend ;
+		Work->do_update = do_update ;
+		new_fnzeros = fnzeros ;
+	    }
+	}
+    }
+
+    /* At this point, a structural pivot has been found. */
+    /* It may be numerically zero, however. */
+    ASSERT (Work->pivot_case != EMPTY) ;
+    DEBUG2 (("local search, best option "ID", best cost "ID"\n",
+	Work->pivot_case, bestcost)) ;
+
+    /* ====================================================================== */
+    /* Pivot row and column, and extension, now determined */
+    /* ====================================================================== */
+
+    Work->fnzeros = new_fnzeros ;
+
+    /* ---------------------------------------------------------------------- */
+    /* finalize the pivot row and column */
+    /* ---------------------------------------------------------------------- */
+
+    switch (Work->pivot_case)
+    {
+	case IN_IN:
+	    DEBUG2 (("IN-IN option selected\n")) ;
+	    ASSERT (fnrows > 0 && fncols > 0) ;
+	    Work->pivcol_in_front = TRUE ;
+	    Work->pivrow_in_front = TRUE ;
+	    Work->pivcol = pivcol [IN] ;
+	    Work->pivrow = pivrow [IN][IN] ;
+	    Work->ccdeg = nr_in ;
+	    Work->Wrow = Fcols ;
+	    Work->rrdeg = rdeg [IN][IN] ;
+	    jj = jcand [IN] ;
+	    Work->fnrows_new = fnrows_new [IN][IN] ;
+	    Work->fncols_new = fncols_new [IN][IN] ;
+	    break ;
+
+	case IN_OUT:
+	    DEBUG2 (("IN-OUT option selected\n")) ;
+	    ASSERT (fnrows >= 0 && fncols > 0) ;
+	    Work->pivcol_in_front = TRUE ;
+	    Work->pivrow_in_front = FALSE ;
+	    Work->pivcol = pivcol [IN] ;
+	    Work->pivrow = pivrow [IN][OUT] ;
+	    Work->ccdeg = nr_in ;
+	    Work->Wrow = Wio ;
+	    Work->rrdeg = rdeg [IN][OUT] ;
+	    jj = jcand [IN] ;
+	    Work->fnrows_new = fnrows_new [IN][OUT] ;
+	    Work->fncols_new = fncols_new [IN][OUT] ;
+	    break ;
+
+	case OUT_IN:
+	    DEBUG2 (("OUT-IN option selected\n")) ;
+	    ASSERT (fnrows > 0 && fncols >= 0) ;
+	    Work->pivcol_in_front = FALSE ;
+	    Work->pivrow_in_front = TRUE ;
+	    Work->pivcol = pivcol [OUT] ;
+	    Work->pivrow = pivrow [OUT][IN] ;
+	    Work->ccdeg = cdeg_out ;
+	    /* Wrow might be equivalenced to Fcols (Freebie in): */
+	    Work->Wrow = Woi ;
+	    Work->rrdeg = rdeg [OUT][IN] ;
+	    /* Work->Wrow[0..fncols-1] is not there.  See Fcols instead */
+	    jj = jcand [OUT] ;
+	    Work->fnrows_new = fnrows_new [OUT][IN] ;
+	    Work->fncols_new = fncols_new [OUT][IN] ;
+	    break ;
+
+	case OUT_OUT:
+	    DEBUG2 (("OUT-OUT option selected\n")) ;
+	    ASSERT (fnrows >= 0 && fncols >= 0) ;
+	    Work->pivcol_in_front = FALSE ;
+	    Work->pivrow_in_front = FALSE ;
+	    Work->pivcol = pivcol [OUT] ;
+	    Work->pivrow = pivrow [OUT][OUT] ;
+	    Work->ccdeg = cdeg_out ;
+	    /* Wrow might be equivalenced to Wio (Freebie out): */
+	    Work->Wrow = Woo ;
+	    Work->rrdeg = rdeg [OUT][OUT] ;
+	    jj = jcand [OUT] ;
+	    Work->fnrows_new = fnrows_new [OUT][OUT] ;
+	    Work->fncols_new = fncols_new [OUT][OUT] ;
+	    break ;
+
+    }
+
+    ASSERT (IMPLIES (fnrows == 0 && fncols == 0, Work->pivot_case == OUT_OUT)) ;
+
+    if (!Work->pivcol_in_front && pivcol [IN] != EMPTY)
+    {
+	/* clear Frpos if pivcol [IN] was searched, but not selected */
+	for (i = fnrows ; i < cdeg_in ; i++)
+	{
+	    Frpos [Frows [i]] = EMPTY;
+	}
+    }
+
+    /* ---------------------------------------------------------------------- */
+    /* Pivot row and column have been found */
+    /* ---------------------------------------------------------------------- */
+
+    /* ---------------------------------------------------------------------- */
+    /* remove pivot column from candidate pivot column set */
+    /* ---------------------------------------------------------------------- */
+
+    ASSERT (jj >= 0 && jj < Work->nCandidates) ;
+    ASSERT (Work->pivcol == Work->Candidates [jj]) ;
+    remove_candidate (jj, Work, Symbolic) ;
+
+    /* ---------------------------------------------------------------------- */
+    /* check for frontal matrix growth */
+    /* ---------------------------------------------------------------------- */
+
+    DEBUG1 (("Check frontal growth:\n")) ;
+    DEBUG1 (("fnrows_new "ID" + 1 = "ID",  fnr_curr "ID"\n",
+	    Work->fnrows_new, Work->fnrows_new + 1, fnr_curr)) ;
+    DEBUG1 (("fncols_new "ID" + 1 = "ID",  fnc_curr "ID"\n",
+	    Work->fncols_new, Work->fncols_new + 1, fnc_curr)) ;
+
+    Work->do_grow = (Work->fnrows_new + 1 > fnr_curr
+		  || Work->fncols_new + 1 > fnc_curr) ;
+    if (Work->do_grow)
+    {
+	DEBUG0 (("\nNeed to grow frontal matrix, force do_update true\n")) ;
+	/* If the front must grow, then apply the pending updates and remove
+	 * the current pivot rows/columns from the front prior to growing the
+	 * front.  This frees up as much space as possible for the new front. */
+	if (!Work->do_update && fnpiv > 0)
+	{
+	    /* This update would not have to be done if the current front
+	     * was big enough. */
+	    Work->nforced++ ;
+	    Work->do_update = TRUE ;
+	}
+    }
+
+    /* ---------------------------------------------------------------------- */
+    /* current pivot column */
+    /* ---------------------------------------------------------------------- */
+
+    /*
+	c1) If pivot column index is in the current front:
+
+	    The pivot column pattern is in Frows [0 .. fnrows-1] and
+	    the extension is in Frows [fnrows ... fnrows+ccdeg-1].
+
+	    Frpos [Frows [0 .. fnrows+ccdeg-1]] is
+	    equal to 0 .. fnrows+ccdeg-1.  Wm is not needed.
+
+	    The values are in Wy [0 .. fnrows+ccdeg-1].
+
+	c2) Otherwise, if the pivot column index is not in the current front:
+
+	    c2a) If the front is being extended, old row indices in the the
+		pivot column pattern are in Frows [0 .. fnrows-1].
+
+		All entries are in Wm [0 ... ccdeg-1], with values in
+		Wx [0 .. ccdeg-1].  These may include entries already in
+		Frows [0 .. fnrows-1].
+
+		Frpos [Frows [0 .. fnrows-1]] is equal to 0 .. fnrows-1.
+		Frpos [Wm [0 .. ccdeg-1]] for new entries is < 0.
+
+	    c2b) If the front is not being extended, then the entire pivot
+		column pattern is in Wm [0 .. ccdeg-1].  It includes
+		the pivot row index.  It is does not contain the pattern
+		Frows [0..fnrows-1].  The intersection of these two
+		sets may or may not be empty.  The values are in Wx [0..ccdeg-1]
+
+	In both cases c1 and c2, Frpos [Frows [0 .. fnrows-1]] is equal
+	to 0 .. fnrows-1, which is the pattern of the current front.
+	Any entry of Frpos that is not specified above is < 0.
+    */
+
+
+#ifndef NDEBUG
+    DEBUG2 (("\n\nSEARCH DONE: Pivot col "ID" in: ("ID") pivot row "ID" in: ("ID
+	") extend: "ID"\n\n", Work->pivcol, Work->pivcol_in_front,
+	Work->pivrow, Work->pivrow_in_front, Work->do_extend)) ;
+    UMF_dump_rowcol (1, Numeric, Work, Work->pivcol, !Symbolic->fixQ) ;
+    DEBUG2 (("Pivot col "ID": fnrows "ID" ccdeg "ID"\n", Work->pivcol, fnrows,
+	Work->ccdeg)) ;
+    if (Work->pivcol_in_front)	/* case c1 */
+    {
+	Int found = FALSE ;
+	DEBUG3 (("Pivcol in front\n")) ;
+	for (i = 0 ; i < fnrows ; i++)
+	{
+	    row = Frows [i] ;
+	    DEBUG3 ((ID":   row:: "ID" in front ", i, row)) ;
+	    ASSERT (row >= 0 && row < n_row && NON_PIVOTAL_ROW (row)) ;
+	    ASSERT (Frpos [row] == i) ;
+	    EDEBUG3 (Wy [i]) ;
+	    if (row == Work->pivrow)
+	    {
+		DEBUG3 ((" <- pivrow")) ;
+		found = TRUE ;
+	    }
+	    DEBUG3 (("\n")) ;
+	}
+	ASSERT (found == Work->pivrow_in_front) ;
+	found = FALSE ;
+	for (i = fnrows ; i < fnrows + Work->ccdeg ; i++)
+	{
+	    row = Frows [i] ;
+	    DEBUG3 ((ID":   row:: "ID" (new)", i, row)) ;
+	    ASSERT (row >= 0 && row < n_row && NON_PIVOTAL_ROW (row)) ;
+	    ASSERT (Frpos [row] == i) ;
+	    EDEBUG3 (Wy [i]) ;
+	    if (row == Work->pivrow)
+	    {
+		DEBUG3 ((" <- pivrow")) ;
+		found = TRUE ;
+	    }
+	    DEBUG3 (("\n")) ;
+	}
+	ASSERT (found == !Work->pivrow_in_front) ;
+    }
+    else
+    {
+	if (Work->do_extend)
+	{
+	    Int found = FALSE ;
+	    DEBUG3 (("Pivcol not in front (extend)\n")) ;
+	    for (i = 0 ; i < fnrows ; i++)
+	    {
+		row = Frows [i] ;
+		DEBUG3 ((ID":   row:: "ID" in front ", i, row)) ;
+		ASSERT (row >= 0 && row < n_row && NON_PIVOTAL_ROW (row)) ;
+		ASSERT (Frpos [row] == i) ;
+		if (row == Work->pivrow)
+		{
+		    DEBUG3 ((" <- pivrow")) ;
+		    found = TRUE ;
+		}
+		DEBUG3 (("\n")) ;
+	    }
+	    ASSERT (found == Work->pivrow_in_front) ;
+	    found = FALSE ;
+	    DEBUG3 (("----\n")) ;
+	    for (i = 0 ; i < Work->ccdeg ; i++)
+	    {
+		row = Wm [i] ;
+		ASSERT (row >= 0 && row < n_row && NON_PIVOTAL_ROW (row)) ;
+		DEBUG3 ((ID":   row:: "ID" ", i, row)) ;
+		EDEBUG3 (Wx [i]) ;
+		if (Frpos [row] < 0)
+		{
+		    DEBUG3 ((" (new) ")) ;
+		}
+		if (row == Work->pivrow)
+		{
+		    DEBUG3 ((" <- pivrow")) ;
+		    found = TRUE ;
+		    /* ... */
+		    if (Work->pivrow_in_front) ASSERT (Frpos [row] >= 0) ;
+		    else ASSERT (Frpos [row] < 0) ;
+		}
+		DEBUG3 (("\n")) ;
+	    }
+	    ASSERT (found) ;
+	}
+	else
+	{
+	    Int found = FALSE ;
+	    DEBUG3 (("Pivcol not in front (no extend)\n")) ;
+	    for (i = 0 ; i < Work->ccdeg ; i++)
+	    {
+		row = Wm [i] ;
+		ASSERT (row >= 0 && row < n_row && NON_PIVOTAL_ROW (row)) ;
+		DEBUG3 ((ID":   row:: "ID" ", i, row)) ;
+		EDEBUG3 (Wx [i]) ;
+		DEBUG3 ((" (new) ")) ;
+		if (row == Work->pivrow)
+		{
+		    DEBUG3 ((" <- pivrow")) ;
+		    found = TRUE ;
+		}
+		DEBUG3 (("\n")) ;
+	    }
+	    ASSERT (found) ;
+	}
+    }
+#endif
+
+    /* ---------------------------------------------------------------------- */
+    /* current pivot row */
+    /* ---------------------------------------------------------------------- */
+
+    /*
+	r1) If the pivot row index is in the current front:
+
+	    The pivot row pattern is in Fcols [0..fncols-1] and the extenson is
+	    in Wrow [fncols .. rrdeg-1].  If the pivot column is in the current
+	    front, then Fcols and Wrow are equivalenced.
+
+	r2)  If the pivot row index is not in the current front:
+
+	    r2a) If the front is being extended, the pivot row pattern is in
+		Fcols [0 .. fncols-1].  New entries are in Wrow [0 .. rrdeg-1],
+		but these may include entries already in Fcols [0 .. fncols-1].
+
+	    r2b) Otherwise, the pivot row pattern is Wrow [0 .. rrdeg-1].
+
+	Fcpos [Fcols [0..fncols-1]] is (0..fncols-1) * fnr_curr.
+	All other entries in Fcpos are < 0.
+
+	These conditions are asserted below.
+
+	------------------------------------------------------------------------
+	Other items in Work structure that are relevant:
+
+	pivcol: the pivot column index
+	pivrow: the pivot column index
+
+	rrdeg:
+	ccdeg:
+
+	fnrows: the number of rows in the currnt contribution block
+	fncols: the number of columns in the current contribution block
+
+	fnrows_new: the number of rows in the new contribution block
+	fncols_new: the number of rows in the new contribution block
+
+	------------------------------------------------------------------------
+    */
+
+
+#ifndef NDEBUG
+    UMF_dump_rowcol (0, Numeric, Work, Work->pivrow, TRUE) ;
+    DEBUG2 (("Pivot row "ID":\n", Work->pivrow)) ;
+    if (Work->pivrow_in_front)
+    {
+	Int found = FALSE ;
+	for (i = 0 ; i < fncols ; i++)
+	{
+	    col = Fcols [i] ;
+	    DEBUG3 (("   col:: "ID" in front\n", col)) ;
+	    ASSERT (col >= 0 && col < n_col && NON_PIVOTAL_COL (col)) ;
+	    ASSERT (Fcpos [col] == i * fnr_curr) ;
+	    if (col == Work->pivcol) found = TRUE ;
+	}
+	ASSERT (found == Work->pivcol_in_front) ;
+	found = FALSE ;
+	ASSERT (IMPLIES (Work->pivcol_in_front, Fcols == Work->Wrow)) ;
+	for (i = fncols ; i < Work->rrdeg ; i++)
+	{
+	    col = Work->Wrow [i] ;
+	    ASSERT (col >= 0 && col < n_col && NON_PIVOTAL_COL (col)) ;
+	    ASSERT (Fcpos [col] < 0) ;
+	    if (col == Work->pivcol) found = TRUE ;
+	    else DEBUG3 (("   col:: "ID" (new)\n", col)) ;
+	}
+	ASSERT (found == !Work->pivcol_in_front) ;
+    }
+    else
+    {
+	if (Work->do_extend)
+	{
+	    Int found = FALSE ;
+	    for (i = 0 ; i < fncols ; i++)
+	    {
+		col = Fcols [i] ;
+		DEBUG3 (("   col:: "ID" in front\n", col)) ;
+		ASSERT (col >= 0 && col < n_col && NON_PIVOTAL_COL (col)) ;
+		ASSERT (Fcpos [col] == i * fnr_curr) ;
+		if (col == Work->pivcol) found = TRUE ;
+	    }
+	    ASSERT (found == Work->pivcol_in_front) ;
+	    found = FALSE ;
+	    for (i = 0 ; i < Work->rrdeg ; i++)
+	    {
+		col = Work->Wrow [i] ;
+		ASSERT (col >= 0 && col < n_col && NON_PIVOTAL_COL (col)) ;
+		if (Fcpos [col] >= 0) continue ;
+		if (col == Work->pivcol) found = TRUE ;
+		else DEBUG3 (("   col:: "ID" (new, extend)\n", col)) ;
+	    }
+	    ASSERT (found == !Work->pivcol_in_front) ;
+	}
+	else
+	{
+	    Int found = FALSE ;
+	    for (i = 0 ; i < Work->rrdeg ; i++)
+	    {
+		col = Work->Wrow [i] ;
+		ASSERT (col >= 0 && col < n_col && NON_PIVOTAL_COL (col)) ;
+		if (col == Work->pivcol) found = TRUE ;
+		else DEBUG3 (("   col:: "ID" (all new)\n", col)) ;
+	    }
+	    ASSERT (found) ;
+	}
+    }
+#endif
+
+    /* ---------------------------------------------------------------------- */
+    /* determine whether to do scan2-row and scan2-col */
+    /* ---------------------------------------------------------------------- */
+
+    if (Work->do_extend)
+    {
+	Work->do_scan2row = (fncols > 0) ;
+	Work->do_scan2col = (fnrows > 0) ;
+    }
+    else
+    {
+	Work->do_scan2row = (fncols > 0) && Work->pivrow_in_front ;
+	Work->do_scan2col = (fnrows > 0) && Work->pivcol_in_front ;
+    }
+
+    /* ---------------------------------------------------------------------- */
+
+    DEBUG2 (("LOCAL SEARCH DONE: pivot column "ID" pivot row: "ID"\n",
+	Work->pivcol, Work->pivrow)) ;
+    DEBUG2 (("do_extend: "ID"\n", Work->do_extend)) ;
+    DEBUG2 (("do_update: "ID"\n", Work->do_update)) ;
+    DEBUG2 (("do_grow:   "ID"\n", Work->do_grow)) ;
+
+    /* ---------------------------------------------------------------------- */
+    /* keep track of the diagonal */
+    /* ---------------------------------------------------------------------- */
+
+    if (Symbolic->prefer_diagonal
+	&& Work->pivcol < Work->n_col - Symbolic->nempty_col)
+    {
+	Diagonal_map = Work->Diagonal_map ;
+	Diagonal_imap = Work->Diagonal_imap ;
+	ASSERT (Diagonal_map != (Int *) NULL) ;
+	ASSERT (Diagonal_imap != (Int *) NULL) ;
+
+	row2 = Diagonal_map  [Work->pivcol] ;
+	col2 = Diagonal_imap [Work->pivrow] ;
+
+	if (row2 < 0)
+	{
+	    /* this was an off-diagonal pivot row */
+	    Work->noff_diagonal++ ;
+	    row2 = UNFLIP (row2) ;
+	}
+
+	ASSERT (Diagonal_imap [row2] == Work->pivcol) ;
+	ASSERT (UNFLIP (Diagonal_map [col2]) == Work->pivrow) ;
+
+	if (row2 != Work->pivrow)
+	{
+	    /* swap the diagonal map to attempt to maintain symmetry later on.
+	     * Also mark the map for col2 (via FLIP) to denote that the entry
+	     * now on the diagonal is not the original entry on the diagonal. */
+
+	    DEBUG0 (("Unsymmetric pivot\n")) ;
+	    Diagonal_map  [Work->pivcol] = FLIP (Work->pivrow) ;
+	    Diagonal_imap [Work->pivrow] = Work->pivcol ;
+
+	    Diagonal_map  [col2] = FLIP (row2) ;
+	    Diagonal_imap [row2] = col2 ;
+
+	}
+	ASSERT (n_row == n_col) ;
+#ifndef NDEBUG
+	UMF_dump_diagonal_map (Diagonal_map, Diagonal_imap, Symbolic->n1,
+	    Symbolic->n_col, Symbolic->nempty_col) ;
+#endif
+    }
+
+    return (UMFPACK_OK) ;
+}