1 /* ========================================================================== */
2 /* === UMF_row_search ======================================================= */
3 /* ========================================================================== */
4 
5 /* -------------------------------------------------------------------------- */
6 /* UMFPACK Copyright (c) Timothy A. Davis, CISE,                              */
7 /* Univ. of Florida.  All Rights Reserved.  See ../Doc/License for License.   */
8 /* web: http://www.cise.ufl.edu/research/sparse/umfpack                       */
9 /* -------------------------------------------------------------------------- */
10 
11 /*
12     Find two candidate pivot rows in a column: the best one in the front,
13     and the best one not in the front.  Return the two pivot row patterns and
14     their exact degrees.  Called by UMF_local_search.
15 
16     Returns UMFPACK_OK if successful, or UMFPACK_WARNING_singular_matrix or
17     UMFPACK_ERROR_different_pattern if not.
18 
19 */
20 
21 #include "umf_internal.h"
22 #include "umf_row_search.h"
23 
UMF_row_search(NumericType * Numeric,WorkType * Work,SymbolicType * Symbolic,Int cdeg0,Int cdeg1,const Int Pattern[],const Int Pos[],Int pivrow[2],Int rdeg[2],Int W_i[],Int W_o[],Int prior_pivrow[2],const Entry Wxy[],Int pivcol,Int freebie[])24 GLOBAL Int UMF_row_search
25 (
26     NumericType *Numeric,
27     WorkType *Work,
28     SymbolicType *Symbolic,
29     Int cdeg0,			/* length of column in Front */
30     Int cdeg1,			/* length of column outside Front */
31     const Int Pattern [ ],	/* pattern of column, Pattern [0..cdeg1 -1] */
32     const Int Pos [ ],		/* Pos [Pattern [0..cdeg1 -1]] = 0..cdeg1 -1 */
33     Int pivrow [2],		/* pivrow [IN] and pivrow [OUT] */
34     Int rdeg [2],		/* rdeg [IN] and rdeg [OUT] */
35     Int W_i [ ],		/* pattern of pivrow [IN], */
36 				/* either Fcols or Woi */
37     Int W_o [ ],		/* pattern of pivrow [OUT], */
38 				/* either Wio or Woo */
39     Int prior_pivrow [2],	/* the two other rows just scanned, if any */
40     const Entry Wxy [ ],	/* numerical values Wxy [0..cdeg1-1],
41 				   either Wx or Wy */
42 
43     Int pivcol,			/* the candidate column being searched */
44     Int freebie [ ]
45 )
46 {
47 
48     /* ---------------------------------------------------------------------- */
49     /* local variables */
50     /* ---------------------------------------------------------------------- */
51 
52     double maxval, toler, toler2, value, pivot [2] ;
53     Int i, row, deg, col, *Frpos, fnrows, *E, j, ncols, *Cols, *Rows,
54 	e, f, Wrpflag, *Fcpos, fncols, tpi, max_rdeg, nans_in_col, was_offdiag,
55 	diag_row, prefer_diagonal, *Wrp, found, *Diagonal_map ;
56     Tuple *tp, *tpend, *tp1, *tp2 ;
57     Unit *Memory, *p ;
58     Element *ep ;
59     Int *Row_tuples, *Row_degree, *Row_tlen ;
60 
61 #ifndef NDEBUG
62     Int *Col_degree ;
63     DEBUG2 (("Row_search:\n")) ;
64     for (i = 0 ; i < cdeg1 ; i++)
65     {
66 	row = Pattern [i] ;
67 	DEBUG4 (("   row: "ID"\n", row)) ;
68 	ASSERT (row >= 0 && row < Numeric->n_row) ;
69 	ASSERT (i == Pos [row]) ;
70     }
71     /* If row is not in Pattern [0..cdeg1-1], then Pos [row] == EMPTY */
72     if (UMF_debug > 0 || Numeric->n_row < 1000)
73     {
74 	Int cnt = cdeg1 ;
75 	DEBUG4 (("Scan all rows:\n")) ;
76 	for (row = 0 ; row < Numeric->n_row ; row++)
77 	{
78 	    if (Pos [row] < 0)
79 	    {
80 		cnt++ ;
81 	    }
82 	    else
83 	    {
84 		DEBUG4 (("   row: "ID" pos "ID"\n", row, Pos [row])) ;
85 	    }
86 	}
87 	ASSERT (cnt == Numeric->n_row) ;
88     }
89     Col_degree = Numeric->Cperm ;   /* for NON_PIVOTAL_COL macro only */
90     ASSERT (pivcol >= 0 && pivcol < Work->n_col) ;
91     ASSERT (NON_PIVOTAL_COL (pivcol)) ;
92 #endif
93 
94     pivot [IN] = 0. ;
95     pivot [OUT] = 0. ;
96 
97     /* ---------------------------------------------------------------------- */
98     /* get parameters */
99     /* ---------------------------------------------------------------------- */
100 
101     Row_degree = Numeric->Rperm ;
102     Row_tuples = Numeric->Uip ;
103     Row_tlen   = Numeric->Uilen ;
104     Wrp = Work->Wrp ;
105     Frpos = Work->Frpos ;
106     E = Work->E ;
107     Memory = Numeric->Memory ;
108     fnrows = Work->fnrows ;
109 
110     prefer_diagonal = Symbolic->prefer_diagonal ;
111     Diagonal_map = Work->Diagonal_map ;
112 
113     if (Diagonal_map)
114     {
115 	diag_row = Diagonal_map [pivcol] ;
116 	was_offdiag = diag_row < 0 ;
117 	if (was_offdiag)
118 	{
119 	    /* the "diagonal" entry in this column was permuted here by an
120 	     * earlier pivot choice.  The tighter off-diagonal tolerance will
121 	     * be used instead of the symmetric tolerance. */
122 	    diag_row = FLIP (diag_row) ;
123 	}
124 	ASSERT (diag_row >= 0 && diag_row < Numeric->n_row) ;
125     }
126     else
127     {
128 	diag_row = EMPTY ;	/* unused */
129 	was_offdiag = EMPTY ;	/* unused */
130     }
131 
132     /* pivot row degree cannot exceed max_rdeg */
133     max_rdeg = Work->fncols_max ;
134 
135     /* ---------------------------------------------------------------------- */
136     /* scan pivot column for candidate rows */
137     /* ---------------------------------------------------------------------- */
138 
139     maxval = 0.0 ;
140     nans_in_col = FALSE ;
141 
142     for (i = 0 ; i < cdeg1 ; i++)
143     {
144 	APPROX_ABS (value, Wxy [i]) ;
145 	if (SCALAR_IS_NAN (value))
146 	{
147 	    nans_in_col = TRUE ;
148 	    maxval = value ;
149 	    break ;
150 	}
151 	/* This test can now ignore the NaN case: */
152 	maxval = MAX (maxval, value) ;
153     }
154 
155     /* if maxval is zero, the matrix is numerically singular */
156 
157     toler = Numeric->relpt * maxval ;
158     toler2 = Numeric->relpt2 * maxval ;
159     toler2 = was_offdiag ? toler : toler2 ;
160 
161     DEBUG5 (("Row_search begins [ maxval %g toler %g %g\n",
162 	maxval, toler, toler2)) ;
163     if (SCALAR_IS_NAN (toler) || SCALAR_IS_NAN (toler2))
164     {
165 	nans_in_col = TRUE ;
166     }
167 
168     if (!nans_in_col)
169     {
170 
171 	/* look for the diagonal entry, if it exists */
172 	found = FALSE ;
173 	ASSERT (!SCALAR_IS_NAN (toler)) ;
174 
175 	if (prefer_diagonal)
176 	{
177 	    ASSERT (diag_row != EMPTY) ;
178 	    i = Pos [diag_row] ;
179 	    if (i >= 0)
180 	    {
181 		double a ;
182 		ASSERT (i < cdeg1) ;
183 		ASSERT (diag_row == Pattern [i]) ;
184 
185 		APPROX_ABS (a, Wxy [i]) ;
186 
187 		ASSERT (!SCALAR_IS_NAN (a)) ;
188 		ASSERT (!SCALAR_IS_NAN (toler2)) ;
189 
190 		if (SCALAR_IS_NONZERO (a) && a >= toler2)
191 		{
192 		    /* found it! */
193 		    DEBUG3 (("Symmetric pivot: "ID" "ID"\n", pivcol, diag_row));
194 		    found = TRUE ;
195 		    if (Frpos [diag_row] >= 0 && Frpos [diag_row] < fnrows)
196 		    {
197 			pivrow [IN] = diag_row ;
198 			pivrow [OUT] = EMPTY ;
199 		    }
200 		    else
201 		    {
202 			pivrow [IN] = EMPTY ;
203 			pivrow [OUT] = diag_row ;
204 		    }
205 		}
206 	    }
207 	}
208 
209 	/* either no diagonal found, or we didn't look for it */
210 	if (!found)
211 	{
212 	    if (cdeg0 > 0)
213 	    {
214 
215 		/* this is a column in the front */
216 		for (i = 0 ; i < cdeg0 ; i++)
217 		{
218 		    double a ;
219 		    APPROX_ABS (a, Wxy [i]) ;
220 		    ASSERT (!SCALAR_IS_NAN (a)) ;
221 		    ASSERT (!SCALAR_IS_NAN (toler)) ;
222 		    if (SCALAR_IS_NONZERO (a) && a >= toler)
223 		    {
224 			row = Pattern [i] ;
225 			deg = Row_degree [row] ;
226 #ifndef NDEBUG
227 			DEBUG6 ((ID" candidate row "ID" deg "ID" absval %g\n",
228 			    i, row, deg, a)) ;
229 			UMF_dump_rowcol (0, Numeric, Work, row, TRUE) ;
230 #endif
231 			ASSERT (Frpos [row] >= 0 && Frpos [row] < fnrows) ;
232 			ASSERT (Frpos [row] == i) ;
233 			/* row is in the current front */
234 			DEBUG4 ((" in front\n")) ;
235 			if (deg < rdeg [IN]
236 			    /* break ties by picking the largest entry: */
237 			       || (deg == rdeg [IN] && a > pivot [IN])
238 			    /* break ties by picking the diagonal entry: */
239 			    /* || (deg == rdeg [IN] && row == diag_row) */
240 			   )
241 			{
242 			    /* best row in front, so far */
243 			    pivrow [IN] = row ;
244 			    rdeg [IN] = deg ;
245 			    pivot [IN] = a ;
246 			}
247 		    }
248 		}
249 		for ( ; i < cdeg1 ; i++)
250 		{
251 		    double a ;
252 		    APPROX_ABS (a, Wxy [i]) ;
253 		    ASSERT (!SCALAR_IS_NAN (a)) ;
254 		    ASSERT (!SCALAR_IS_NAN (toler)) ;
255 		    if (SCALAR_IS_NONZERO (a) && a >= toler)
256 		    {
257 			row = Pattern [i] ;
258 			deg = Row_degree [row] ;
259 #ifndef NDEBUG
260 			DEBUG6 ((ID" candidate row "ID" deg "ID" absval %g\n",
261 			    i, row, deg, a)) ;
262 			UMF_dump_rowcol (0, Numeric, Work, row, TRUE) ;
263 #endif
264 			ASSERT (Frpos [row] == i) ;
265 			/* row is not in the current front */
266 			DEBUG4 ((" NOT in front\n")) ;
267 			if (deg < rdeg [OUT]
268 			    /* break ties by picking the largest entry: */
269 			       || (deg == rdeg [OUT] && a > pivot [OUT])
270 			    /* break ties by picking the diagonal entry: */
271 			    /* || (deg == rdeg [OUT] && row == diag_row) */
272 			   )
273 			{
274 			    /* best row not in front, so far */
275 			    pivrow [OUT] = row ;
276 			    rdeg [OUT] = deg ;
277 			    pivot [OUT] = a ;
278 			}
279 		    }
280 		}
281 
282 	    }
283 	    else
284 	    {
285 
286 		/* this column is not in the front */
287 		for (i = 0 ; i < cdeg1 ; i++)
288 		{
289 		    double a ;
290 		    APPROX_ABS (a, Wxy [i]) ;
291 		    ASSERT (!SCALAR_IS_NAN (a)) ;
292 		    ASSERT (!SCALAR_IS_NAN (toler)) ;
293 		    if (SCALAR_IS_NONZERO (a) && a >= toler)
294 		    {
295 			row = Pattern [i] ;
296 			deg = Row_degree [row] ;
297 #ifndef NDEBUG
298 			DEBUG6 ((ID" candidate row "ID" deg "ID" absval %g\n",
299 			    i, row, deg, a)) ;
300 			UMF_dump_rowcol (0, Numeric, Work, row, TRUE) ;
301 #endif
302 			if (Frpos [row] >= 0 && Frpos [row] < fnrows)
303 			{
304 			    /* row is in the current front */
305 			    DEBUG4 ((" in front\n")) ;
306 			    if (deg < rdeg [IN]
307 			    /* break ties by picking the largest entry: */
308 			       || (deg == rdeg [IN] && a > pivot [IN])
309 			    /* break ties by picking the diagonal entry: */
310 			    /* || (deg == rdeg [IN] && row == diag_row) */
311 			       )
312 			    {
313 				/* best row in front, so far */
314 				pivrow [IN] = row ;
315 				rdeg [IN] = deg ;
316 				pivot [IN] = a ;
317 			    }
318 			}
319 			else
320 			{
321 			    /* row is not in the current front */
322 			    DEBUG4 ((" NOT in front\n")) ;
323 			    if (deg < rdeg [OUT]
324 			    /* break ties by picking the largest entry: */
325 			       || (deg == rdeg[OUT] && a > pivot [OUT])
326 			    /* break ties by picking the diagonal entry: */
327 			    /* || (deg == rdeg[OUT] && row == diag_row) */
328 			       )
329 			    {
330 				/* best row not in front, so far */
331 				pivrow [OUT] = row ;
332 				rdeg [OUT] = deg ;
333 				pivot [OUT] = a ;
334 			    }
335 			}
336 		    }
337 		}
338 	    }
339 	}
340     }
341 
342     /* ---------------------------------------------------------------------- */
343     /* NaN handling */
344     /* ---------------------------------------------------------------------- */
345 
346     /* if cdeg1 > 0 then we must have found a pivot row ... unless NaN's */
347     /* exist.  Try with no numerical tests if no pivot found. */
348 
349     if (cdeg1 > 0 && pivrow [IN] == EMPTY && pivrow [OUT] == EMPTY)
350     {
351 	/* cleanup for the NaN case */
352 	DEBUG0 (("Found a NaN in pivot column!\n")) ;
353 
354 	/* grab the first entry in the pivot column, ignoring degree, */
355 	/* numerical stability, and symmetric preference */
356 	row = Pattern [0] ;
357 	deg = Row_degree [row] ;
358 	if (Frpos [row] >= 0 && Frpos [row] < fnrows)
359 	{
360 	    /* row is in the current front */
361 	    DEBUG4 ((" in front\n")) ;
362 	    pivrow [IN] = row ;
363 	    rdeg [IN] = deg ;
364 	}
365 	else
366 	{
367 	    /* row is not in the current front */
368 	    DEBUG4 ((" NOT in front\n")) ;
369 	    pivrow [OUT] = row ;
370 	    rdeg [OUT] = deg ;
371 	}
372 
373 	/* We are now guaranteed to have a pivot, no matter how broken */
374 	/* (non-IEEE compliant) the underlying numerical operators are. */
375 	/* This is particularly a problem for Microsoft compilers (they do */
376 	/* not handle NaN's properly). Now try to find a sparser pivot, if */
377 	/* possible. */
378 
379 	for (i = 1 ; i < cdeg1 ; i++)
380 	{
381 	    row = Pattern [i] ;
382 	    deg = Row_degree [row] ;
383 
384 	    if (Frpos [row] >= 0 && Frpos [row] < fnrows)
385 	    {
386 		/* row is in the current front */
387 		DEBUG4 ((" in front\n")) ;
388 		if (deg < rdeg [IN] || (deg == rdeg [IN] && row == diag_row))
389 		{
390 		    /* best row in front, so far */
391 		    pivrow [IN] = row ;
392 		    rdeg [IN] = deg ;
393 		}
394 	    }
395 	    else
396 	    {
397 		/* row is not in the current front */
398 		DEBUG4 ((" NOT in front\n")) ;
399 		if (deg < rdeg [OUT] || (deg == rdeg [OUT] && row == diag_row))
400 		{
401 		    /* best row not in front, so far */
402 		    pivrow [OUT] = row ;
403 		    rdeg [OUT] = deg ;
404 		}
405 	    }
406 	}
407     }
408 
409     /* We found a pivot if there are entries (even zero ones) in pivot col */
410     ASSERT (IMPLIES (cdeg1 > 0, pivrow[IN] != EMPTY || pivrow[OUT] != EMPTY)) ;
411 
412     /* If there are no entries in the pivot column, then no pivot is found */
413     ASSERT (IMPLIES (cdeg1 == 0, pivrow[IN] == EMPTY && pivrow[OUT] == EMPTY)) ;
414 
415     /* ---------------------------------------------------------------------- */
416     /* check for singular matrix */
417     /* ---------------------------------------------------------------------- */
418 
419     if (cdeg1  == 0)
420     {
421 	if (fnrows > 0)
422 	{
423 	    /*
424 		Get the pivrow [OUT][IN] from the current front.
425 		The frontal matrix looks like this:
426 
427 			pivcol[OUT]
428 			|
429 			v
430 		x x x x 0   <- so grab this row as the pivrow [OUT][IN].
431 		x x x x 0
432 		x x x x 0
433 		0 0 0 0 0
434 
435 		The current frontal matrix has some rows in it.  The degree
436 		of the pivcol[OUT] is zero.  The column is empty, and the
437 		current front does not contribute to it.
438 
439 	    */
440 	    pivrow [IN] = Work->Frows [0] ;
441 	    DEBUGm4 (("Got zero pivrow[OUT][IN] "ID" from current front\n",
442 		pivrow [IN])) ;
443 	}
444 	else
445 	{
446 
447 	    /*
448 		Get a pivot row from the row-merge tree, use as
449 		pivrow [OUT][OUT].   pivrow [IN] remains EMPTY.
450 		This can only happen if the current front is 0-by-0.
451 	    */
452 
453 	    Int *Front_leftmostdesc, *Front_1strow, *Front_new1strow, row1,
454 		row2, fleftmost, nfr, n_row, frontid ;
455 
456 	    ASSERT (Work->fncols == 0) ;
457 
458 	    Front_leftmostdesc = Symbolic->Front_leftmostdesc ;
459 	    Front_1strow = Symbolic->Front_1strow ;
460 	    Front_new1strow = Work->Front_new1strow ;
461 	    nfr = Symbolic->nfr ;
462 	    n_row = Numeric->n_row ;
463 	    frontid = Work->frontid ;
464 
465 	    DEBUGm4 (("Note: pivcol: "ID" is empty front "ID"\n",
466 		pivcol, frontid)) ;
467 #ifndef NDEBUG
468 	    DEBUG1 (("Calling dump rowmerge\n")) ;
469 	    UMF_dump_rowmerge (Numeric, Symbolic, Work) ;
470 #endif
471 
472 	    /* Row-merge set is the non-pivotal rows in the range */
473 	    /* Front_new1strow [Front_leftmostdesc [frontid]] to */
474 	    /* Front_1strow [frontid+1] - 1. */
475 	    /* If this is empty, then use the empty rows, in the range */
476 	    /* Front_new1strow [nfr] to n_row-1. */
477 	    /* If this too is empty, then pivrow [OUT] will be empty. */
478 	    /* In both cases, update Front_new1strow [...]. */
479 
480 	    fleftmost = Front_leftmostdesc [frontid] ;
481 	    row1 = Front_new1strow [fleftmost] ;
482 	    row2 = Front_1strow [frontid+1] - 1 ;
483 	    DEBUG1 (("Leftmost: "ID" Rows ["ID" to "ID"] srch ["ID" to "ID"]\n",
484 		fleftmost, Front_1strow [frontid], row2, row1, row2)) ;
485 
486 	    /* look in the range row1 ... row2 */
487 	    for (row = row1 ; row <= row2 ; row++)
488 	    {
489 		DEBUG3 (("   Row: "ID"\n", row)) ;
490 		if (NON_PIVOTAL_ROW (row))
491 		{
492 		    /* found it */
493 		    DEBUG3 (("   Row: "ID" found\n", row)) ;
494 		    ASSERT (Frpos [row] == EMPTY) ;
495 		    pivrow [OUT] = row ;
496 		    DEBUGm4 (("got row merge pivrow %d\n", pivrow [OUT])) ;
497 		    break ;
498 		}
499 	    }
500 	    Front_new1strow [fleftmost] = row ;
501 
502 	    if (pivrow [OUT] == EMPTY)
503 	    {
504 		/* not found, look in empty row set in "dummy" front */
505 		row1 = Front_new1strow [nfr] ;
506 		row2 = n_row-1 ;
507 		DEBUG3 (("Empty: "ID" Rows ["ID" to "ID"] srch["ID" to "ID"]\n",
508 		    nfr, Front_1strow [nfr], row2, row1, row2)) ;
509 
510 		/* look in the range row1 ... row2 */
511 		for (row = row1 ; row <= row2 ; row++)
512 		{
513 		    DEBUG3 (("   Empty Row: "ID"\n", row)) ;
514 		    if (NON_PIVOTAL_ROW (row))
515 		    {
516 			/* found it */
517 			DEBUG3 (("   Empty Row: "ID" found\n", row)) ;
518 			ASSERT (Frpos [row] == EMPTY) ;
519 			pivrow [OUT] = row ;
520 			DEBUGm4 (("got dummy row pivrow %d\n", pivrow [OUT])) ;
521 			break ;
522 		    }
523 		}
524 		Front_new1strow [nfr] = row ;
525 	    }
526 
527 	    if (pivrow [OUT] == EMPTY)
528 	    {
529 		/* Row-merge set is empty.  We can just discard */
530 		/* the candidate pivot column. */
531 		DEBUG0 (("Note: row-merge set empty\n")) ;
532 		DEBUGm4 (("got no pivrow \n")) ;
533 		return (UMFPACK_WARNING_singular_matrix) ;
534 	    }
535 	}
536     }
537 
538     /* ---------------------------------------------------------------------- */
539     /* construct the candidate row in the front, if any */
540     /* ---------------------------------------------------------------------- */
541 
542 #ifndef NDEBUG
543     /* check Wrp */
544     ASSERT (Work->Wrpflag > 0) ;
545     if (UMF_debug > 0 || Work->n_col < 1000)
546     {
547 	for (i = 0 ; i < Work->n_col ; i++)
548 	{
549 	    ASSERT (Wrp [i] < Work->Wrpflag) ;
550 	}
551     }
552 #endif
553 
554 #ifndef NDEBUG
555     DEBUG4 (("pivrow [IN]: "ID"\n", pivrow [IN])) ;
556     UMF_dump_rowcol (0, Numeric, Work, pivrow [IN], TRUE) ;
557 #endif
558 
559     if (pivrow [IN] != EMPTY)
560     {
561 
562 	/* the row merge candidate row is not pivrow [IN] */
563 	freebie [IN] = (pivrow [IN] == prior_pivrow [IN]) && (cdeg1  > 0) ;
564 	ASSERT (cdeg1  >= 0) ;
565 
566 	if (!freebie [IN])
567 	{
568 	    /* include current front in the degree of this row */
569 
570 	    Fcpos = Work->Fcpos ;
571 	    fncols = Work->fncols ;
572 
573 	    Wrpflag = Work->Wrpflag ;
574 
575 	    /* -------------------------------------------------------------- */
576 	    /* construct the pattern of the IN row */
577 	    /* -------------------------------------------------------------- */
578 
579 #ifndef NDEBUG
580 	    /* check Fcols */
581 	    DEBUG5 (("ROW ASSEMBLE: rdeg "ID"\nREDUCE ROW "ID"\n",
582 		fncols, pivrow [IN])) ;
583 	    for (j = 0 ; j < fncols ; j++)
584 	    {
585 		col = Work->Fcols [j] ;
586 		ASSERT (col >= 0 && col < Work->n_col) ;
587 		ASSERT (Fcpos [col] >= 0) ;
588 	    }
589 	    if (UMF_debug > 0 || Work->n_col < 1000)
590 	    {
591 		Int cnt = fncols ;
592 		for (col = 0 ; col < Work->n_col ; col++)
593 		{
594 		    if (Fcpos [col] < 0) cnt++ ;
595 		}
596 		ASSERT (cnt == Work->n_col) ;
597 	    }
598 #endif
599 
600 	    rdeg [IN] = fncols ;
601 
602 	    ASSERT (pivrow [IN] >= 0 && pivrow [IN] < Work->n_row) ;
603 	    ASSERT (NON_PIVOTAL_ROW (pivrow [IN])) ;
604 
605 	    /* add the pivot column itself */
606 	    ASSERT (Wrp [pivcol] != Wrpflag) ;
607 	    if (Fcpos [pivcol] < 0)
608 	    {
609 		DEBUG3 (("Adding pivot col to pivrow [IN] pattern\n")) ;
610 		if (rdeg [IN] >= max_rdeg)
611 		{
612 		    /* :: pattern change (in) :: */
613 		    return (UMFPACK_ERROR_different_pattern) ;
614 		}
615 		Wrp [pivcol] = Wrpflag ;
616 		W_i [rdeg [IN]++] = pivcol ;
617 	    }
618 
619 	    tpi = Row_tuples [pivrow [IN]] ;
620 	    if (tpi)
621 	    {
622 		tp = (Tuple *) (Memory + tpi) ;
623 		tp1 = tp ;
624 		tp2 = tp ;
625 		tpend = tp + Row_tlen [pivrow [IN]] ;
626 		for ( ; tp < tpend ; tp++)
627 		{
628 		    e = tp->e ;
629 		    ASSERT (e > 0 && e <= Work->nel) ;
630 		    if (!E [e])
631 		    {
632 			continue ;	/* element already deallocated */
633 		    }
634 		    f = tp->f ;
635 		    p = Memory + E [e] ;
636 		    ep = (Element *) p ;
637 		    p += UNITS (Element, 1) ;
638 		    Cols = (Int *) p ;
639 		    ncols = ep->ncols ;
640 		    Rows = Cols + ncols ;
641 		    if (Rows [f] == EMPTY)
642 		    {
643 			continue ;	/* row already assembled */
644 		    }
645 		    ASSERT (pivrow [IN] == Rows [f]) ;
646 
647 		    for (j = 0 ; j < ncols ; j++)
648 		    {
649 			col = Cols [j] ;
650 			ASSERT (col >= EMPTY && col < Work->n_col) ;
651 			if ((col >= 0) && (Wrp [col] != Wrpflag)
652 			    && Fcpos [col] <0)
653 			{
654 			    ASSERT (NON_PIVOTAL_COL (col)) ;
655 			    if (rdeg [IN] >= max_rdeg)
656 			    {
657 				/* :: pattern change (rdeg in failure) :: */
658 				DEBUGm4 (("rdeg [IN] >= max_rdeg failure\n")) ;
659 				return (UMFPACK_ERROR_different_pattern) ;
660 			    }
661 			    Wrp [col] = Wrpflag ;
662 			    W_i [rdeg [IN]++] = col ;
663 			}
664 		    }
665 
666 		    *tp2++ = *tp ;	/* leave the tuple in the list */
667 		}
668 		Row_tlen [pivrow [IN]] = tp2 - tp1 ;
669 	    }
670 
671 #ifndef NDEBUG
672 	    DEBUG4 (("Reduced IN row:\n")) ;
673 	    for (j = 0 ; j < fncols ; j++)
674 	    {
675 		DEBUG6 ((" "ID" "ID" "ID"\n",
676 		    j, Work->Fcols [j], Fcpos [Work->Fcols [j]])) ;
677 		ASSERT (Fcpos [Work->Fcols [j]] >= 0) ;
678 	    }
679 	    for (j = fncols ; j < rdeg [IN] ; j++)
680 	    {
681 		DEBUG6 ((" "ID" "ID" "ID"\n", j, W_i [j], Wrp [W_i [j]]));
682 		ASSERT (W_i [j] >= 0 && W_i [j] < Work->n_col) ;
683 		ASSERT (Wrp [W_i [j]] == Wrpflag) ;
684 	    }
685 	    /* mark the end of the pattern in case we scan it by mistake */
686 	    /* Note that this means W_i must be of size >= fncols_max + 1 */
687 	    W_i [rdeg [IN]] = EMPTY ;
688 #endif
689 
690 	    /* rdeg [IN] is now the exact degree of the IN row */
691 
692 	    /* clear Work->Wrp. */
693 	    Work->Wrpflag++ ;
694 	    /* All Wrp [0..n_col] is now < Wrpflag */
695 	}
696     }
697 
698 #ifndef NDEBUG
699     /* check Wrp */
700     if (UMF_debug > 0 || Work->n_col < 1000)
701     {
702 	for (i = 0 ; i < Work->n_col ; i++)
703 	{
704 	    ASSERT (Wrp [i] < Work->Wrpflag) ;
705 	}
706     }
707 #endif
708 
709     /* ---------------------------------------------------------------------- */
710     /* construct the candidate row not in the front, if any */
711     /* ---------------------------------------------------------------------- */
712 
713 #ifndef NDEBUG
714     DEBUG4 (("pivrow [OUT]: "ID"\n", pivrow [OUT])) ;
715     UMF_dump_rowcol (0, Numeric, Work, pivrow [OUT], TRUE) ;
716 #endif
717 
718     /* If this is a candidate row from the row merge set, force it to be */
719     /* scanned (ignore prior_pivrow [OUT]). */
720 
721     if (pivrow [OUT] != EMPTY)
722     {
723 	freebie [OUT] = (pivrow [OUT] == prior_pivrow [OUT]) && cdeg1  > 0 ;
724 	ASSERT (cdeg1  >= 0) ;
725 
726 	if (!freebie [OUT])
727 	{
728 
729 	    Wrpflag = Work->Wrpflag ;
730 
731 	    /* -------------------------------------------------------------- */
732 	    /* construct the pattern of the row */
733 	    /* -------------------------------------------------------------- */
734 
735 	    rdeg [OUT] = 0 ;
736 
737 	    ASSERT (pivrow [OUT] >= 0 && pivrow [OUT] < Work->n_row) ;
738 	    ASSERT (NON_PIVOTAL_ROW (pivrow [OUT])) ;
739 
740 	    /* add the pivot column itself */
741 	    ASSERT (Wrp [pivcol] != Wrpflag) ;
742 	    DEBUG3 (("Adding pivot col to pivrow [OUT] pattern\n")) ;
743 	    if (rdeg [OUT] >= max_rdeg)
744 	    {
745 		/* :: pattern change (out) :: */
746 		return (UMFPACK_ERROR_different_pattern) ;
747 	    }
748 	    Wrp [pivcol] = Wrpflag ;
749 	    W_o [rdeg [OUT]++] = pivcol ;
750 
751 	    tpi = Row_tuples [pivrow [OUT]] ;
752 	    if (tpi)
753 	    {
754 		tp = (Tuple *) (Memory + tpi) ;
755 		tp1 = tp ;
756 		tp2 = tp ;
757 		tpend = tp + Row_tlen [pivrow [OUT]] ;
758 		for ( ; tp < tpend ; tp++)
759 		{
760 		    e = tp->e ;
761 		    ASSERT (e > 0 && e <= Work->nel) ;
762 		    if (!E [e])
763 		    {
764 			continue ;	/* element already deallocated */
765 		    }
766 		    f = tp->f ;
767 		    p = Memory + E [e] ;
768 		    ep = (Element *) p ;
769 		    p += UNITS (Element, 1) ;
770 		    Cols = (Int *) p ;
771 		    ncols = ep->ncols ;
772 		    Rows = Cols + ncols ;
773 		    if (Rows [f] == EMPTY)
774 		    {
775 			continue ;	/* row already assembled */
776 		    }
777 		    ASSERT (pivrow [OUT] == Rows [f]) ;
778 
779 		    for (j = 0 ; j < ncols ; j++)
780 		    {
781 			col = Cols [j] ;
782 			ASSERT (col >= EMPTY && col < Work->n_col) ;
783 			if ((col >= 0) && (Wrp [col] != Wrpflag))
784 			{
785 			    ASSERT (NON_PIVOTAL_COL (col)) ;
786 			    if (rdeg [OUT] >= max_rdeg)
787 			    {
788 				/* :: pattern change (rdeg out failure) :: */
789 				DEBUGm4 (("rdeg [OUT] failure\n")) ;
790 				return (UMFPACK_ERROR_different_pattern) ;
791 			    }
792 			    Wrp [col] = Wrpflag ;
793 			    W_o [rdeg [OUT]++] = col ;
794 			}
795 		    }
796 		    *tp2++ = *tp ;	/* leave the tuple in the list */
797 		}
798 		Row_tlen [pivrow [OUT]] = tp2 - tp1 ;
799 	    }
800 
801 #ifndef NDEBUG
802 	    DEBUG4 (("Reduced row OUT:\n")) ;
803 	    for (j = 0 ; j < rdeg [OUT] ; j++)
804 	    {
805 		DEBUG6 ((" "ID" "ID" "ID"\n", j, W_o [j], Wrp [W_o [j]])) ;
806 		ASSERT (W_o [j] >= 0 && W_o [j] < Work->n_col) ;
807 		ASSERT (Wrp [W_o [j]] == Wrpflag) ;
808 	    }
809 	    /* mark the end of the pattern in case we scan it by mistake */
810 	    /* Note that this means W_o must be of size >= fncols_max + 1 */
811 	    W_o [rdeg [OUT]] = EMPTY ;
812 #endif
813 
814 	    /* rdeg [OUT] is now the exact degree of the row */
815 
816 	    /* clear Work->Wrp. */
817 	    Work->Wrpflag++ ;
818 	    /* All Wrp [0..n] is now < Wrpflag */
819 
820 	}
821 
822     }
823     DEBUG5 (("Row_search end ] \n")) ;
824 
825 #ifndef NDEBUG
826     /* check Wrp */
827     if (UMF_debug > 0 || Work->n_col < 1000)
828     {
829 	for (i = 0 ; i < Work->n_col ; i++)
830 	{
831 	    ASSERT (Wrp [i] < Work->Wrpflag) ;
832 	}
833     }
834 #endif
835 
836     return (UMFPACK_OK) ;
837 }
838