/* ========================================================================== */
/* === UMF_row_search ======================================================= */
/* ========================================================================== */

/* -------------------------------------------------------------------------- */
/* UMFPACK Version 3.2 (Jan. 1, 2002), Copyright (c) 2002 by Timothy A.       */
/* Davis, University of Florida, davis@cise.ufl.edu.  All Rights Reserved.    */
/* See README, umfpack.h, or type "umfpack_details" in Matlab for License.    */
/* -------------------------------------------------------------------------- */

/*
    Find two candidate pivot rows in a column: the best one in the front,
    and the best one not in the front.  Return the two pivot row patterns and
    their exact degrees.  Called by UMF_local_search.

    Returns UMFPACK_OK if successful, or UMFPACK_ERROR_singular_matrix or
    UMFPACK_ERROR_different_pattern if not.

*/

#include "umf_internal.h"

#define IN 0
#define OUT 1

GLOBAL Int UMF_row_search
(
    NumericType *Numeric,
    WorkType *Work,
    Int cdeg,			/* length of column */
    const Int Pattern [ ],	/* pattern of column */
    Int pivrow [2],		/* pivrow [IN] and pivrow [OUT] */
    Int rdeg [2],		/* rdeg [IN] and rdeg [OUT] */
    Int W_i [ ],		/* pattern of pivrow [IN], */
				/* either Fcols or Woi */
    Int W_o [ ],		/* pattern of pivrow [OUT], */
				/* either Wio or Woo */
    Int prior_pivrow [2],	/* the two other rows just scanned, if any */
    const double Fcol [ ]	/* numerical values in column */
				/* (a column in the front) */

    , Int pivcol		/* the candidate column being searched */
)
{

    /* ---------------------------------------------------------------------- */
    /* local variables */
    /* ---------------------------------------------------------------------- */

    double maxval, toler, value ;
    Int i, row, deg, *Wp, col, *Frpos, fnrows, *E, j, ncols, *Cols, *Rows,
	e, f, wpflag, *Fcpos, fncols, tpi, max_rdeg, sym_pivoting, in_front ;
    Tuple *tp, *tpend, *tp1, *tp2 ;
    Unit *Memory, *p ;
    Element *ep ;
    Int *Row_tuples, *Row_degree, *Row_tlen ;

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

    Row_degree = Numeric->Rperm ;

    Row_tuples = Numeric->Uip ;
    Row_tlen   = Numeric->Uilen ;

    Wp = Work->Wp ;
    Frpos = Work->Frpos ;
    E = Work->E ;
    Memory = Numeric->Memory ;
    fnrows = Work->fnrows ;

    /* pivot row degree cannot exceed max_rdeg */
    max_rdeg = Work->fncols_max - Work->fnpiv ;

    sym_pivoting = Numeric->pivot_option != UMFPACK_DEFAULT_PIVOT_OPTION ;

    /* ---------------------------------------------------------------------- */
    /* scan pivot column for candidate rows */
    /* ---------------------------------------------------------------------- */

    maxval = 0.0 ;
    for (i = 0 ; i < cdeg ; i++)
    {
	value = ABS (Fcol [i]) ;
	maxval = MAX (maxval, value) ;
    }

    if (maxval == 0.0)
    {
	/* singular matrix */
	return (UMFPACK_ERROR_singular_matrix) ;
    }

    toler = Numeric->relpt * maxval ;
    if (toler == 0.0)
    {
	toler = maxval ;
    }
    DEBUG5 (("maxval %g toler %g\n", maxval, toler)) ;

    for (i = 0 ; i < cdeg ; i++)
    {
	double a = ABS (Fcol [i]) ;
	if (a >= toler)
	{
	    row = Pattern [i] ;
	    deg = Row_degree [row] ;
#ifndef NDEBUG
	    DEBUG6 ((ID" Candidate row "ID" deg "ID" absval %g\n", i, row, deg,
		a)) ;
	    UMF_dump_rowcol (0, Numeric, Work, row, TRUE) ;
#endif

	    in_front = (Frpos [row] >= 0 && Frpos [row] < fnrows) ;

	    /* find the diagonal - use it if it exists (sym. pivoting only) */
	    if (sym_pivoting && row == pivcol)
	    {
		DEBUG3 (("Symmetric pivot found: %d\n", pivcol)) ;
		if (in_front)
		{
		    pivrow [IN] = pivcol ;
		    pivrow [OUT] = EMPTY ;
		}
		else
		{
		    pivrow [IN] = EMPTY ;
		    pivrow [OUT] = pivcol ;
		}
		break ;
	    }

	    /* find the best off-diagonal pivots */
	    if (in_front)
	    {
		/* row is in the current front */
		DEBUG4 ((" in front\n")) ;
		if (deg < rdeg [IN] || (deg == rdeg [IN] && row < pivrow [IN]))
		{
		    /* best row in front, so far */
		    pivrow [IN] = row ;
		    rdeg [IN] = deg ;
		}
	    }
	    else
	    {
		/* row is not in the current front */
		DEBUG4 ((" NOT in front\n")) ;
		if (deg < rdeg [OUT] || (deg == rdeg[OUT] && row < pivrow[OUT]))
		{
		    /* best row not in front, so far */
		    pivrow [OUT] = row ;
		    rdeg [OUT] = deg ;
		}
	    }
	}
    }

    /* ---------------------------------------------------------------------- */
    /* construct the candidate row in the front, if any */
    /* ---------------------------------------------------------------------- */

#ifndef NDEBUG
    DEBUG4 (("pivrow [IN]: "ID"\n", pivrow [IN])) ;
    UMF_dump_rowcol (0, Numeric, Work, pivrow [IN], TRUE) ;
#endif

    if (pivrow [IN] != EMPTY && pivrow [IN] != prior_pivrow [IN])
    {
	/* include current front in the degree of this row */

	Fcpos = Work->Fcpos ;
	fncols = Work->fncols ;

	wpflag = Work->Wpflag ;
	ASSERT (wpflag < EMPTY) ;

	/* ------------------------------------------------------------------ */
	/* construct the pattern of the IN row */
	/* ------------------------------------------------------------------ */

#ifndef NDEBUG
	/* check Fcols */
	DEBUG5 (("ROW ASSEMBLE: rdeg "ID"\nREDUCE ROW "ID"\n",
	    fncols, pivrow [IN])) ;
	for (j = 0 ; j < fncols ; j++)
	{
	    col = Work->Fcols [j] ;
	    ASSERT (col >= 0 && col < Work->n) ;
	    ASSERT (Fcpos [col] >= 0) ;
	}
	if (UMF_debug > 0 || Work->n < 1000)
	{
	    Int cnt = fncols ;
	    for (i = 0 ; i < Work->n ; i++)
	    {
		if (Fcpos [i] < 0) cnt++ ;
	    }
	    ASSERT (cnt == Work->n) ;
	}
	/* check Wp */
	if (UMF_debug > 0 || Work->n < 1000)
	{
	    for (i = 0 ; i < Work->n ; i++)
	    {
		ASSERT (Wp [i] < 0) ;
		ASSERT (Wp [i] > wpflag) ;
	    }
	}
#endif

	rdeg [IN] = fncols ;

	ASSERT (pivrow [IN] >= 0 && pivrow [IN] < Work->n) ;
	ASSERT (NON_PIVOTAL_ROW (pivrow [IN])) ;

	tpi = Row_tuples [pivrow [IN]] ;
	if (tpi)
	{
	    tp = (Tuple *) (Memory + tpi) ;
	    tp1 = tp ;
	    tp2 = tp ;
	    tpend = tp + Row_tlen [pivrow [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 ;
		ncols = ep->ncols ;
		Rows = Cols + ncols ;
		if (Rows [f] == EMPTY)
		{
		    continue ;	/* row already assembled */
		}
		ASSERT (pivrow [IN] == Rows [f]) ;

		for (j = 0 ; j < ncols ; j++)
		{
		    col = Cols [j] ;
		    if ((col >= 0) && (Wp [col] > wpflag) && Fcpos [col] < 0)
		    {
			if (rdeg [IN] >= max_rdeg)
			{
			    return (UMFPACK_ERROR_different_pattern) ;
			}
			Wp [col] = wpflag ;
			W_i [rdeg [IN]++] = col ;
		    }
		}

		*tp2++ = *tp ;	/* leave the tuple in the list */
	    }
	    Row_tlen [pivrow [IN]] = tp2 - tp1 ;
	}

#ifndef NDEBUG
	DEBUG4 (("Reduced IN row:\n")) ;
	for (j = 0 ; j < fncols ; j++)
	{
	    DEBUG6 ((" "ID" "ID" "ID"\n",
		j, Work->Fcols [j], Fcpos [Work->Fcols [j]])) ;
	    ASSERT (Fcpos [Work->Fcols [j]] >= 0) ;
	}
	for (j = fncols ; j < rdeg [IN] ; j++)
	{
	    DEBUG6 ((" "ID" "ID" "ID"\n", j, W_i [j], Wp [W_i [j]]));
	    ASSERT (Wp [W_i [j]] == wpflag) ;
	}
	/* mark the end of the pattern in case we scan it by mistake */
	/* Note that this means W_i must be of size >= fncols_max + 1 */
	W_i [rdeg [IN]] = EMPTY ;
#endif

	/* rdeg [IN] is now the exact degree of the IN row */

	/* clear Work->Wp.  All Wp [0..n] is now negative, and > Work->wpflag */
	Work->Wpflag-- ;

    }

    /* ---------------------------------------------------------------------- */
    /* construct the candidate row not in the front, if any */
    /* ---------------------------------------------------------------------- */

#ifndef NDEBUG
    DEBUG4 (("pivrow [OUT]: "ID"\n", pivrow [OUT])) ;
    UMF_dump_rowcol (0, Numeric, Work, pivrow [OUT], TRUE) ;
#endif

    if (pivrow [OUT] != EMPTY && pivrow [OUT] != prior_pivrow [OUT])
    {
	wpflag = Work->Wpflag ;
	ASSERT (wpflag < EMPTY) ;

	/* ------------------------------------------------------------------ */
	/* construct the pattern of the row */
	/* ------------------------------------------------------------------ */

#ifndef NDEBUG
	/* check Wp */
	if (UMF_debug > 0 || Work->n < 1000)
	{
	    for (i = 0 ; i < Work->n ; i++)
	    {
		ASSERT (Wp [i] < 0) ;
		ASSERT (Wp [i] > wpflag) ;
	    }
	}
#endif

	rdeg [OUT] = 0 ;

	ASSERT (pivrow [OUT] >= 0 && pivrow [OUT] < Work->n) ;
	ASSERT (NON_PIVOTAL_ROW (pivrow [OUT])) ;

	tpi = Row_tuples [pivrow [OUT]] ;
	if (tpi)
	{
	    tp = (Tuple *) (Memory + tpi) ;
	    tp1 = tp ;
	    tp2 = tp ;
	    tpend = tp + Row_tlen [pivrow [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 ;
		ncols = ep->ncols ;
		Rows = Cols + ncols ;
		if (Rows [f] == EMPTY)
		{
		    continue ;	/* row already assembled */
		}
		ASSERT (pivrow [OUT] == Rows [f]) ;

		for (j = 0 ; j < ncols ; j++)
		{
		    col = Cols [j] ;
		    if ((col >= 0) && (Wp [col] > wpflag))
		    {
			if (rdeg [OUT] >= max_rdeg)
			{
			    return (UMFPACK_ERROR_different_pattern) ;
			}
			Wp [col] = wpflag ;
			W_o [rdeg [OUT]++] = col ;
		    }
		}

		*tp2++ = *tp ;	/* leave the tuple in the list */
	    }
	    Row_tlen [pivrow [OUT]] = tp2 - tp1 ;
	}

#ifndef NDEBUG
	DEBUG4 (("Reduced row OUT:\n")) ;
	for (j = 0 ; j < rdeg [OUT] ; j++)
	{
	    DEBUG6 ((" "ID" "ID" "ID"\n",
		j, W_o [j], Wp [W_o [j]])) ;
	    ASSERT (Wp [W_o [j]] == wpflag) ;
	}
	/* mark the end of the pattern in case we scan it by mistake */
	/* Note that this means W_o must be of size >= fncols_max + 1 */
	W_o [rdeg [OUT]] = EMPTY ;
#endif

	/* rdeg [OUT] is now the exact degree of the row */

	/* clear Work->Wp.  All Wp [0..n] is now negative, and > Work->Wpflag */
	Work->Wpflag-- ;

    }

    return (UMFPACK_OK) ;
}

