3612 lines
		
	
	
		
			106 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
			
		
		
	
	
			3612 lines
		
	
	
		
			106 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
/* ========================================================================== */
 | 
						|
/* === colamd/symamd - a sparse matrix column ordering algorithm ============ */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/* COLAMD / SYMAMD
 | 
						|
 | 
						|
    colamd:  an approximate minimum degree column ordering algorithm,
 | 
						|
    	for LU factorization of symmetric or unsymmetric matrices,
 | 
						|
	QR factorization, least squares, interior point methods for
 | 
						|
	linear programming problems, and other related problems.
 | 
						|
 | 
						|
    symamd:  an approximate minimum degree ordering algorithm for Cholesky
 | 
						|
    	factorization of symmetric matrices.
 | 
						|
 | 
						|
    Purpose:
 | 
						|
 | 
						|
	Colamd computes a permutation Q such that the Cholesky factorization of
 | 
						|
	(AQ)'(AQ) has less fill-in and requires fewer floating point operations
 | 
						|
	than A'A.  This also provides a good ordering for sparse partial
 | 
						|
	pivoting methods, P(AQ) = LU, where Q is computed prior to numerical
 | 
						|
	factorization, and P is computed during numerical factorization via
 | 
						|
	conventional partial pivoting with row interchanges.  Colamd is the
 | 
						|
	column ordering method used in SuperLU, part of the ScaLAPACK library.
 | 
						|
	It is also available as built-in function in MATLAB Version 6,
 | 
						|
	available from MathWorks, Inc. (http://www.mathworks.com).  This
 | 
						|
	routine can be used in place of colmmd in MATLAB.
 | 
						|
 | 
						|
    	Symamd computes a permutation P of a symmetric matrix A such that the
 | 
						|
	Cholesky factorization of PAP' has less fill-in and requires fewer
 | 
						|
	floating point operations than A.  Symamd constructs a matrix M such
 | 
						|
	that M'M has the same nonzero pattern of A, and then orders the columns
 | 
						|
	of M using colmmd.  The column ordering of M is then returned as the
 | 
						|
	row and column ordering P of A. 
 | 
						|
 | 
						|
    Authors:
 | 
						|
 | 
						|
	The authors of the code itself are Stefan I. Larimore and Timothy A.
 | 
						|
	Davis (davis at cise.ufl.edu), University of Florida.  The algorithm was
 | 
						|
	developed in collaboration with John Gilbert, Xerox PARC, and Esmond
 | 
						|
	Ng, Oak Ridge National Laboratory.
 | 
						|
 | 
						|
    Acknowledgements:
 | 
						|
 | 
						|
	This work was supported by the National Science Foundation, under
 | 
						|
	grants DMS-9504974 and DMS-9803599.
 | 
						|
 | 
						|
    Copyright and License:
 | 
						|
 | 
						|
	Copyright (c) 1998-2007, Timothy A. Davis, All Rights Reserved.
 | 
						|
	COLAMD is also available under alternate licenses, contact T. Davis
 | 
						|
	for details.
 | 
						|
 | 
						|
	This library is free software; you can redistribute it and/or
 | 
						|
	modify it under the terms of the GNU Lesser General Public
 | 
						|
	License as published by the Free Software Foundation; either
 | 
						|
	version 2.1 of the License, or (at your option) any later version.
 | 
						|
 | 
						|
	This library is distributed in the hope that it will be useful,
 | 
						|
	but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
	Lesser General Public License for more details.
 | 
						|
 | 
						|
	You should have received a copy of the GNU Lesser General Public
 | 
						|
	License along with this library; if not, write to the Free Software
 | 
						|
	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 | 
						|
	USA
 | 
						|
 | 
						|
	Permission is hereby granted to use or copy this program under the
 | 
						|
	terms of the GNU LGPL, provided that the Copyright, this License,
 | 
						|
	and the Availability of the original version is retained on all copies.
 | 
						|
	User documentation of any code that uses this code or any modified
 | 
						|
	version of this code must cite the Copyright, this License, the
 | 
						|
	Availability note, and "Used by permission." Permission to modify
 | 
						|
	the code and to distribute modified code is granted, provided the
 | 
						|
	Copyright, this License, and the Availability note are retained,
 | 
						|
	and a notice that the code was modified is included.
 | 
						|
 | 
						|
    Availability:
 | 
						|
 | 
						|
	The colamd/symamd library is available at
 | 
						|
 | 
						|
	    http://www.cise.ufl.edu/research/sparse/colamd/
 | 
						|
 | 
						|
	This is the http://www.cise.ufl.edu/research/sparse/colamd/colamd.c
 | 
						|
	file.  It requires the colamd.h file.  It is required by the colamdmex.c
 | 
						|
	and symamdmex.c files, for the MATLAB interface to colamd and symamd.
 | 
						|
	Appears as ACM Algorithm 836.
 | 
						|
 | 
						|
    See the ChangeLog file for changes since Version 1.0.
 | 
						|
 | 
						|
    References:
 | 
						|
 | 
						|
	T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, An approximate column
 | 
						|
	minimum degree ordering algorithm, ACM Transactions on Mathematical
 | 
						|
	Software, vol. 30, no. 3., pp. 353-376, 2004.
 | 
						|
 | 
						|
	T. A. Davis, J. R. Gilbert, S. Larimore, E. Ng, Algorithm 836: COLAMD,
 | 
						|
	an approximate column minimum degree ordering algorithm, ACM
 | 
						|
	Transactions on Mathematical Software, vol. 30, no. 3., pp. 377-380,
 | 
						|
	2004.
 | 
						|
 | 
						|
*/
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === Description of user-callable routines ================================ */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/* COLAMD includes both int and UF_long versions of all its routines.  The
 | 
						|
 * description below is for the int version.  For UF_long, all int arguments
 | 
						|
 * become UF_long.  UF_long is normally defined as long, except for WIN64.
 | 
						|
 | 
						|
    ----------------------------------------------------------------------------
 | 
						|
    colamd_recommended:
 | 
						|
    ----------------------------------------------------------------------------
 | 
						|
 | 
						|
	C syntax:
 | 
						|
 | 
						|
	    #include "colamd.h"
 | 
						|
	    size_t colamd_recommended (int nnz, int n_row, int n_col) ;
 | 
						|
	    size_t colamd_l_recommended (UF_long nnz, UF_long n_row,
 | 
						|
		UF_long n_col) ;
 | 
						|
 | 
						|
	Purpose:
 | 
						|
 | 
						|
	    Returns recommended value of Alen for use by colamd.  Returns 0
 | 
						|
	    if any input argument is negative.  The use of this routine
 | 
						|
	    is optional.  Not needed for symamd, which dynamically allocates
 | 
						|
	    its own memory.
 | 
						|
 | 
						|
	    Note that in v2.4 and earlier, these routines returned int or long.
 | 
						|
	    They now return a value of type size_t.
 | 
						|
 | 
						|
	Arguments (all input arguments):
 | 
						|
 | 
						|
	    int nnz ;		Number of nonzeros in the matrix A.  This must
 | 
						|
				be the same value as p [n_col] in the call to
 | 
						|
				colamd - otherwise you will get a wrong value
 | 
						|
				of the recommended memory to use.
 | 
						|
 | 
						|
	    int n_row ;		Number of rows in the matrix A.
 | 
						|
 | 
						|
	    int n_col ;		Number of columns in the matrix A.
 | 
						|
 | 
						|
    ----------------------------------------------------------------------------
 | 
						|
    colamd_set_defaults:
 | 
						|
    ----------------------------------------------------------------------------
 | 
						|
 | 
						|
	C syntax:
 | 
						|
 | 
						|
	    #include "colamd.h"
 | 
						|
	    colamd_set_defaults (double knobs [COLAMD_KNOBS]) ;
 | 
						|
	    colamd_l_set_defaults (double knobs [COLAMD_KNOBS]) ;
 | 
						|
 | 
						|
	Purpose:
 | 
						|
 | 
						|
	    Sets the default parameters.  The use of this routine is optional.
 | 
						|
 | 
						|
	Arguments:
 | 
						|
 | 
						|
	    double knobs [COLAMD_KNOBS] ;	Output only.
 | 
						|
 | 
						|
		NOTE: the meaning of the dense row/col knobs has changed in v2.4
 | 
						|
 | 
						|
		knobs [0] and knobs [1] control dense row and col detection:
 | 
						|
 | 
						|
		Colamd: rows with more than
 | 
						|
		max (16, knobs [COLAMD_DENSE_ROW] * sqrt (n_col))
 | 
						|
		entries are removed prior to ordering.  Columns with more than
 | 
						|
		max (16, knobs [COLAMD_DENSE_COL] * sqrt (MIN (n_row,n_col)))
 | 
						|
		entries are removed prior to
 | 
						|
		ordering, and placed last in the output column ordering. 
 | 
						|
 | 
						|
		Symamd: uses only knobs [COLAMD_DENSE_ROW], which is knobs [0].
 | 
						|
		Rows and columns with more than
 | 
						|
		max (16, knobs [COLAMD_DENSE_ROW] * sqrt (n))
 | 
						|
		entries are removed prior to ordering, and placed last in the
 | 
						|
		output ordering.
 | 
						|
 | 
						|
		COLAMD_DENSE_ROW and COLAMD_DENSE_COL are defined as 0 and 1,
 | 
						|
		respectively, in colamd.h.  Default values of these two knobs
 | 
						|
		are both 10.  Currently, only knobs [0] and knobs [1] are
 | 
						|
		used, but future versions may use more knobs.  If so, they will
 | 
						|
		be properly set to their defaults by the future version of
 | 
						|
		colamd_set_defaults, so that the code that calls colamd will
 | 
						|
		not need to change, assuming that you either use
 | 
						|
		colamd_set_defaults, or pass a (double *) NULL pointer as the
 | 
						|
		knobs array to colamd or symamd.
 | 
						|
 | 
						|
	    knobs [2]: aggressive absorption
 | 
						|
 | 
						|
	        knobs [COLAMD_AGGRESSIVE] controls whether or not to do
 | 
						|
	        aggressive absorption during the ordering.  Default is TRUE.
 | 
						|
 | 
						|
 | 
						|
    ----------------------------------------------------------------------------
 | 
						|
    colamd:
 | 
						|
    ----------------------------------------------------------------------------
 | 
						|
 | 
						|
	C syntax:
 | 
						|
 | 
						|
	    #include "colamd.h"
 | 
						|
	    int colamd (int n_row, int n_col, int Alen, int *A, int *p,
 | 
						|
	    	double knobs [COLAMD_KNOBS], int stats [COLAMD_STATS]) ;
 | 
						|
	    UF_long colamd_l (UF_long n_row, UF_long n_col, UF_long Alen,
 | 
						|
		UF_long *A, UF_long *p, double knobs [COLAMD_KNOBS],
 | 
						|
		UF_long stats [COLAMD_STATS]) ;
 | 
						|
 | 
						|
	Purpose:
 | 
						|
 | 
						|
	    Computes a column ordering (Q) of A such that P(AQ)=LU or
 | 
						|
	    (AQ)'AQ=LL' have less fill-in and require fewer floating point
 | 
						|
	    operations than factorizing the unpermuted matrix A or A'A,
 | 
						|
	    respectively.
 | 
						|
	    
 | 
						|
	Returns:
 | 
						|
 | 
						|
	    TRUE (1) if successful, FALSE (0) otherwise.
 | 
						|
 | 
						|
	Arguments:
 | 
						|
 | 
						|
	    int n_row ;		Input argument.
 | 
						|
 | 
						|
		Number of rows in the matrix A.
 | 
						|
		Restriction:  n_row >= 0.
 | 
						|
		Colamd returns FALSE if n_row is negative.
 | 
						|
 | 
						|
	    int n_col ;		Input argument.
 | 
						|
 | 
						|
		Number of columns in the matrix A.
 | 
						|
		Restriction:  n_col >= 0.
 | 
						|
		Colamd returns FALSE if n_col is negative.
 | 
						|
 | 
						|
	    int Alen ;		Input argument.
 | 
						|
 | 
						|
		Restriction (see note):
 | 
						|
		Alen >= 2*nnz + 6*(n_col+1) + 4*(n_row+1) + n_col
 | 
						|
		Colamd returns FALSE if these conditions are not met.
 | 
						|
 | 
						|
		Note:  this restriction makes an modest assumption regarding
 | 
						|
		the size of the two typedef's structures in colamd.h.
 | 
						|
		We do, however, guarantee that
 | 
						|
 | 
						|
			Alen >= colamd_recommended (nnz, n_row, n_col)
 | 
						|
 | 
						|
		will be sufficient.  Note: the macro version does not check
 | 
						|
		for integer overflow, and thus is not recommended.  Use
 | 
						|
		the colamd_recommended routine instead.
 | 
						|
 | 
						|
	    int A [Alen] ;	Input argument, undefined on output.
 | 
						|
 | 
						|
		A is an integer array of size Alen.  Alen must be at least as
 | 
						|
		large as the bare minimum value given above, but this is very
 | 
						|
		low, and can result in excessive run time.  For best
 | 
						|
		performance, we recommend that Alen be greater than or equal to
 | 
						|
		colamd_recommended (nnz, n_row, n_col), which adds
 | 
						|
		nnz/5 to the bare minimum value given above.
 | 
						|
 | 
						|
		On input, the row indices of the entries in column c of the
 | 
						|
		matrix are held in A [(p [c]) ... (p [c+1]-1)].  The row indices
 | 
						|
		in a given column c need not be in ascending order, and
 | 
						|
		duplicate row indices may be be present.  However, colamd will
 | 
						|
		work a little faster if both of these conditions are met
 | 
						|
		(Colamd puts the matrix into this format, if it finds that the
 | 
						|
		the conditions are not met).
 | 
						|
 | 
						|
		The matrix is 0-based.  That is, rows are in the range 0 to
 | 
						|
		n_row-1, and columns are in the range 0 to n_col-1.  Colamd
 | 
						|
		returns FALSE if any row index is out of range.
 | 
						|
 | 
						|
		The contents of A are modified during ordering, and are
 | 
						|
		undefined on output.
 | 
						|
 | 
						|
	    int p [n_col+1] ;	Both input and output argument.
 | 
						|
 | 
						|
		p is an integer array of size n_col+1.  On input, it holds the
 | 
						|
		"pointers" for the column form of the matrix A.  Column c of
 | 
						|
		the matrix A is held in A [(p [c]) ... (p [c+1]-1)].  The first
 | 
						|
		entry, p [0], must be zero, and p [c] <= p [c+1] must hold
 | 
						|
		for all c in the range 0 to n_col-1.  The value p [n_col] is
 | 
						|
		thus the total number of entries in the pattern of the matrix A.
 | 
						|
		Colamd returns FALSE if these conditions are not met.
 | 
						|
 | 
						|
		On output, if colamd returns TRUE, the array p holds the column
 | 
						|
		permutation (Q, for P(AQ)=LU or (AQ)'(AQ)=LL'), where p [0] is
 | 
						|
		the first column index in the new ordering, and p [n_col-1] is
 | 
						|
		the last.  That is, p [k] = j means that column j of A is the
 | 
						|
		kth pivot column, in AQ, where k is in the range 0 to n_col-1
 | 
						|
		(p [0] = j means that column j of A is the first column in AQ).
 | 
						|
 | 
						|
		If colamd returns FALSE, then no permutation is returned, and
 | 
						|
		p is undefined on output.
 | 
						|
 | 
						|
	    double knobs [COLAMD_KNOBS] ;	Input argument.
 | 
						|
 | 
						|
		See colamd_set_defaults for a description.
 | 
						|
 | 
						|
	    int stats [COLAMD_STATS] ;		Output argument.
 | 
						|
 | 
						|
		Statistics on the ordering, and error status.
 | 
						|
		See colamd.h for related definitions.
 | 
						|
		Colamd returns FALSE if stats is not present.
 | 
						|
 | 
						|
		stats [0]:  number of dense or empty rows ignored.
 | 
						|
 | 
						|
		stats [1]:  number of dense or empty columns ignored (and
 | 
						|
				ordered last in the output permutation p)
 | 
						|
				Note that a row can become "empty" if it
 | 
						|
				contains only "dense" and/or "empty" columns,
 | 
						|
				and similarly a column can become "empty" if it
 | 
						|
				only contains "dense" and/or "empty" rows.
 | 
						|
 | 
						|
		stats [2]:  number of garbage collections performed.
 | 
						|
				This can be excessively high if Alen is close
 | 
						|
				to the minimum required value.
 | 
						|
 | 
						|
		stats [3]:  status code.  < 0 is an error code.
 | 
						|
			    > 1 is a warning or notice.
 | 
						|
 | 
						|
			0	OK.  Each column of the input matrix contained
 | 
						|
				row indices in increasing order, with no
 | 
						|
				duplicates.
 | 
						|
 | 
						|
			1	OK, but columns of input matrix were jumbled
 | 
						|
				(unsorted columns or duplicate entries).  Colamd
 | 
						|
				had to do some extra work to sort the matrix
 | 
						|
				first and remove duplicate entries, but it
 | 
						|
				still was able to return a valid permutation
 | 
						|
				(return value of colamd was TRUE).
 | 
						|
 | 
						|
					stats [4]: highest numbered column that
 | 
						|
						is unsorted or has duplicate
 | 
						|
						entries.
 | 
						|
					stats [5]: last seen duplicate or
 | 
						|
						unsorted row index.
 | 
						|
					stats [6]: number of duplicate or
 | 
						|
						unsorted row indices.
 | 
						|
 | 
						|
			-1	A is a null pointer
 | 
						|
 | 
						|
			-2	p is a null pointer
 | 
						|
 | 
						|
			-3 	n_row is negative
 | 
						|
 | 
						|
					stats [4]: n_row
 | 
						|
 | 
						|
			-4	n_col is negative
 | 
						|
 | 
						|
					stats [4]: n_col
 | 
						|
 | 
						|
			-5	number of nonzeros in matrix is negative
 | 
						|
 | 
						|
					stats [4]: number of nonzeros, p [n_col]
 | 
						|
 | 
						|
			-6	p [0] is nonzero
 | 
						|
 | 
						|
					stats [4]: p [0]
 | 
						|
 | 
						|
			-7	A is too small
 | 
						|
 | 
						|
					stats [4]: required size
 | 
						|
					stats [5]: actual size (Alen)
 | 
						|
 | 
						|
			-8	a column has a negative number of entries
 | 
						|
 | 
						|
					stats [4]: column with < 0 entries
 | 
						|
					stats [5]: number of entries in col
 | 
						|
 | 
						|
			-9	a row index is out of bounds
 | 
						|
 | 
						|
					stats [4]: column with bad row index
 | 
						|
					stats [5]: bad row index
 | 
						|
					stats [6]: n_row, # of rows of matrx
 | 
						|
 | 
						|
			-10	(unused; see symamd.c)
 | 
						|
 | 
						|
			-999	(unused; see symamd.c)
 | 
						|
 | 
						|
		Future versions may return more statistics in the stats array.
 | 
						|
 | 
						|
	Example:
 | 
						|
	
 | 
						|
	    See http://www.cise.ufl.edu/research/sparse/colamd/example.c
 | 
						|
	    for a complete example.
 | 
						|
 | 
						|
	    To order the columns of a 5-by-4 matrix with 11 nonzero entries in
 | 
						|
	    the following nonzero pattern
 | 
						|
 | 
						|
	    	x 0 x 0
 | 
						|
		x 0 x x
 | 
						|
		0 x x 0
 | 
						|
		0 0 x x
 | 
						|
		x x 0 0
 | 
						|
 | 
						|
	    with default knobs and no output statistics, do the following:
 | 
						|
 | 
						|
		#include "colamd.h"
 | 
						|
		#define ALEN 100
 | 
						|
		int A [ALEN] = {0, 1, 4, 2, 4, 0, 1, 2, 3, 1, 3} ;
 | 
						|
		int p [ ] = {0, 3, 5, 9, 11} ;
 | 
						|
		int stats [COLAMD_STATS] ;
 | 
						|
		colamd (5, 4, ALEN, A, p, (double *) NULL, stats) ;
 | 
						|
 | 
						|
	    The permutation is returned in the array p, and A is destroyed.
 | 
						|
 | 
						|
    ----------------------------------------------------------------------------
 | 
						|
    symamd:
 | 
						|
    ----------------------------------------------------------------------------
 | 
						|
 | 
						|
	C syntax:
 | 
						|
 | 
						|
	    #include "colamd.h"
 | 
						|
	    int symamd (int n, int *A, int *p, int *perm,
 | 
						|
	    	double knobs [COLAMD_KNOBS], int stats [COLAMD_STATS],
 | 
						|
		void (*allocate) (size_t, size_t), void (*release) (void *)) ;
 | 
						|
	    UF_long symamd_l (UF_long n, UF_long *A, UF_long *p, UF_long *perm,
 | 
						|
	    	double knobs [COLAMD_KNOBS], UF_long stats [COLAMD_STATS],
 | 
						|
		void (*allocate) (size_t, size_t), void (*release) (void *)) ;
 | 
						|
 | 
						|
	Purpose:
 | 
						|
 | 
						|
    	    The symamd routine computes an ordering P of a symmetric sparse
 | 
						|
	    matrix A such that the Cholesky factorization PAP' = LL' remains
 | 
						|
	    sparse.  It is based on a column ordering of a matrix M constructed
 | 
						|
	    so that the nonzero pattern of M'M is the same as A.  The matrix A
 | 
						|
	    is assumed to be symmetric; only the strictly lower triangular part
 | 
						|
	    is accessed.  You must pass your selected memory allocator (usually
 | 
						|
	    calloc/free or mxCalloc/mxFree) to symamd, for it to allocate
 | 
						|
	    memory for the temporary matrix M.
 | 
						|
 | 
						|
	Returns:
 | 
						|
 | 
						|
	    TRUE (1) if successful, FALSE (0) otherwise.
 | 
						|
 | 
						|
	Arguments:
 | 
						|
 | 
						|
	    int n ;		Input argument.
 | 
						|
 | 
						|
	    	Number of rows and columns in the symmetrix matrix A.
 | 
						|
		Restriction:  n >= 0.
 | 
						|
		Symamd returns FALSE if n is negative.
 | 
						|
 | 
						|
	    int A [nnz] ;	Input argument.
 | 
						|
 | 
						|
	    	A is an integer array of size nnz, where nnz = p [n].
 | 
						|
		
 | 
						|
		The row indices of the entries in column c of the matrix are
 | 
						|
		held in A [(p [c]) ... (p [c+1]-1)].  The row indices in a
 | 
						|
		given column c need not be in ascending order, and duplicate
 | 
						|
		row indices may be present.  However, symamd will run faster
 | 
						|
		if the columns are in sorted order with no duplicate entries. 
 | 
						|
 | 
						|
		The matrix is 0-based.  That is, rows are in the range 0 to
 | 
						|
		n-1, and columns are in the range 0 to n-1.  Symamd
 | 
						|
		returns FALSE if any row index is out of range.
 | 
						|
 | 
						|
		The contents of A are not modified.
 | 
						|
 | 
						|
	    int p [n+1] ;   	Input argument.
 | 
						|
 | 
						|
		p is an integer array of size n+1.  On input, it holds the
 | 
						|
		"pointers" for the column form of the matrix A.  Column c of
 | 
						|
		the matrix A is held in A [(p [c]) ... (p [c+1]-1)].  The first
 | 
						|
		entry, p [0], must be zero, and p [c] <= p [c+1] must hold
 | 
						|
		for all c in the range 0 to n-1.  The value p [n] is
 | 
						|
		thus the total number of entries in the pattern of the matrix A.
 | 
						|
		Symamd returns FALSE if these conditions are not met.
 | 
						|
 | 
						|
		The contents of p are not modified.
 | 
						|
 | 
						|
	    int perm [n+1] ;   	Output argument.
 | 
						|
 | 
						|
		On output, if symamd returns TRUE, the array perm holds the
 | 
						|
		permutation P, where perm [0] is the first index in the new
 | 
						|
		ordering, and perm [n-1] is the last.  That is, perm [k] = j
 | 
						|
		means that row and column j of A is the kth column in PAP',
 | 
						|
		where k is in the range 0 to n-1 (perm [0] = j means
 | 
						|
		that row and column j of A are the first row and column in
 | 
						|
		PAP').  The array is used as a workspace during the ordering,
 | 
						|
		which is why it must be of length n+1, not just n.
 | 
						|
 | 
						|
	    double knobs [COLAMD_KNOBS] ;	Input argument.
 | 
						|
 | 
						|
		See colamd_set_defaults for a description.
 | 
						|
 | 
						|
	    int stats [COLAMD_STATS] ;		Output argument.
 | 
						|
 | 
						|
		Statistics on the ordering, and error status.
 | 
						|
		See colamd.h for related definitions.
 | 
						|
		Symamd returns FALSE if stats is not present.
 | 
						|
 | 
						|
		stats [0]:  number of dense or empty row and columns ignored
 | 
						|
				(and ordered last in the output permutation 
 | 
						|
				perm).  Note that a row/column can become
 | 
						|
				"empty" if it contains only "dense" and/or
 | 
						|
				"empty" columns/rows.
 | 
						|
 | 
						|
		stats [1]:  (same as stats [0])
 | 
						|
 | 
						|
		stats [2]:  number of garbage collections performed.
 | 
						|
 | 
						|
		stats [3]:  status code.  < 0 is an error code.
 | 
						|
			    > 1 is a warning or notice.
 | 
						|
 | 
						|
			0	OK.  Each column of the input matrix contained
 | 
						|
				row indices in increasing order, with no
 | 
						|
				duplicates.
 | 
						|
 | 
						|
			1	OK, but columns of input matrix were jumbled
 | 
						|
				(unsorted columns or duplicate entries).  Symamd
 | 
						|
				had to do some extra work to sort the matrix
 | 
						|
				first and remove duplicate entries, but it
 | 
						|
				still was able to return a valid permutation
 | 
						|
				(return value of symamd was TRUE).
 | 
						|
 | 
						|
					stats [4]: highest numbered column that
 | 
						|
						is unsorted or has duplicate
 | 
						|
						entries.
 | 
						|
					stats [5]: last seen duplicate or
 | 
						|
						unsorted row index.
 | 
						|
					stats [6]: number of duplicate or
 | 
						|
						unsorted row indices.
 | 
						|
 | 
						|
			-1	A is a null pointer
 | 
						|
 | 
						|
			-2	p is a null pointer
 | 
						|
 | 
						|
			-3	(unused, see colamd.c)
 | 
						|
 | 
						|
			-4 	n is negative
 | 
						|
 | 
						|
					stats [4]: n
 | 
						|
 | 
						|
			-5	number of nonzeros in matrix is negative
 | 
						|
 | 
						|
					stats [4]: # of nonzeros (p [n]).
 | 
						|
 | 
						|
			-6	p [0] is nonzero
 | 
						|
 | 
						|
					stats [4]: p [0]
 | 
						|
 | 
						|
			-7	(unused)
 | 
						|
 | 
						|
			-8	a column has a negative number of entries
 | 
						|
 | 
						|
					stats [4]: column with < 0 entries
 | 
						|
					stats [5]: number of entries in col
 | 
						|
 | 
						|
			-9	a row index is out of bounds
 | 
						|
 | 
						|
					stats [4]: column with bad row index
 | 
						|
					stats [5]: bad row index
 | 
						|
					stats [6]: n_row, # of rows of matrx
 | 
						|
 | 
						|
			-10	out of memory (unable to allocate temporary
 | 
						|
				workspace for M or count arrays using the
 | 
						|
				"allocate" routine passed into symamd).
 | 
						|
 | 
						|
		Future versions may return more statistics in the stats array.
 | 
						|
 | 
						|
	    void * (*allocate) (size_t, size_t)
 | 
						|
 | 
						|
	    	A pointer to a function providing memory allocation.  The
 | 
						|
		allocated memory must be returned initialized to zero.  For a
 | 
						|
		C application, this argument should normally be a pointer to
 | 
						|
		calloc.  For a MATLAB mexFunction, the routine mxCalloc is
 | 
						|
		passed instead.
 | 
						|
 | 
						|
	    void (*release) (size_t, size_t)
 | 
						|
 | 
						|
	    	A pointer to a function that frees memory allocated by the
 | 
						|
		memory allocation routine above.  For a C application, this
 | 
						|
		argument should normally be a pointer to free.  For a MATLAB
 | 
						|
		mexFunction, the routine mxFree is passed instead.
 | 
						|
 | 
						|
 | 
						|
    ----------------------------------------------------------------------------
 | 
						|
    colamd_report:
 | 
						|
    ----------------------------------------------------------------------------
 | 
						|
 | 
						|
	C syntax:
 | 
						|
 | 
						|
	    #include "colamd.h"
 | 
						|
	    colamd_report (int stats [COLAMD_STATS]) ;
 | 
						|
	    colamd_l_report (UF_long stats [COLAMD_STATS]) ;
 | 
						|
 | 
						|
	Purpose:
 | 
						|
 | 
						|
	    Prints the error status and statistics recorded in the stats
 | 
						|
	    array on the standard error output (for a standard C routine)
 | 
						|
	    or on the MATLAB output (for a mexFunction).
 | 
						|
 | 
						|
	Arguments:
 | 
						|
 | 
						|
	    int stats [COLAMD_STATS] ;	Input only.  Statistics from colamd.
 | 
						|
 | 
						|
 | 
						|
    ----------------------------------------------------------------------------
 | 
						|
    symamd_report:
 | 
						|
    ----------------------------------------------------------------------------
 | 
						|
 | 
						|
	C syntax:
 | 
						|
 | 
						|
	    #include "colamd.h"
 | 
						|
	    symamd_report (int stats [COLAMD_STATS]) ;
 | 
						|
	    symamd_l_report (UF_long stats [COLAMD_STATS]) ;
 | 
						|
 | 
						|
	Purpose:
 | 
						|
 | 
						|
	    Prints the error status and statistics recorded in the stats
 | 
						|
	    array on the standard error output (for a standard C routine)
 | 
						|
	    or on the MATLAB output (for a mexFunction).
 | 
						|
 | 
						|
	Arguments:
 | 
						|
 | 
						|
	    int stats [COLAMD_STATS] ;	Input only.  Statistics from symamd.
 | 
						|
 | 
						|
 | 
						|
*/
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === Scaffolding code definitions  ======================================== */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/* Ensure that debugging is turned off: */
 | 
						|
#ifndef NDEBUG
 | 
						|
#define NDEBUG
 | 
						|
#endif
 | 
						|
 | 
						|
/* turn on debugging by uncommenting the following line
 | 
						|
 #undef NDEBUG
 | 
						|
*/
 | 
						|
 | 
						|
/*
 | 
						|
   Our "scaffolding code" philosophy:  In our opinion, well-written library
 | 
						|
   code should keep its "debugging" code, and just normally have it turned off
 | 
						|
   by the compiler so as not to interfere with performance.  This serves
 | 
						|
   several purposes:
 | 
						|
 | 
						|
   (1) assertions act as comments to the reader, telling you what the code
 | 
						|
	expects at that point.  All assertions will always be true (unless
 | 
						|
	there really is a bug, of course).
 | 
						|
 | 
						|
   (2) leaving in the scaffolding code assists anyone who would like to modify
 | 
						|
	the code, or understand the algorithm (by reading the debugging output,
 | 
						|
	one can get a glimpse into what the code is doing).
 | 
						|
 | 
						|
   (3) (gasp!) for actually finding bugs.  This code has been heavily tested
 | 
						|
	and "should" be fully functional and bug-free ... but you never know...
 | 
						|
 | 
						|
    The code will become outrageously slow when debugging is
 | 
						|
    enabled.  To control the level of debugging output, set an environment
 | 
						|
    variable D to 0 (little), 1 (some), 2, 3, or 4 (lots).  When debugging,
 | 
						|
    you should see the following message on the standard output:
 | 
						|
 | 
						|
    	colamd: debug version, D = 1 (THIS WILL BE SLOW!)
 | 
						|
 | 
						|
    or a similar message for symamd.  If you don't, then debugging has not
 | 
						|
    been enabled.
 | 
						|
 | 
						|
*/
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === Include files ======================================================== */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
#include "colamd.h"
 | 
						|
#include <limits.h>
 | 
						|
#include <math.h>
 | 
						|
 | 
						|
#ifdef MATLAB_MEX_FILE
 | 
						|
#include "mex.h"
 | 
						|
#include "matrix.h"
 | 
						|
#endif /* MATLAB_MEX_FILE */
 | 
						|
 | 
						|
#if !defined (NPRINT) || !defined (NDEBUG)
 | 
						|
#include <stdio.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#ifndef NULL
 | 
						|
#define NULL ((void *) 0)
 | 
						|
#endif
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === int or UF_long ======================================================= */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/* define UF_long */
 | 
						|
#include "UFconfig.h"
 | 
						|
 | 
						|
#ifdef DLONG
 | 
						|
 | 
						|
#define Int UF_long
 | 
						|
#define ID  UF_long_id
 | 
						|
#define Int_MAX UF_long_max
 | 
						|
 | 
						|
#define COLAMD_recommended colamd_l_recommended
 | 
						|
#define COLAMD_set_defaults colamd_l_set_defaults
 | 
						|
#define COLAMD_MAIN colamd_l
 | 
						|
#define SYMAMD_MAIN symamd_l
 | 
						|
#define COLAMD_report colamd_l_report
 | 
						|
#define SYMAMD_report symamd_l_report
 | 
						|
 | 
						|
#else
 | 
						|
 | 
						|
#define Int int
 | 
						|
#define ID "%d"
 | 
						|
#define Int_MAX INT_MAX
 | 
						|
 | 
						|
#define COLAMD_recommended colamd_recommended
 | 
						|
#define COLAMD_set_defaults colamd_set_defaults
 | 
						|
#define COLAMD_MAIN colamd
 | 
						|
#define SYMAMD_MAIN symamd
 | 
						|
#define COLAMD_report colamd_report
 | 
						|
#define SYMAMD_report symamd_report
 | 
						|
 | 
						|
#endif
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === Row and Column structures ============================================ */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/* User code that makes use of the colamd/symamd routines need not directly */
 | 
						|
/* reference these structures.  They are used only for colamd_recommended. */
 | 
						|
 | 
						|
typedef struct Colamd_Col_struct
 | 
						|
{
 | 
						|
    Int start ;		/* index for A of first row in this column, or DEAD */
 | 
						|
			/* if column is dead */
 | 
						|
    Int length ;	/* number of rows in this column */
 | 
						|
    union
 | 
						|
    {
 | 
						|
	Int thickness ;	/* number of original columns represented by this */
 | 
						|
			/* col, if the column is alive */
 | 
						|
	Int parent ;	/* parent in parent tree super-column structure, if */
 | 
						|
			/* the column is dead */
 | 
						|
    } shared1 ;
 | 
						|
    union
 | 
						|
    {
 | 
						|
	Int score ;	/* the score used to maintain heap, if col is alive */
 | 
						|
	Int order ;	/* pivot ordering of this column, if col is dead */
 | 
						|
    } shared2 ;
 | 
						|
    union
 | 
						|
    {
 | 
						|
	Int headhash ;	/* head of a hash bucket, if col is at the head of */
 | 
						|
			/* a degree list */
 | 
						|
	Int hash ;	/* hash value, if col is not in a degree list */
 | 
						|
	Int prev ;	/* previous column in degree list, if col is in a */
 | 
						|
			/* degree list (but not at the head of a degree list) */
 | 
						|
    } shared3 ;
 | 
						|
    union
 | 
						|
    {
 | 
						|
	Int degree_next ;	/* next column, if col is in a degree list */
 | 
						|
	Int hash_next ;		/* next column, if col is in a hash list */
 | 
						|
    } shared4 ;
 | 
						|
 | 
						|
} Colamd_Col ;
 | 
						|
 | 
						|
typedef struct Colamd_Row_struct
 | 
						|
{
 | 
						|
    Int start ;		/* index for A of first col in this row */
 | 
						|
    Int length ;	/* number of principal columns in this row */
 | 
						|
    union
 | 
						|
    {
 | 
						|
	Int degree ;	/* number of principal & non-principal columns in row */
 | 
						|
	Int p ;		/* used as a row pointer in init_rows_cols () */
 | 
						|
    } shared1 ;
 | 
						|
    union
 | 
						|
    {
 | 
						|
	Int mark ;	/* for computing set differences and marking dead rows*/
 | 
						|
	Int first_column ;/* first column in row (used in garbage collection) */
 | 
						|
    } shared2 ;
 | 
						|
 | 
						|
} Colamd_Row ;
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === Definitions ========================================================== */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/* Routines are either PUBLIC (user-callable) or PRIVATE (not user-callable) */
 | 
						|
#define PUBLIC
 | 
						|
#define PRIVATE static
 | 
						|
 | 
						|
#define DENSE_DEGREE(alpha,n) \
 | 
						|
    ((Int) MAX (16.0, (alpha) * sqrt ((double) (n))))
 | 
						|
 | 
						|
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
 | 
						|
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
 | 
						|
 | 
						|
#define ONES_COMPLEMENT(r) (-(r)-1)
 | 
						|
 | 
						|
/* -------------------------------------------------------------------------- */
 | 
						|
/* Change for version 2.1:  define TRUE and FALSE only if not yet defined */  
 | 
						|
/* -------------------------------------------------------------------------- */
 | 
						|
 | 
						|
#ifndef TRUE
 | 
						|
#define TRUE (1)
 | 
						|
#endif
 | 
						|
 | 
						|
#ifndef FALSE
 | 
						|
#define FALSE (0)
 | 
						|
#endif
 | 
						|
 | 
						|
/* -------------------------------------------------------------------------- */
 | 
						|
 | 
						|
#define EMPTY	(-1)
 | 
						|
 | 
						|
/* Row and column status */
 | 
						|
#define ALIVE	(0)
 | 
						|
#define DEAD	(-1)
 | 
						|
 | 
						|
/* Column status */
 | 
						|
#define DEAD_PRINCIPAL		(-1)
 | 
						|
#define DEAD_NON_PRINCIPAL	(-2)
 | 
						|
 | 
						|
/* Macros for row and column status update and checking. */
 | 
						|
#define ROW_IS_DEAD(r)			ROW_IS_MARKED_DEAD (Row[r].shared2.mark)
 | 
						|
#define ROW_IS_MARKED_DEAD(row_mark)	(row_mark < ALIVE)
 | 
						|
#define ROW_IS_ALIVE(r)			(Row [r].shared2.mark >= ALIVE)
 | 
						|
#define COL_IS_DEAD(c)			(Col [c].start < ALIVE)
 | 
						|
#define COL_IS_ALIVE(c)			(Col [c].start >= ALIVE)
 | 
						|
#define COL_IS_DEAD_PRINCIPAL(c)	(Col [c].start == DEAD_PRINCIPAL)
 | 
						|
#define KILL_ROW(r)			{ Row [r].shared2.mark = DEAD ; }
 | 
						|
#define KILL_PRINCIPAL_COL(c)		{ Col [c].start = DEAD_PRINCIPAL ; }
 | 
						|
#define KILL_NON_PRINCIPAL_COL(c)	{ Col [c].start = DEAD_NON_PRINCIPAL ; }
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === Colamd reporting mechanism =========================================== */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
#if defined (MATLAB_MEX_FILE) || defined (MATHWORKS)
 | 
						|
/* In MATLAB, matrices are 1-based to the user, but 0-based internally */
 | 
						|
#define INDEX(i) ((i)+1)
 | 
						|
#else
 | 
						|
/* In C, matrices are 0-based and indices are reported as such in *_report */
 | 
						|
#define INDEX(i) (i)
 | 
						|
#endif
 | 
						|
 | 
						|
/* All output goes through the PRINTF macro.  */
 | 
						|
#define PRINTF(params) { if (colamd_printf != NULL) (void) colamd_printf params ; }
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === Prototypes of PRIVATE routines ======================================= */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
PRIVATE Int init_rows_cols
 | 
						|
(
 | 
						|
    Int n_row,
 | 
						|
    Int n_col,
 | 
						|
    Colamd_Row Row [],
 | 
						|
    Colamd_Col Col [],
 | 
						|
    Int A [],
 | 
						|
    Int p [],
 | 
						|
    Int stats [COLAMD_STATS]
 | 
						|
) ;
 | 
						|
 | 
						|
PRIVATE void init_scoring
 | 
						|
(
 | 
						|
    Int n_row,
 | 
						|
    Int n_col,
 | 
						|
    Colamd_Row Row [],
 | 
						|
    Colamd_Col Col [],
 | 
						|
    Int A [],
 | 
						|
    Int head [],
 | 
						|
    double knobs [COLAMD_KNOBS],
 | 
						|
    Int *p_n_row2,
 | 
						|
    Int *p_n_col2,
 | 
						|
    Int *p_max_deg
 | 
						|
) ;
 | 
						|
 | 
						|
PRIVATE Int find_ordering
 | 
						|
(
 | 
						|
    Int n_row,
 | 
						|
    Int n_col,
 | 
						|
    Int Alen,
 | 
						|
    Colamd_Row Row [],
 | 
						|
    Colamd_Col Col [],
 | 
						|
    Int A [],
 | 
						|
    Int head [],
 | 
						|
    Int n_col2,
 | 
						|
    Int max_deg,
 | 
						|
    Int pfree,
 | 
						|
    Int aggressive
 | 
						|
) ;
 | 
						|
 | 
						|
PRIVATE void order_children
 | 
						|
(
 | 
						|
    Int n_col,
 | 
						|
    Colamd_Col Col [],
 | 
						|
    Int p []
 | 
						|
) ;
 | 
						|
 | 
						|
PRIVATE void detect_super_cols
 | 
						|
(
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
    Int n_col,
 | 
						|
    Colamd_Row Row [],
 | 
						|
#endif /* NDEBUG */
 | 
						|
 | 
						|
    Colamd_Col Col [],
 | 
						|
    Int A [],
 | 
						|
    Int head [],
 | 
						|
    Int row_start,
 | 
						|
    Int row_length
 | 
						|
) ;
 | 
						|
 | 
						|
PRIVATE Int garbage_collection
 | 
						|
(
 | 
						|
    Int n_row,
 | 
						|
    Int n_col,
 | 
						|
    Colamd_Row Row [],
 | 
						|
    Colamd_Col Col [],
 | 
						|
    Int A [],
 | 
						|
    Int *pfree
 | 
						|
) ;
 | 
						|
 | 
						|
PRIVATE Int clear_mark
 | 
						|
(
 | 
						|
    Int tag_mark,
 | 
						|
    Int max_mark,
 | 
						|
    Int n_row,
 | 
						|
    Colamd_Row Row []
 | 
						|
) ;
 | 
						|
 | 
						|
PRIVATE void print_report
 | 
						|
(
 | 
						|
    char *method,
 | 
						|
    Int stats [COLAMD_STATS]
 | 
						|
) ;
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === Debugging prototypes and definitions ================================= */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
 | 
						|
#include <assert.h>
 | 
						|
 | 
						|
/* colamd_debug is the *ONLY* global variable, and is only */
 | 
						|
/* present when debugging */
 | 
						|
 | 
						|
PRIVATE Int colamd_debug = 0 ;	/* debug print level */
 | 
						|
 | 
						|
#define DEBUG0(params) { PRINTF (params) ; }
 | 
						|
#define DEBUG1(params) { if (colamd_debug >= 1) PRINTF (params) ; }
 | 
						|
#define DEBUG2(params) { if (colamd_debug >= 2) PRINTF (params) ; }
 | 
						|
#define DEBUG3(params) { if (colamd_debug >= 3) PRINTF (params) ; }
 | 
						|
#define DEBUG4(params) { if (colamd_debug >= 4) PRINTF (params) ; }
 | 
						|
 | 
						|
#ifdef MATLAB_MEX_FILE
 | 
						|
#define ASSERT(expression) (mxAssert ((expression), ""))
 | 
						|
#else
 | 
						|
#define ASSERT(expression) (assert (expression))
 | 
						|
#endif /* MATLAB_MEX_FILE */
 | 
						|
 | 
						|
PRIVATE void colamd_get_debug	/* gets the debug print level from getenv */
 | 
						|
(
 | 
						|
    char *method
 | 
						|
) ;
 | 
						|
 | 
						|
PRIVATE void debug_deg_lists
 | 
						|
(
 | 
						|
    Int n_row,
 | 
						|
    Int n_col,
 | 
						|
    Colamd_Row Row [],
 | 
						|
    Colamd_Col Col [],
 | 
						|
    Int head [],
 | 
						|
    Int min_score,
 | 
						|
    Int should,
 | 
						|
    Int max_deg
 | 
						|
) ;
 | 
						|
 | 
						|
PRIVATE void debug_mark
 | 
						|
(
 | 
						|
    Int n_row,
 | 
						|
    Colamd_Row Row [],
 | 
						|
    Int tag_mark,
 | 
						|
    Int max_mark
 | 
						|
) ;
 | 
						|
 | 
						|
PRIVATE void debug_matrix
 | 
						|
(
 | 
						|
    Int n_row,
 | 
						|
    Int n_col,
 | 
						|
    Colamd_Row Row [],
 | 
						|
    Colamd_Col Col [],
 | 
						|
    Int A []
 | 
						|
) ;
 | 
						|
 | 
						|
PRIVATE void debug_structures
 | 
						|
(
 | 
						|
    Int n_row,
 | 
						|
    Int n_col,
 | 
						|
    Colamd_Row Row [],
 | 
						|
    Colamd_Col Col [],
 | 
						|
    Int A [],
 | 
						|
    Int n_col2
 | 
						|
) ;
 | 
						|
 | 
						|
#else /* NDEBUG */
 | 
						|
 | 
						|
/* === No debugging ========================================================= */
 | 
						|
 | 
						|
#define DEBUG0(params) ;
 | 
						|
#define DEBUG1(params) ;
 | 
						|
#define DEBUG2(params) ;
 | 
						|
#define DEBUG3(params) ;
 | 
						|
#define DEBUG4(params) ;
 | 
						|
 | 
						|
#define ASSERT(expression)
 | 
						|
 | 
						|
#endif /* NDEBUG */
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === USER-CALLABLE ROUTINES: ============================================== */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === colamd_recommended =================================================== */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/*
 | 
						|
    The colamd_recommended routine returns the suggested size for Alen.  This
 | 
						|
    value has been determined to provide good balance between the number of
 | 
						|
    garbage collections and the memory requirements for colamd.  If any
 | 
						|
    argument is negative, or if integer overflow occurs, a 0 is returned as an
 | 
						|
    error condition.  2*nnz space is required for the row and column
 | 
						|
    indices of the matrix. COLAMD_C (n_col) + COLAMD_R (n_row) space is
 | 
						|
    required for the Col and Row arrays, respectively, which are internal to
 | 
						|
    colamd (roughly 6*n_col + 4*n_row).  An additional n_col space is the
 | 
						|
    minimal amount of "elbow room", and nnz/5 more space is recommended for
 | 
						|
    run time efficiency.
 | 
						|
 | 
						|
    Alen is approximately 2.2*nnz + 7*n_col + 4*n_row + 10.
 | 
						|
 | 
						|
    This function is not needed when using symamd.
 | 
						|
*/
 | 
						|
 | 
						|
/* add two values of type size_t, and check for integer overflow */
 | 
						|
static size_t t_add (size_t a, size_t b, int *ok)
 | 
						|
{
 | 
						|
    (*ok) = (*ok) && ((a + b) >= MAX (a,b)) ;
 | 
						|
    return ((*ok) ? (a + b) : 0) ;
 | 
						|
}
 | 
						|
 | 
						|
/* compute a*k where k is a small integer, and check for integer overflow */
 | 
						|
static size_t t_mult (size_t a, size_t k, int *ok)
 | 
						|
{
 | 
						|
    size_t i, s = 0 ;
 | 
						|
    for (i = 0 ; i < k ; i++)
 | 
						|
    {
 | 
						|
	s = t_add (s, a, ok) ;
 | 
						|
    }
 | 
						|
    return (s) ;
 | 
						|
}
 | 
						|
 | 
						|
/* size of the Col and Row structures */
 | 
						|
#define COLAMD_C(n_col,ok) \
 | 
						|
    ((t_mult (t_add (n_col, 1, ok), sizeof (Colamd_Col), ok) / sizeof (Int)))
 | 
						|
 | 
						|
#define COLAMD_R(n_row,ok) \
 | 
						|
    ((t_mult (t_add (n_row, 1, ok), sizeof (Colamd_Row), ok) / sizeof (Int)))
 | 
						|
 | 
						|
 | 
						|
PUBLIC size_t COLAMD_recommended	/* returns recommended value of Alen. */
 | 
						|
(
 | 
						|
    /* === Parameters ======================================================= */
 | 
						|
 | 
						|
    Int nnz,			/* number of nonzeros in A */
 | 
						|
    Int n_row,			/* number of rows in A */
 | 
						|
    Int n_col			/* number of columns in A */
 | 
						|
)
 | 
						|
{
 | 
						|
    size_t s, c, r ;
 | 
						|
    int ok = TRUE ;
 | 
						|
    if (nnz < 0 || n_row < 0 || n_col < 0)
 | 
						|
    {
 | 
						|
	return (0) ;
 | 
						|
    }
 | 
						|
    s = t_mult (nnz, 2, &ok) ;	    /* 2*nnz */
 | 
						|
    c = COLAMD_C (n_col, &ok) ;	    /* size of column structures */
 | 
						|
    r = COLAMD_R (n_row, &ok) ;	    /* size of row structures */
 | 
						|
    s = t_add (s, c, &ok) ;
 | 
						|
    s = t_add (s, r, &ok) ;
 | 
						|
    s = t_add (s, n_col, &ok) ;	    /* elbow room */
 | 
						|
    s = t_add (s, nnz/5, &ok) ;	    /* elbow room */
 | 
						|
    ok = ok && (s < Int_MAX) ;
 | 
						|
    return (ok ? s : 0) ;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === colamd_set_defaults ================================================== */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/*
 | 
						|
    The colamd_set_defaults routine sets the default values of the user-
 | 
						|
    controllable parameters for colamd and symamd:
 | 
						|
 | 
						|
	Colamd: rows with more than max (16, knobs [0] * sqrt (n_col))
 | 
						|
	entries are removed prior to ordering.  Columns with more than
 | 
						|
	max (16, knobs [1] * sqrt (MIN (n_row,n_col))) entries are removed
 | 
						|
	prior to ordering, and placed last in the output column ordering. 
 | 
						|
 | 
						|
	Symamd: Rows and columns with more than max (16, knobs [0] * sqrt (n))
 | 
						|
	entries are removed prior to ordering, and placed last in the
 | 
						|
	output ordering.
 | 
						|
 | 
						|
	knobs [0]	dense row control
 | 
						|
 | 
						|
	knobs [1]	dense column control
 | 
						|
 | 
						|
	knobs [2]	if nonzero, do aggresive absorption
 | 
						|
 | 
						|
	knobs [3..19]	unused, but future versions might use this
 | 
						|
 | 
						|
*/
 | 
						|
 | 
						|
PUBLIC void COLAMD_set_defaults
 | 
						|
(
 | 
						|
    /* === Parameters ======================================================= */
 | 
						|
 | 
						|
    double knobs [COLAMD_KNOBS]		/* knob array */
 | 
						|
)
 | 
						|
{
 | 
						|
    /* === Local variables ================================================== */
 | 
						|
 | 
						|
    Int i ;
 | 
						|
 | 
						|
    if (!knobs)
 | 
						|
    {
 | 
						|
	return ;			/* no knobs to initialize */
 | 
						|
    }
 | 
						|
    for (i = 0 ; i < COLAMD_KNOBS ; i++)
 | 
						|
    {
 | 
						|
	knobs [i] = 0 ;
 | 
						|
    }
 | 
						|
    knobs [COLAMD_DENSE_ROW] = 10 ;
 | 
						|
    knobs [COLAMD_DENSE_COL] = 10 ;
 | 
						|
    knobs [COLAMD_AGGRESSIVE] = TRUE ;	/* default: do aggressive absorption*/
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === symamd =============================================================== */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
PUBLIC Int SYMAMD_MAIN			/* return TRUE if OK, FALSE otherwise */
 | 
						|
(
 | 
						|
    /* === Parameters ======================================================= */
 | 
						|
 | 
						|
    Int n,				/* number of rows and columns of A */
 | 
						|
    Int A [],				/* row indices of A */
 | 
						|
    Int p [],				/* column pointers of A */
 | 
						|
    Int perm [],			/* output permutation, size n+1 */
 | 
						|
    double knobs [COLAMD_KNOBS],	/* parameters (uses defaults if NULL) */
 | 
						|
    Int stats [COLAMD_STATS],		/* output statistics and error codes */
 | 
						|
    void * (*allocate) (size_t, size_t),
 | 
						|
    					/* pointer to calloc (ANSI C) or */
 | 
						|
					/* mxCalloc (for MATLAB mexFunction) */
 | 
						|
    void (*release) (void *)
 | 
						|
    					/* pointer to free (ANSI C) or */
 | 
						|
    					/* mxFree (for MATLAB mexFunction) */
 | 
						|
)
 | 
						|
{
 | 
						|
    /* === Local variables ================================================== */
 | 
						|
 | 
						|
    Int *count ;		/* length of each column of M, and col pointer*/
 | 
						|
    Int *mark ;			/* mark array for finding duplicate entries */
 | 
						|
    Int *M ;			/* row indices of matrix M */
 | 
						|
    size_t Mlen ;		/* length of M */
 | 
						|
    Int n_row ;			/* number of rows in M */
 | 
						|
    Int nnz ;			/* number of entries in A */
 | 
						|
    Int i ;			/* row index of A */
 | 
						|
    Int j ;			/* column index of A */
 | 
						|
    Int k ;			/* row index of M */ 
 | 
						|
    Int mnz ;			/* number of nonzeros in M */
 | 
						|
    Int pp ;			/* index into a column of A */
 | 
						|
    Int last_row ;		/* last row seen in the current column */
 | 
						|
    Int length ;		/* number of nonzeros in a column */
 | 
						|
 | 
						|
    double cknobs [COLAMD_KNOBS] ;		/* knobs for colamd */
 | 
						|
    double default_knobs [COLAMD_KNOBS] ;	/* default knobs for colamd */
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
    colamd_get_debug ("symamd") ;
 | 
						|
#endif /* NDEBUG */
 | 
						|
 | 
						|
    /* === Check the input arguments ======================================== */
 | 
						|
 | 
						|
    if (!stats)
 | 
						|
    {
 | 
						|
	DEBUG0 (("symamd: stats not present\n")) ;
 | 
						|
	return (FALSE) ;
 | 
						|
    }
 | 
						|
    for (i = 0 ; i < COLAMD_STATS ; i++)
 | 
						|
    {
 | 
						|
	stats [i] = 0 ;
 | 
						|
    }
 | 
						|
    stats [COLAMD_STATUS] = COLAMD_OK ;
 | 
						|
    stats [COLAMD_INFO1] = -1 ;
 | 
						|
    stats [COLAMD_INFO2] = -1 ;
 | 
						|
 | 
						|
    if (!A)
 | 
						|
    {
 | 
						|
    	stats [COLAMD_STATUS] = COLAMD_ERROR_A_not_present ;
 | 
						|
	DEBUG0 (("symamd: A not present\n")) ;
 | 
						|
	return (FALSE) ;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!p)		/* p is not present */
 | 
						|
    {
 | 
						|
	stats [COLAMD_STATUS] = COLAMD_ERROR_p_not_present ;
 | 
						|
	DEBUG0 (("symamd: p not present\n")) ;
 | 
						|
    	return (FALSE) ;
 | 
						|
    }
 | 
						|
 | 
						|
    if (n < 0)		/* n must be >= 0 */
 | 
						|
    {
 | 
						|
	stats [COLAMD_STATUS] = COLAMD_ERROR_ncol_negative ;
 | 
						|
	stats [COLAMD_INFO1] = n ;
 | 
						|
	DEBUG0 (("symamd: n negative %d\n", n)) ;
 | 
						|
    	return (FALSE) ;
 | 
						|
    }
 | 
						|
 | 
						|
    nnz = p [n] ;
 | 
						|
    if (nnz < 0)	/* nnz must be >= 0 */
 | 
						|
    {
 | 
						|
	stats [COLAMD_STATUS] = COLAMD_ERROR_nnz_negative ;
 | 
						|
	stats [COLAMD_INFO1] = nnz ;
 | 
						|
	DEBUG0 (("symamd: number of entries negative %d\n", nnz)) ;
 | 
						|
	return (FALSE) ;
 | 
						|
    }
 | 
						|
 | 
						|
    if (p [0] != 0)
 | 
						|
    {
 | 
						|
	stats [COLAMD_STATUS] = COLAMD_ERROR_p0_nonzero ;
 | 
						|
	stats [COLAMD_INFO1] = p [0] ;
 | 
						|
	DEBUG0 (("symamd: p[0] not zero %d\n", p [0])) ;
 | 
						|
	return (FALSE) ;
 | 
						|
    }
 | 
						|
 | 
						|
    /* === If no knobs, set default knobs =================================== */
 | 
						|
 | 
						|
    if (!knobs)
 | 
						|
    {
 | 
						|
	COLAMD_set_defaults (default_knobs) ;
 | 
						|
	knobs = default_knobs ;
 | 
						|
    }
 | 
						|
 | 
						|
    /* === Allocate count and mark ========================================== */
 | 
						|
 | 
						|
    count = (Int *) ((*allocate) (n+1, sizeof (Int))) ;
 | 
						|
    if (!count)
 | 
						|
    {
 | 
						|
	stats [COLAMD_STATUS] = COLAMD_ERROR_out_of_memory ;
 | 
						|
	DEBUG0 (("symamd: allocate count (size %d) failed\n", n+1)) ;
 | 
						|
	return (FALSE) ;
 | 
						|
    }
 | 
						|
 | 
						|
    mark = (Int *) ((*allocate) (n+1, sizeof (Int))) ;
 | 
						|
    if (!mark)
 | 
						|
    {
 | 
						|
	stats [COLAMD_STATUS] = COLAMD_ERROR_out_of_memory ;
 | 
						|
	(*release) ((void *) count) ;
 | 
						|
	DEBUG0 (("symamd: allocate mark (size %d) failed\n", n+1)) ;
 | 
						|
	return (FALSE) ;
 | 
						|
    }
 | 
						|
 | 
						|
    /* === Compute column counts of M, check if A is valid ================== */
 | 
						|
 | 
						|
    stats [COLAMD_INFO3] = 0 ;  /* number of duplicate or unsorted row indices*/
 | 
						|
 | 
						|
    for (i = 0 ; i < n ; i++)
 | 
						|
    {
 | 
						|
    	mark [i] = -1 ;
 | 
						|
    }
 | 
						|
 | 
						|
    for (j = 0 ; j < n ; j++)
 | 
						|
    {
 | 
						|
	last_row = -1 ;
 | 
						|
 | 
						|
	length = p [j+1] - p [j] ;
 | 
						|
	if (length < 0)
 | 
						|
	{
 | 
						|
	    /* column pointers must be non-decreasing */
 | 
						|
	    stats [COLAMD_STATUS] = COLAMD_ERROR_col_length_negative ;
 | 
						|
	    stats [COLAMD_INFO1] = j ;
 | 
						|
	    stats [COLAMD_INFO2] = length ;
 | 
						|
	    (*release) ((void *) count) ;
 | 
						|
	    (*release) ((void *) mark) ;
 | 
						|
	    DEBUG0 (("symamd: col %d negative length %d\n", j, length)) ;
 | 
						|
	    return (FALSE) ;
 | 
						|
	}
 | 
						|
 | 
						|
	for (pp = p [j] ; pp < p [j+1] ; pp++)
 | 
						|
	{
 | 
						|
	    i = A [pp] ;
 | 
						|
	    if (i < 0 || i >= n)
 | 
						|
	    {
 | 
						|
		/* row index i, in column j, is out of bounds */
 | 
						|
		stats [COLAMD_STATUS] = COLAMD_ERROR_row_index_out_of_bounds ;
 | 
						|
		stats [COLAMD_INFO1] = j ;
 | 
						|
		stats [COLAMD_INFO2] = i ;
 | 
						|
		stats [COLAMD_INFO3] = n ;
 | 
						|
		(*release) ((void *) count) ;
 | 
						|
		(*release) ((void *) mark) ;
 | 
						|
		DEBUG0 (("symamd: row %d col %d out of bounds\n", i, j)) ;
 | 
						|
		return (FALSE) ;
 | 
						|
	    }
 | 
						|
 | 
						|
	    if (i <= last_row || mark [i] == j)
 | 
						|
	    {
 | 
						|
		/* row index is unsorted or repeated (or both), thus col */
 | 
						|
		/* is jumbled.  This is a notice, not an error condition. */
 | 
						|
		stats [COLAMD_STATUS] = COLAMD_OK_BUT_JUMBLED ;
 | 
						|
		stats [COLAMD_INFO1] = j ;
 | 
						|
		stats [COLAMD_INFO2] = i ;
 | 
						|
		(stats [COLAMD_INFO3]) ++ ;
 | 
						|
		DEBUG1 (("symamd: row %d col %d unsorted/duplicate\n", i, j)) ;
 | 
						|
	    }
 | 
						|
 | 
						|
	    if (i > j && mark [i] != j)
 | 
						|
	    {
 | 
						|
		/* row k of M will contain column indices i and j */
 | 
						|
		count [i]++ ;
 | 
						|
		count [j]++ ;
 | 
						|
	    }
 | 
						|
 | 
						|
	    /* mark the row as having been seen in this column */
 | 
						|
	    mark [i] = j ;
 | 
						|
 | 
						|
	    last_row = i ;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    /* v2.4: removed free(mark) */
 | 
						|
 | 
						|
    /* === Compute column pointers of M ===================================== */
 | 
						|
 | 
						|
    /* use output permutation, perm, for column pointers of M */
 | 
						|
    perm [0] = 0 ;
 | 
						|
    for (j = 1 ; j <= n ; j++)
 | 
						|
    {
 | 
						|
	perm [j] = perm [j-1] + count [j-1] ;
 | 
						|
    }
 | 
						|
    for (j = 0 ; j < n ; j++)
 | 
						|
    {
 | 
						|
	count [j] = perm [j] ;
 | 
						|
    }
 | 
						|
 | 
						|
    /* === Construct M ====================================================== */
 | 
						|
 | 
						|
    mnz = perm [n] ;
 | 
						|
    n_row = mnz / 2 ;
 | 
						|
    Mlen = COLAMD_recommended (mnz, n_row, n) ;
 | 
						|
    M = (Int *) ((*allocate) (Mlen, sizeof (Int))) ;
 | 
						|
    DEBUG0 (("symamd: M is %d-by-%d with %d entries, Mlen = %g\n",
 | 
						|
    	n_row, n, mnz, (double) Mlen)) ;
 | 
						|
 | 
						|
    if (!M)
 | 
						|
    {
 | 
						|
	stats [COLAMD_STATUS] = COLAMD_ERROR_out_of_memory ;
 | 
						|
	(*release) ((void *) count) ;
 | 
						|
	(*release) ((void *) mark) ;
 | 
						|
	DEBUG0 (("symamd: allocate M (size %g) failed\n", (double) Mlen)) ;
 | 
						|
	return (FALSE) ;
 | 
						|
    }
 | 
						|
 | 
						|
    k = 0 ;
 | 
						|
 | 
						|
    if (stats [COLAMD_STATUS] == COLAMD_OK)
 | 
						|
    {
 | 
						|
	/* Matrix is OK */
 | 
						|
	for (j = 0 ; j < n ; j++)
 | 
						|
	{
 | 
						|
	    ASSERT (p [j+1] - p [j] >= 0) ;
 | 
						|
	    for (pp = p [j] ; pp < p [j+1] ; pp++)
 | 
						|
	    {
 | 
						|
		i = A [pp] ;
 | 
						|
		ASSERT (i >= 0 && i < n) ;
 | 
						|
		if (i > j)
 | 
						|
		{
 | 
						|
		    /* row k of M contains column indices i and j */
 | 
						|
		    M [count [i]++] = k ;
 | 
						|
		    M [count [j]++] = k ;
 | 
						|
		    k++ ;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
	/* Matrix is jumbled.  Do not add duplicates to M.  Unsorted cols OK. */
 | 
						|
	DEBUG0 (("symamd: Duplicates in A.\n")) ;
 | 
						|
	for (i = 0 ; i < n ; i++)
 | 
						|
	{
 | 
						|
	    mark [i] = -1 ;
 | 
						|
	}
 | 
						|
	for (j = 0 ; j < n ; j++)
 | 
						|
	{
 | 
						|
	    ASSERT (p [j+1] - p [j] >= 0) ;
 | 
						|
	    for (pp = p [j] ; pp < p [j+1] ; pp++)
 | 
						|
	    {
 | 
						|
		i = A [pp] ;
 | 
						|
		ASSERT (i >= 0 && i < n) ;
 | 
						|
		if (i > j && mark [i] != j)
 | 
						|
		{
 | 
						|
		    /* row k of M contains column indices i and j */
 | 
						|
		    M [count [i]++] = k ;
 | 
						|
		    M [count [j]++] = k ;
 | 
						|
		    k++ ;
 | 
						|
		    mark [i] = j ;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	/* v2.4: free(mark) moved below */
 | 
						|
    }
 | 
						|
 | 
						|
    /* count and mark no longer needed */
 | 
						|
    (*release) ((void *) count) ;
 | 
						|
    (*release) ((void *) mark) ;	/* v2.4: free (mark) moved here */
 | 
						|
    ASSERT (k == n_row) ;
 | 
						|
 | 
						|
    /* === Adjust the knobs for M =========================================== */
 | 
						|
 | 
						|
    for (i = 0 ; i < COLAMD_KNOBS ; i++)
 | 
						|
    {
 | 
						|
	cknobs [i] = knobs [i] ;
 | 
						|
    }
 | 
						|
 | 
						|
    /* there are no dense rows in M */
 | 
						|
    cknobs [COLAMD_DENSE_ROW] = -1 ;
 | 
						|
    cknobs [COLAMD_DENSE_COL] = knobs [COLAMD_DENSE_ROW] ;
 | 
						|
 | 
						|
    /* === Order the columns of M =========================================== */
 | 
						|
 | 
						|
    /* v2.4: colamd cannot fail here, so the error check is removed */
 | 
						|
    (void) COLAMD_MAIN (n_row, n, (Int) Mlen, M, perm, cknobs, stats) ;
 | 
						|
 | 
						|
    /* Note that the output permutation is now in perm */
 | 
						|
 | 
						|
    /* === get the statistics for symamd from colamd ======================== */
 | 
						|
 | 
						|
    /* a dense column in colamd means a dense row and col in symamd */
 | 
						|
    stats [COLAMD_DENSE_ROW] = stats [COLAMD_DENSE_COL] ;
 | 
						|
 | 
						|
    /* === Free M =========================================================== */
 | 
						|
 | 
						|
    (*release) ((void *) M) ;
 | 
						|
    DEBUG0 (("symamd: done.\n")) ;
 | 
						|
    return (TRUE) ;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === colamd =============================================================== */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/*
 | 
						|
    The colamd routine computes a column ordering Q of a sparse matrix
 | 
						|
    A such that the LU factorization P(AQ) = LU remains sparse, where P is
 | 
						|
    selected via partial pivoting.   The routine can also be viewed as
 | 
						|
    providing a permutation Q such that the Cholesky factorization
 | 
						|
    (AQ)'(AQ) = LL' remains sparse.
 | 
						|
*/
 | 
						|
 | 
						|
PUBLIC Int COLAMD_MAIN		/* returns TRUE if successful, FALSE otherwise*/
 | 
						|
(
 | 
						|
    /* === Parameters ======================================================= */
 | 
						|
 | 
						|
    Int n_row,			/* number of rows in A */
 | 
						|
    Int n_col,			/* number of columns in A */
 | 
						|
    Int Alen,			/* length of A */
 | 
						|
    Int A [],			/* row indices of A */
 | 
						|
    Int p [],			/* pointers to columns in A */
 | 
						|
    double knobs [COLAMD_KNOBS],/* parameters (uses defaults if NULL) */
 | 
						|
    Int stats [COLAMD_STATS]	/* output statistics and error codes */
 | 
						|
)
 | 
						|
{
 | 
						|
    /* === Local variables ================================================== */
 | 
						|
 | 
						|
    Int i ;			/* loop index */
 | 
						|
    Int nnz ;			/* nonzeros in A */
 | 
						|
    size_t Row_size ;		/* size of Row [], in integers */
 | 
						|
    size_t Col_size ;		/* size of Col [], in integers */
 | 
						|
    size_t need ;		/* minimum required length of A */
 | 
						|
    Colamd_Row *Row ;		/* pointer into A of Row [0..n_row] array */
 | 
						|
    Colamd_Col *Col ;		/* pointer into A of Col [0..n_col] array */
 | 
						|
    Int n_col2 ;		/* number of non-dense, non-empty columns */
 | 
						|
    Int n_row2 ;		/* number of non-dense, non-empty rows */
 | 
						|
    Int ngarbage ;		/* number of garbage collections performed */
 | 
						|
    Int max_deg ;		/* maximum row degree */
 | 
						|
    double default_knobs [COLAMD_KNOBS] ;	/* default knobs array */
 | 
						|
    Int aggressive ;		/* do aggressive absorption */
 | 
						|
    int ok ;
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
    colamd_get_debug ("colamd") ;
 | 
						|
#endif /* NDEBUG */
 | 
						|
 | 
						|
    /* === Check the input arguments ======================================== */
 | 
						|
 | 
						|
    if (!stats)
 | 
						|
    {
 | 
						|
	DEBUG0 (("colamd: stats not present\n")) ;
 | 
						|
	return (FALSE) ;
 | 
						|
    }
 | 
						|
    for (i = 0 ; i < COLAMD_STATS ; i++)
 | 
						|
    {
 | 
						|
	stats [i] = 0 ;
 | 
						|
    }
 | 
						|
    stats [COLAMD_STATUS] = COLAMD_OK ;
 | 
						|
    stats [COLAMD_INFO1] = -1 ;
 | 
						|
    stats [COLAMD_INFO2] = -1 ;
 | 
						|
 | 
						|
    if (!A)		/* A is not present */
 | 
						|
    {
 | 
						|
	stats [COLAMD_STATUS] = COLAMD_ERROR_A_not_present ;
 | 
						|
	DEBUG0 (("colamd: A not present\n")) ;
 | 
						|
	return (FALSE) ;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!p)		/* p is not present */
 | 
						|
    {
 | 
						|
	stats [COLAMD_STATUS] = COLAMD_ERROR_p_not_present ;
 | 
						|
	DEBUG0 (("colamd: p not present\n")) ;
 | 
						|
    	return (FALSE) ;
 | 
						|
    }
 | 
						|
 | 
						|
    if (n_row < 0)	/* n_row must be >= 0 */
 | 
						|
    {
 | 
						|
	stats [COLAMD_STATUS] = COLAMD_ERROR_nrow_negative ;
 | 
						|
	stats [COLAMD_INFO1] = n_row ;
 | 
						|
	DEBUG0 (("colamd: nrow negative %d\n", n_row)) ;
 | 
						|
    	return (FALSE) ;
 | 
						|
    }
 | 
						|
 | 
						|
    if (n_col < 0)	/* n_col must be >= 0 */
 | 
						|
    {
 | 
						|
	stats [COLAMD_STATUS] = COLAMD_ERROR_ncol_negative ;
 | 
						|
	stats [COLAMD_INFO1] = n_col ;
 | 
						|
	DEBUG0 (("colamd: ncol negative %d\n", n_col)) ;
 | 
						|
    	return (FALSE) ;
 | 
						|
    }
 | 
						|
 | 
						|
    nnz = p [n_col] ;
 | 
						|
    if (nnz < 0)	/* nnz must be >= 0 */
 | 
						|
    {
 | 
						|
	stats [COLAMD_STATUS] = COLAMD_ERROR_nnz_negative ;
 | 
						|
	stats [COLAMD_INFO1] = nnz ;
 | 
						|
	DEBUG0 (("colamd: number of entries negative %d\n", nnz)) ;
 | 
						|
	return (FALSE) ;
 | 
						|
    }
 | 
						|
 | 
						|
    if (p [0] != 0)
 | 
						|
    {
 | 
						|
	stats [COLAMD_STATUS] = COLAMD_ERROR_p0_nonzero	;
 | 
						|
	stats [COLAMD_INFO1] = p [0] ;
 | 
						|
	DEBUG0 (("colamd: p[0] not zero %d\n", p [0])) ;
 | 
						|
	return (FALSE) ;
 | 
						|
    }
 | 
						|
 | 
						|
    /* === If no knobs, set default knobs =================================== */
 | 
						|
 | 
						|
    if (!knobs)
 | 
						|
    {
 | 
						|
	COLAMD_set_defaults (default_knobs) ;
 | 
						|
	knobs = default_knobs ;
 | 
						|
    }
 | 
						|
 | 
						|
    aggressive = (knobs [COLAMD_AGGRESSIVE] != FALSE) ;
 | 
						|
 | 
						|
    /* === Allocate the Row and Col arrays from array A ===================== */
 | 
						|
 | 
						|
    ok = TRUE ;
 | 
						|
    Col_size = COLAMD_C (n_col, &ok) ;	    /* size of Col array of structs */
 | 
						|
    Row_size = COLAMD_R (n_row, &ok) ;	    /* size of Row array of structs */
 | 
						|
 | 
						|
    /* need = 2*nnz + n_col + Col_size + Row_size ; */
 | 
						|
    need = t_mult (nnz, 2, &ok) ;
 | 
						|
    need = t_add (need, n_col, &ok) ;
 | 
						|
    need = t_add (need, Col_size, &ok) ;
 | 
						|
    need = t_add (need, Row_size, &ok) ;
 | 
						|
 | 
						|
    if (!ok || need > (size_t) Alen || need > Int_MAX)
 | 
						|
    {
 | 
						|
	/* not enough space in array A to perform the ordering */
 | 
						|
	stats [COLAMD_STATUS] = COLAMD_ERROR_A_too_small ;
 | 
						|
	stats [COLAMD_INFO1] = need ;
 | 
						|
	stats [COLAMD_INFO2] = Alen ;
 | 
						|
	DEBUG0 (("colamd: Need Alen >= %d, given only Alen = %d\n", need,Alen));
 | 
						|
	return (FALSE) ;
 | 
						|
    }
 | 
						|
 | 
						|
    Alen -= Col_size + Row_size ;
 | 
						|
    Col = (Colamd_Col *) &A [Alen] ;
 | 
						|
    Row = (Colamd_Row *) &A [Alen + Col_size] ;
 | 
						|
 | 
						|
    /* === Construct the row and column data structures ===================== */
 | 
						|
 | 
						|
    if (!init_rows_cols (n_row, n_col, Row, Col, A, p, stats))
 | 
						|
    {
 | 
						|
	/* input matrix is invalid */
 | 
						|
	DEBUG0 (("colamd: Matrix invalid\n")) ;
 | 
						|
	return (FALSE) ;
 | 
						|
    }
 | 
						|
 | 
						|
    /* === Initialize scores, kill dense rows/columns ======================= */
 | 
						|
 | 
						|
    init_scoring (n_row, n_col, Row, Col, A, p, knobs,
 | 
						|
	&n_row2, &n_col2, &max_deg) ;
 | 
						|
 | 
						|
    /* === Order the supercolumns =========================================== */
 | 
						|
 | 
						|
    ngarbage = find_ordering (n_row, n_col, Alen, Row, Col, A, p,
 | 
						|
	n_col2, max_deg, 2*nnz, aggressive) ;
 | 
						|
 | 
						|
    /* === Order the non-principal columns ================================== */
 | 
						|
 | 
						|
    order_children (n_col, Col, p) ;
 | 
						|
 | 
						|
    /* === Return statistics in stats ======================================= */
 | 
						|
 | 
						|
    stats [COLAMD_DENSE_ROW] = n_row - n_row2 ;
 | 
						|
    stats [COLAMD_DENSE_COL] = n_col - n_col2 ;
 | 
						|
    stats [COLAMD_DEFRAG_COUNT] = ngarbage ;
 | 
						|
    DEBUG0 (("colamd: done.\n")) ; 
 | 
						|
    return (TRUE) ;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === colamd_report ======================================================== */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
PUBLIC void COLAMD_report
 | 
						|
(
 | 
						|
    Int stats [COLAMD_STATS]
 | 
						|
)
 | 
						|
{
 | 
						|
    print_report ("colamd", stats) ;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === symamd_report ======================================================== */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
PUBLIC void SYMAMD_report
 | 
						|
(
 | 
						|
    Int stats [COLAMD_STATS]
 | 
						|
)
 | 
						|
{
 | 
						|
    print_report ("symamd", stats) ;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === NON-USER-CALLABLE ROUTINES: ========================================== */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/* There are no user-callable routines beyond this point in the file */
 | 
						|
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === init_rows_cols ======================================================= */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/*
 | 
						|
    Takes the column form of the matrix in A and creates the row form of the
 | 
						|
    matrix.  Also, row and column attributes are stored in the Col and Row
 | 
						|
    structs.  If the columns are un-sorted or contain duplicate row indices,
 | 
						|
    this routine will also sort and remove duplicate row indices from the
 | 
						|
    column form of the matrix.  Returns FALSE if the matrix is invalid,
 | 
						|
    TRUE otherwise.  Not user-callable.
 | 
						|
*/
 | 
						|
 | 
						|
PRIVATE Int init_rows_cols	/* returns TRUE if OK, or FALSE otherwise */
 | 
						|
(
 | 
						|
    /* === Parameters ======================================================= */
 | 
						|
 | 
						|
    Int n_row,			/* number of rows of A */
 | 
						|
    Int n_col,			/* number of columns of A */
 | 
						|
    Colamd_Row Row [],		/* of size n_row+1 */
 | 
						|
    Colamd_Col Col [],		/* of size n_col+1 */
 | 
						|
    Int A [],			/* row indices of A, of size Alen */
 | 
						|
    Int p [],			/* pointers to columns in A, of size n_col+1 */
 | 
						|
    Int stats [COLAMD_STATS]	/* colamd statistics */ 
 | 
						|
)
 | 
						|
{
 | 
						|
    /* === Local variables ================================================== */
 | 
						|
 | 
						|
    Int col ;			/* a column index */
 | 
						|
    Int row ;			/* a row index */
 | 
						|
    Int *cp ;			/* a column pointer */
 | 
						|
    Int *cp_end ;		/* a pointer to the end of a column */
 | 
						|
    Int *rp ;			/* a row pointer */
 | 
						|
    Int *rp_end ;		/* a pointer to the end of a row */
 | 
						|
    Int last_row ;		/* previous row */
 | 
						|
 | 
						|
    /* === Initialize columns, and check column pointers ==================== */
 | 
						|
 | 
						|
    for (col = 0 ; col < n_col ; col++)
 | 
						|
    {
 | 
						|
	Col [col].start = p [col] ;
 | 
						|
	Col [col].length = p [col+1] - p [col] ;
 | 
						|
 | 
						|
	if (Col [col].length < 0)
 | 
						|
	{
 | 
						|
	    /* column pointers must be non-decreasing */
 | 
						|
	    stats [COLAMD_STATUS] = COLAMD_ERROR_col_length_negative ;
 | 
						|
	    stats [COLAMD_INFO1] = col ;
 | 
						|
	    stats [COLAMD_INFO2] = Col [col].length ;
 | 
						|
	    DEBUG0 (("colamd: col %d length %d < 0\n", col, Col [col].length)) ;
 | 
						|
	    return (FALSE) ;
 | 
						|
	}
 | 
						|
 | 
						|
	Col [col].shared1.thickness = 1 ;
 | 
						|
	Col [col].shared2.score = 0 ;
 | 
						|
	Col [col].shared3.prev = EMPTY ;
 | 
						|
	Col [col].shared4.degree_next = EMPTY ;
 | 
						|
    }
 | 
						|
 | 
						|
    /* p [0..n_col] no longer needed, used as "head" in subsequent routines */
 | 
						|
 | 
						|
    /* === Scan columns, compute row degrees, and check row indices ========= */
 | 
						|
 | 
						|
    stats [COLAMD_INFO3] = 0 ;	/* number of duplicate or unsorted row indices*/
 | 
						|
 | 
						|
    for (row = 0 ; row < n_row ; row++)
 | 
						|
    {
 | 
						|
	Row [row].length = 0 ;
 | 
						|
	Row [row].shared2.mark = -1 ;
 | 
						|
    }
 | 
						|
 | 
						|
    for (col = 0 ; col < n_col ; col++)
 | 
						|
    {
 | 
						|
	last_row = -1 ;
 | 
						|
 | 
						|
	cp = &A [p [col]] ;
 | 
						|
	cp_end = &A [p [col+1]] ;
 | 
						|
 | 
						|
	while (cp < cp_end)
 | 
						|
	{
 | 
						|
	    row = *cp++ ;
 | 
						|
 | 
						|
	    /* make sure row indices within range */
 | 
						|
	    if (row < 0 || row >= n_row)
 | 
						|
	    {
 | 
						|
		stats [COLAMD_STATUS] = COLAMD_ERROR_row_index_out_of_bounds ;
 | 
						|
		stats [COLAMD_INFO1] = col ;
 | 
						|
		stats [COLAMD_INFO2] = row ;
 | 
						|
		stats [COLAMD_INFO3] = n_row ;
 | 
						|
		DEBUG0 (("colamd: row %d col %d out of bounds\n", row, col)) ;
 | 
						|
		return (FALSE) ;
 | 
						|
	    }
 | 
						|
 | 
						|
	    if (row <= last_row || Row [row].shared2.mark == col)
 | 
						|
	    {
 | 
						|
		/* row index are unsorted or repeated (or both), thus col */
 | 
						|
		/* is jumbled.  This is a notice, not an error condition. */
 | 
						|
		stats [COLAMD_STATUS] = COLAMD_OK_BUT_JUMBLED ;
 | 
						|
		stats [COLAMD_INFO1] = col ;
 | 
						|
		stats [COLAMD_INFO2] = row ;
 | 
						|
		(stats [COLAMD_INFO3]) ++ ;
 | 
						|
		DEBUG1 (("colamd: row %d col %d unsorted/duplicate\n",row,col));
 | 
						|
	    }
 | 
						|
 | 
						|
	    if (Row [row].shared2.mark != col)
 | 
						|
	    {
 | 
						|
		Row [row].length++ ;
 | 
						|
	    }
 | 
						|
	    else
 | 
						|
	    {
 | 
						|
		/* this is a repeated entry in the column, */
 | 
						|
		/* it will be removed */
 | 
						|
		Col [col].length-- ;
 | 
						|
	    }
 | 
						|
 | 
						|
	    /* mark the row as having been seen in this column */
 | 
						|
	    Row [row].shared2.mark = col ;
 | 
						|
 | 
						|
	    last_row = row ;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    /* === Compute row pointers ============================================= */
 | 
						|
 | 
						|
    /* row form of the matrix starts directly after the column */
 | 
						|
    /* form of matrix in A */
 | 
						|
    Row [0].start = p [n_col] ;
 | 
						|
    Row [0].shared1.p = Row [0].start ;
 | 
						|
    Row [0].shared2.mark = -1 ;
 | 
						|
    for (row = 1 ; row < n_row ; row++)
 | 
						|
    {
 | 
						|
	Row [row].start = Row [row-1].start + Row [row-1].length ;
 | 
						|
	Row [row].shared1.p = Row [row].start ;
 | 
						|
	Row [row].shared2.mark = -1 ;
 | 
						|
    }
 | 
						|
 | 
						|
    /* === Create row form ================================================== */
 | 
						|
 | 
						|
    if (stats [COLAMD_STATUS] == COLAMD_OK_BUT_JUMBLED)
 | 
						|
    {
 | 
						|
	/* if cols jumbled, watch for repeated row indices */
 | 
						|
	for (col = 0 ; col < n_col ; col++)
 | 
						|
	{
 | 
						|
	    cp = &A [p [col]] ;
 | 
						|
	    cp_end = &A [p [col+1]] ;
 | 
						|
	    while (cp < cp_end)
 | 
						|
	    {
 | 
						|
		row = *cp++ ;
 | 
						|
		if (Row [row].shared2.mark != col)
 | 
						|
		{
 | 
						|
		    A [(Row [row].shared1.p)++] = col ;
 | 
						|
		    Row [row].shared2.mark = col ;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
	/* if cols not jumbled, we don't need the mark (this is faster) */
 | 
						|
	for (col = 0 ; col < n_col ; col++)
 | 
						|
	{
 | 
						|
	    cp = &A [p [col]] ;
 | 
						|
	    cp_end = &A [p [col+1]] ;
 | 
						|
	    while (cp < cp_end)
 | 
						|
	    {
 | 
						|
		A [(Row [*cp++].shared1.p)++] = col ;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    /* === Clear the row marks and set row degrees ========================== */
 | 
						|
 | 
						|
    for (row = 0 ; row < n_row ; row++)
 | 
						|
    {
 | 
						|
	Row [row].shared2.mark = 0 ;
 | 
						|
	Row [row].shared1.degree = Row [row].length ;
 | 
						|
    }
 | 
						|
 | 
						|
    /* === See if we need to re-create columns ============================== */
 | 
						|
 | 
						|
    if (stats [COLAMD_STATUS] == COLAMD_OK_BUT_JUMBLED)
 | 
						|
    {
 | 
						|
    	DEBUG0 (("colamd: reconstructing column form, matrix jumbled\n")) ;
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
	/* make sure column lengths are correct */
 | 
						|
	for (col = 0 ; col < n_col ; col++)
 | 
						|
	{
 | 
						|
	    p [col] = Col [col].length ;
 | 
						|
	}
 | 
						|
	for (row = 0 ; row < n_row ; row++)
 | 
						|
	{
 | 
						|
	    rp = &A [Row [row].start] ;
 | 
						|
	    rp_end = rp + Row [row].length ;
 | 
						|
	    while (rp < rp_end)
 | 
						|
	    {
 | 
						|
		p [*rp++]-- ;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	for (col = 0 ; col < n_col ; col++)
 | 
						|
	{
 | 
						|
	    ASSERT (p [col] == 0) ;
 | 
						|
	}
 | 
						|
	/* now p is all zero (different than when debugging is turned off) */
 | 
						|
#endif /* NDEBUG */
 | 
						|
 | 
						|
	/* === Compute col pointers ========================================= */
 | 
						|
 | 
						|
	/* col form of the matrix starts at A [0]. */
 | 
						|
	/* Note, we may have a gap between the col form and the row */
 | 
						|
	/* form if there were duplicate entries, if so, it will be */
 | 
						|
	/* removed upon the first garbage collection */
 | 
						|
	Col [0].start = 0 ;
 | 
						|
	p [0] = Col [0].start ;
 | 
						|
	for (col = 1 ; col < n_col ; col++)
 | 
						|
	{
 | 
						|
	    /* note that the lengths here are for pruned columns, i.e. */
 | 
						|
	    /* no duplicate row indices will exist for these columns */
 | 
						|
	    Col [col].start = Col [col-1].start + Col [col-1].length ;
 | 
						|
	    p [col] = Col [col].start ;
 | 
						|
	}
 | 
						|
 | 
						|
	/* === Re-create col form =========================================== */
 | 
						|
 | 
						|
	for (row = 0 ; row < n_row ; row++)
 | 
						|
	{
 | 
						|
	    rp = &A [Row [row].start] ;
 | 
						|
	    rp_end = rp + Row [row].length ;
 | 
						|
	    while (rp < rp_end)
 | 
						|
	    {
 | 
						|
		A [(p [*rp++])++] = row ;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    /* === Done.  Matrix is not (or no longer) jumbled ====================== */
 | 
						|
 | 
						|
    return (TRUE) ;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === init_scoring ========================================================= */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/*
 | 
						|
    Kills dense or empty columns and rows, calculates an initial score for
 | 
						|
    each column, and places all columns in the degree lists.  Not user-callable.
 | 
						|
*/
 | 
						|
 | 
						|
PRIVATE void init_scoring
 | 
						|
(
 | 
						|
    /* === Parameters ======================================================= */
 | 
						|
 | 
						|
    Int n_row,			/* number of rows of A */
 | 
						|
    Int n_col,			/* number of columns of A */
 | 
						|
    Colamd_Row Row [],		/* of size n_row+1 */
 | 
						|
    Colamd_Col Col [],		/* of size n_col+1 */
 | 
						|
    Int A [],			/* column form and row form of A */
 | 
						|
    Int head [],		/* of size n_col+1 */
 | 
						|
    double knobs [COLAMD_KNOBS],/* parameters */
 | 
						|
    Int *p_n_row2,		/* number of non-dense, non-empty rows */
 | 
						|
    Int *p_n_col2,		/* number of non-dense, non-empty columns */
 | 
						|
    Int *p_max_deg		/* maximum row degree */
 | 
						|
)
 | 
						|
{
 | 
						|
    /* === Local variables ================================================== */
 | 
						|
 | 
						|
    Int c ;			/* a column index */
 | 
						|
    Int r, row ;		/* a row index */
 | 
						|
    Int *cp ;			/* a column pointer */
 | 
						|
    Int deg ;			/* degree of a row or column */
 | 
						|
    Int *cp_end ;		/* a pointer to the end of a column */
 | 
						|
    Int *new_cp ;		/* new column pointer */
 | 
						|
    Int col_length ;		/* length of pruned column */
 | 
						|
    Int score ;			/* current column score */
 | 
						|
    Int n_col2 ;		/* number of non-dense, non-empty columns */
 | 
						|
    Int n_row2 ;		/* number of non-dense, non-empty rows */
 | 
						|
    Int dense_row_count ;	/* remove rows with more entries than this */
 | 
						|
    Int dense_col_count ;	/* remove cols with more entries than this */
 | 
						|
    Int min_score ;		/* smallest column score */
 | 
						|
    Int max_deg ;		/* maximum row degree */
 | 
						|
    Int next_col ;		/* Used to add to degree list.*/
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
    Int debug_count ;		/* debug only. */
 | 
						|
#endif /* NDEBUG */
 | 
						|
 | 
						|
    /* === Extract knobs ==================================================== */
 | 
						|
 | 
						|
    /* Note: if knobs contains a NaN, this is undefined: */
 | 
						|
    if (knobs [COLAMD_DENSE_ROW] < 0)
 | 
						|
    {
 | 
						|
	/* only remove completely dense rows */
 | 
						|
	dense_row_count = n_col-1 ;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
	dense_row_count = DENSE_DEGREE (knobs [COLAMD_DENSE_ROW], n_col) ;
 | 
						|
    }
 | 
						|
    if (knobs [COLAMD_DENSE_COL] < 0)
 | 
						|
    {
 | 
						|
	/* only remove completely dense columns */
 | 
						|
	dense_col_count = n_row-1 ;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
	dense_col_count =
 | 
						|
	    DENSE_DEGREE (knobs [COLAMD_DENSE_COL], MIN (n_row, n_col)) ;
 | 
						|
    }
 | 
						|
 | 
						|
    DEBUG1 (("colamd: densecount: %d %d\n", dense_row_count, dense_col_count)) ;
 | 
						|
    max_deg = 0 ;
 | 
						|
    n_col2 = n_col ;
 | 
						|
    n_row2 = n_row ;
 | 
						|
 | 
						|
    /* === Kill empty columns =============================================== */
 | 
						|
 | 
						|
    /* Put the empty columns at the end in their natural order, so that LU */
 | 
						|
    /* factorization can proceed as far as possible. */
 | 
						|
    for (c = n_col-1 ; c >= 0 ; c--)
 | 
						|
    {
 | 
						|
	deg = Col [c].length ;
 | 
						|
	if (deg == 0)
 | 
						|
	{
 | 
						|
	    /* this is a empty column, kill and order it last */
 | 
						|
	    Col [c].shared2.order = --n_col2 ;
 | 
						|
	    KILL_PRINCIPAL_COL (c) ;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    DEBUG1 (("colamd: null columns killed: %d\n", n_col - n_col2)) ;
 | 
						|
 | 
						|
    /* === Kill dense columns =============================================== */
 | 
						|
 | 
						|
    /* Put the dense columns at the end, in their natural order */
 | 
						|
    for (c = n_col-1 ; c >= 0 ; c--)
 | 
						|
    {
 | 
						|
	/* skip any dead columns */
 | 
						|
	if (COL_IS_DEAD (c))
 | 
						|
	{
 | 
						|
	    continue ;
 | 
						|
	}
 | 
						|
	deg = Col [c].length ;
 | 
						|
	if (deg > dense_col_count)
 | 
						|
	{
 | 
						|
	    /* this is a dense column, kill and order it last */
 | 
						|
	    Col [c].shared2.order = --n_col2 ;
 | 
						|
	    /* decrement the row degrees */
 | 
						|
	    cp = &A [Col [c].start] ;
 | 
						|
	    cp_end = cp + Col [c].length ;
 | 
						|
	    while (cp < cp_end)
 | 
						|
	    {
 | 
						|
		Row [*cp++].shared1.degree-- ;
 | 
						|
	    }
 | 
						|
	    KILL_PRINCIPAL_COL (c) ;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    DEBUG1 (("colamd: Dense and null columns killed: %d\n", n_col - n_col2)) ;
 | 
						|
 | 
						|
    /* === Kill dense and empty rows ======================================== */
 | 
						|
 | 
						|
    for (r = 0 ; r < n_row ; r++)
 | 
						|
    {
 | 
						|
	deg = Row [r].shared1.degree ;
 | 
						|
	ASSERT (deg >= 0 && deg <= n_col) ;
 | 
						|
	if (deg > dense_row_count || deg == 0)
 | 
						|
	{
 | 
						|
	    /* kill a dense or empty row */
 | 
						|
	    KILL_ROW (r) ;
 | 
						|
	    --n_row2 ;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
	    /* keep track of max degree of remaining rows */
 | 
						|
	    max_deg = MAX (max_deg, deg) ;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    DEBUG1 (("colamd: Dense and null rows killed: %d\n", n_row - n_row2)) ;
 | 
						|
 | 
						|
    /* === Compute initial column scores ==================================== */
 | 
						|
 | 
						|
    /* At this point the row degrees are accurate.  They reflect the number */
 | 
						|
    /* of "live" (non-dense) columns in each row.  No empty rows exist. */
 | 
						|
    /* Some "live" columns may contain only dead rows, however.  These are */
 | 
						|
    /* pruned in the code below. */
 | 
						|
 | 
						|
    /* now find the initial matlab score for each column */
 | 
						|
    for (c = n_col-1 ; c >= 0 ; c--)
 | 
						|
    {
 | 
						|
	/* skip dead column */
 | 
						|
	if (COL_IS_DEAD (c))
 | 
						|
	{
 | 
						|
	    continue ;
 | 
						|
	}
 | 
						|
	score = 0 ;
 | 
						|
	cp = &A [Col [c].start] ;
 | 
						|
	new_cp = cp ;
 | 
						|
	cp_end = cp + Col [c].length ;
 | 
						|
	while (cp < cp_end)
 | 
						|
	{
 | 
						|
	    /* get a row */
 | 
						|
	    row = *cp++ ;
 | 
						|
	    /* skip if dead */
 | 
						|
	    if (ROW_IS_DEAD (row))
 | 
						|
	    {
 | 
						|
		continue ;
 | 
						|
	    }
 | 
						|
	    /* compact the column */
 | 
						|
	    *new_cp++ = row ;
 | 
						|
	    /* add row's external degree */
 | 
						|
	    score += Row [row].shared1.degree - 1 ;
 | 
						|
	    /* guard against integer overflow */
 | 
						|
	    score = MIN (score, n_col) ;
 | 
						|
	}
 | 
						|
	/* determine pruned column length */
 | 
						|
	col_length = (Int) (new_cp - &A [Col [c].start]) ;
 | 
						|
	if (col_length == 0)
 | 
						|
	{
 | 
						|
	    /* a newly-made null column (all rows in this col are "dense" */
 | 
						|
	    /* and have already been killed) */
 | 
						|
	    DEBUG2 (("Newly null killed: %d\n", c)) ;
 | 
						|
	    Col [c].shared2.order = --n_col2 ;
 | 
						|
	    KILL_PRINCIPAL_COL (c) ;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
	    /* set column length and set score */
 | 
						|
	    ASSERT (score >= 0) ;
 | 
						|
	    ASSERT (score <= n_col) ;
 | 
						|
	    Col [c].length = col_length ;
 | 
						|
	    Col [c].shared2.score = score ;
 | 
						|
	}
 | 
						|
    }
 | 
						|
    DEBUG1 (("colamd: Dense, null, and newly-null columns killed: %d\n",
 | 
						|
    	n_col-n_col2)) ;
 | 
						|
 | 
						|
    /* At this point, all empty rows and columns are dead.  All live columns */
 | 
						|
    /* are "clean" (containing no dead rows) and simplicial (no supercolumns */
 | 
						|
    /* yet).  Rows may contain dead columns, but all live rows contain at */
 | 
						|
    /* least one live column. */
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
    debug_structures (n_row, n_col, Row, Col, A, n_col2) ;
 | 
						|
#endif /* NDEBUG */
 | 
						|
 | 
						|
    /* === Initialize degree lists ========================================== */
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
    debug_count = 0 ;
 | 
						|
#endif /* NDEBUG */
 | 
						|
 | 
						|
    /* clear the hash buckets */
 | 
						|
    for (c = 0 ; c <= n_col ; c++)
 | 
						|
    {
 | 
						|
	head [c] = EMPTY ;
 | 
						|
    }
 | 
						|
    min_score = n_col ;
 | 
						|
    /* place in reverse order, so low column indices are at the front */
 | 
						|
    /* of the lists.  This is to encourage natural tie-breaking */
 | 
						|
    for (c = n_col-1 ; c >= 0 ; c--)
 | 
						|
    {
 | 
						|
	/* only add principal columns to degree lists */
 | 
						|
	if (COL_IS_ALIVE (c))
 | 
						|
	{
 | 
						|
	    DEBUG4 (("place %d score %d minscore %d ncol %d\n",
 | 
						|
		c, Col [c].shared2.score, min_score, n_col)) ;
 | 
						|
 | 
						|
	    /* === Add columns score to DList =============================== */
 | 
						|
 | 
						|
	    score = Col [c].shared2.score ;
 | 
						|
 | 
						|
	    ASSERT (min_score >= 0) ;
 | 
						|
	    ASSERT (min_score <= n_col) ;
 | 
						|
	    ASSERT (score >= 0) ;
 | 
						|
	    ASSERT (score <= n_col) ;
 | 
						|
	    ASSERT (head [score] >= EMPTY) ;
 | 
						|
 | 
						|
	    /* now add this column to dList at proper score location */
 | 
						|
	    next_col = head [score] ;
 | 
						|
	    Col [c].shared3.prev = EMPTY ;
 | 
						|
	    Col [c].shared4.degree_next = next_col ;
 | 
						|
 | 
						|
	    /* if there already was a column with the same score, set its */
 | 
						|
	    /* previous pointer to this new column */
 | 
						|
	    if (next_col != EMPTY)
 | 
						|
	    {
 | 
						|
		Col [next_col].shared3.prev = c ;
 | 
						|
	    }
 | 
						|
	    head [score] = c ;
 | 
						|
 | 
						|
	    /* see if this score is less than current min */
 | 
						|
	    min_score = MIN (min_score, score) ;
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
	    debug_count++ ;
 | 
						|
#endif /* NDEBUG */
 | 
						|
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
    DEBUG1 (("colamd: Live cols %d out of %d, non-princ: %d\n",
 | 
						|
	debug_count, n_col, n_col-debug_count)) ;
 | 
						|
    ASSERT (debug_count == n_col2) ;
 | 
						|
    debug_deg_lists (n_row, n_col, Row, Col, head, min_score, n_col2, max_deg) ;
 | 
						|
#endif /* NDEBUG */
 | 
						|
 | 
						|
    /* === Return number of remaining columns, and max row degree =========== */
 | 
						|
 | 
						|
    *p_n_col2 = n_col2 ;
 | 
						|
    *p_n_row2 = n_row2 ;
 | 
						|
    *p_max_deg = max_deg ;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === find_ordering ======================================================== */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/*
 | 
						|
    Order the principal columns of the supercolumn form of the matrix
 | 
						|
    (no supercolumns on input).  Uses a minimum approximate column minimum
 | 
						|
    degree ordering method.  Not user-callable.
 | 
						|
*/
 | 
						|
 | 
						|
PRIVATE Int find_ordering	/* return the number of garbage collections */
 | 
						|
(
 | 
						|
    /* === Parameters ======================================================= */
 | 
						|
 | 
						|
    Int n_row,			/* number of rows of A */
 | 
						|
    Int n_col,			/* number of columns of A */
 | 
						|
    Int Alen,			/* size of A, 2*nnz + n_col or larger */
 | 
						|
    Colamd_Row Row [],		/* of size n_row+1 */
 | 
						|
    Colamd_Col Col [],		/* of size n_col+1 */
 | 
						|
    Int A [],			/* column form and row form of A */
 | 
						|
    Int head [],		/* of size n_col+1 */
 | 
						|
    Int n_col2,			/* Remaining columns to order */
 | 
						|
    Int max_deg,		/* Maximum row degree */
 | 
						|
    Int pfree,			/* index of first free slot (2*nnz on entry) */
 | 
						|
    Int aggressive
 | 
						|
)
 | 
						|
{
 | 
						|
    /* === Local variables ================================================== */
 | 
						|
 | 
						|
    Int k ;			/* current pivot ordering step */
 | 
						|
    Int pivot_col ;		/* current pivot column */
 | 
						|
    Int *cp ;			/* a column pointer */
 | 
						|
    Int *rp ;			/* a row pointer */
 | 
						|
    Int pivot_row ;		/* current pivot row */
 | 
						|
    Int *new_cp ;		/* modified column pointer */
 | 
						|
    Int *new_rp ;		/* modified row pointer */
 | 
						|
    Int pivot_row_start ;	/* pointer to start of pivot row */
 | 
						|
    Int pivot_row_degree ;	/* number of columns in pivot row */
 | 
						|
    Int pivot_row_length ;	/* number of supercolumns in pivot row */
 | 
						|
    Int pivot_col_score ;	/* score of pivot column */
 | 
						|
    Int needed_memory ;		/* free space needed for pivot row */
 | 
						|
    Int *cp_end ;		/* pointer to the end of a column */
 | 
						|
    Int *rp_end ;		/* pointer to the end of a row */
 | 
						|
    Int row ;			/* a row index */
 | 
						|
    Int col ;			/* a column index */
 | 
						|
    Int max_score ;		/* maximum possible score */
 | 
						|
    Int cur_score ;		/* score of current column */
 | 
						|
    unsigned Int hash ;		/* hash value for supernode detection */
 | 
						|
    Int head_column ;		/* head of hash bucket */
 | 
						|
    Int first_col ;		/* first column in hash bucket */
 | 
						|
    Int tag_mark ;		/* marker value for mark array */
 | 
						|
    Int row_mark ;		/* Row [row].shared2.mark */
 | 
						|
    Int set_difference ;	/* set difference size of row with pivot row */
 | 
						|
    Int min_score ;		/* smallest column score */
 | 
						|
    Int col_thickness ;		/* "thickness" (no. of columns in a supercol) */
 | 
						|
    Int max_mark ;		/* maximum value of tag_mark */
 | 
						|
    Int pivot_col_thickness ;	/* number of columns represented by pivot col */
 | 
						|
    Int prev_col ;		/* Used by Dlist operations. */
 | 
						|
    Int next_col ;		/* Used by Dlist operations. */
 | 
						|
    Int ngarbage ;		/* number of garbage collections performed */
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
    Int debug_d ;		/* debug loop counter */
 | 
						|
    Int debug_step = 0 ;	/* debug loop counter */
 | 
						|
#endif /* NDEBUG */
 | 
						|
 | 
						|
    /* === Initialization and clear mark ==================================== */
 | 
						|
 | 
						|
    max_mark = INT_MAX - n_col ;	/* INT_MAX defined in <limits.h> */
 | 
						|
    tag_mark = clear_mark (0, max_mark, n_row, Row) ;
 | 
						|
    min_score = 0 ;
 | 
						|
    ngarbage = 0 ;
 | 
						|
    DEBUG1 (("colamd: Ordering, n_col2=%d\n", n_col2)) ;
 | 
						|
 | 
						|
    /* === Order the columns ================================================ */
 | 
						|
 | 
						|
    for (k = 0 ; k < n_col2 ; /* 'k' is incremented below */)
 | 
						|
    {
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
	if (debug_step % 100 == 0)
 | 
						|
	{
 | 
						|
	    DEBUG2 (("\n...       Step k: %d out of n_col2: %d\n", k, n_col2)) ;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
	    DEBUG3 (("\n----------Step k: %d out of n_col2: %d\n", k, n_col2)) ;
 | 
						|
	}
 | 
						|
	debug_step++ ;
 | 
						|
	debug_deg_lists (n_row, n_col, Row, Col, head,
 | 
						|
		min_score, n_col2-k, max_deg) ;
 | 
						|
	debug_matrix (n_row, n_col, Row, Col, A) ;
 | 
						|
#endif /* NDEBUG */
 | 
						|
 | 
						|
	/* === Select pivot column, and order it ============================ */
 | 
						|
 | 
						|
	/* make sure degree list isn't empty */
 | 
						|
	ASSERT (min_score >= 0) ;
 | 
						|
	ASSERT (min_score <= n_col) ;
 | 
						|
	ASSERT (head [min_score] >= EMPTY) ;
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
	for (debug_d = 0 ; debug_d < min_score ; debug_d++)
 | 
						|
	{
 | 
						|
	    ASSERT (head [debug_d] == EMPTY) ;
 | 
						|
	}
 | 
						|
#endif /* NDEBUG */
 | 
						|
 | 
						|
	/* get pivot column from head of minimum degree list */
 | 
						|
	while (head [min_score] == EMPTY && min_score < n_col)
 | 
						|
	{
 | 
						|
	    min_score++ ;
 | 
						|
	}
 | 
						|
	pivot_col = head [min_score] ;
 | 
						|
	ASSERT (pivot_col >= 0 && pivot_col <= n_col) ;
 | 
						|
	next_col = Col [pivot_col].shared4.degree_next ;
 | 
						|
	head [min_score] = next_col ;
 | 
						|
	if (next_col != EMPTY)
 | 
						|
	{
 | 
						|
	    Col [next_col].shared3.prev = EMPTY ;
 | 
						|
	}
 | 
						|
 | 
						|
	ASSERT (COL_IS_ALIVE (pivot_col)) ;
 | 
						|
 | 
						|
	/* remember score for defrag check */
 | 
						|
	pivot_col_score = Col [pivot_col].shared2.score ;
 | 
						|
 | 
						|
	/* the pivot column is the kth column in the pivot order */
 | 
						|
	Col [pivot_col].shared2.order = k ;
 | 
						|
 | 
						|
	/* increment order count by column thickness */
 | 
						|
	pivot_col_thickness = Col [pivot_col].shared1.thickness ;
 | 
						|
	k += pivot_col_thickness ;
 | 
						|
	ASSERT (pivot_col_thickness > 0) ;
 | 
						|
	DEBUG3 (("Pivot col: %d thick %d\n", pivot_col, pivot_col_thickness)) ;
 | 
						|
 | 
						|
	/* === Garbage_collection, if necessary ============================= */
 | 
						|
 | 
						|
	needed_memory = MIN (pivot_col_score, n_col - k) ;
 | 
						|
	if (pfree + needed_memory >= Alen)
 | 
						|
	{
 | 
						|
	    pfree = garbage_collection (n_row, n_col, Row, Col, A, &A [pfree]) ;
 | 
						|
	    ngarbage++ ;
 | 
						|
	    /* after garbage collection we will have enough */
 | 
						|
	    ASSERT (pfree + needed_memory < Alen) ;
 | 
						|
	    /* garbage collection has wiped out the Row[].shared2.mark array */
 | 
						|
	    tag_mark = clear_mark (0, max_mark, n_row, Row) ;
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
	    debug_matrix (n_row, n_col, Row, Col, A) ;
 | 
						|
#endif /* NDEBUG */
 | 
						|
	}
 | 
						|
 | 
						|
	/* === Compute pivot row pattern ==================================== */
 | 
						|
 | 
						|
	/* get starting location for this new merged row */
 | 
						|
	pivot_row_start = pfree ;
 | 
						|
 | 
						|
	/* initialize new row counts to zero */
 | 
						|
	pivot_row_degree = 0 ;
 | 
						|
 | 
						|
	/* tag pivot column as having been visited so it isn't included */
 | 
						|
	/* in merged pivot row */
 | 
						|
	Col [pivot_col].shared1.thickness = -pivot_col_thickness ;
 | 
						|
 | 
						|
	/* pivot row is the union of all rows in the pivot column pattern */
 | 
						|
	cp = &A [Col [pivot_col].start] ;
 | 
						|
	cp_end = cp + Col [pivot_col].length ;
 | 
						|
	while (cp < cp_end)
 | 
						|
	{
 | 
						|
	    /* get a row */
 | 
						|
	    row = *cp++ ;
 | 
						|
	    DEBUG4 (("Pivot col pattern %d %d\n", ROW_IS_ALIVE (row), row)) ;
 | 
						|
	    /* skip if row is dead */
 | 
						|
	    if (ROW_IS_ALIVE (row))
 | 
						|
	    {
 | 
						|
		rp = &A [Row [row].start] ;
 | 
						|
		rp_end = rp + Row [row].length ;
 | 
						|
		while (rp < rp_end)
 | 
						|
		{
 | 
						|
		    /* get a column */
 | 
						|
		    col = *rp++ ;
 | 
						|
		    /* add the column, if alive and untagged */
 | 
						|
		    col_thickness = Col [col].shared1.thickness ;
 | 
						|
		    if (col_thickness > 0 && COL_IS_ALIVE (col))
 | 
						|
		    {
 | 
						|
			/* tag column in pivot row */
 | 
						|
			Col [col].shared1.thickness = -col_thickness ;
 | 
						|
			ASSERT (pfree < Alen) ;
 | 
						|
			/* place column in pivot row */
 | 
						|
			A [pfree++] = col ;
 | 
						|
			pivot_row_degree += col_thickness ;
 | 
						|
		    }
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
	/* clear tag on pivot column */
 | 
						|
	Col [pivot_col].shared1.thickness = pivot_col_thickness ;
 | 
						|
	max_deg = MAX (max_deg, pivot_row_degree) ;
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
	DEBUG3 (("check2\n")) ;
 | 
						|
	debug_mark (n_row, Row, tag_mark, max_mark) ;
 | 
						|
#endif /* NDEBUG */
 | 
						|
 | 
						|
	/* === Kill all rows used to construct pivot row ==================== */
 | 
						|
 | 
						|
	/* also kill pivot row, temporarily */
 | 
						|
	cp = &A [Col [pivot_col].start] ;
 | 
						|
	cp_end = cp + Col [pivot_col].length ;
 | 
						|
	while (cp < cp_end)
 | 
						|
	{
 | 
						|
	    /* may be killing an already dead row */
 | 
						|
	    row = *cp++ ;
 | 
						|
	    DEBUG3 (("Kill row in pivot col: %d\n", row)) ;
 | 
						|
	    KILL_ROW (row) ;
 | 
						|
	}
 | 
						|
 | 
						|
	/* === Select a row index to use as the new pivot row =============== */
 | 
						|
 | 
						|
	pivot_row_length = pfree - pivot_row_start ;
 | 
						|
	if (pivot_row_length > 0)
 | 
						|
	{
 | 
						|
	    /* pick the "pivot" row arbitrarily (first row in col) */
 | 
						|
	    pivot_row = A [Col [pivot_col].start] ;
 | 
						|
	    DEBUG3 (("Pivotal row is %d\n", pivot_row)) ;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
	    /* there is no pivot row, since it is of zero length */
 | 
						|
	    pivot_row = EMPTY ;
 | 
						|
	    ASSERT (pivot_row_length == 0) ;
 | 
						|
	}
 | 
						|
	ASSERT (Col [pivot_col].length > 0 || pivot_row_length == 0) ;
 | 
						|
 | 
						|
	/* === Approximate degree computation =============================== */
 | 
						|
 | 
						|
	/* Here begins the computation of the approximate degree.  The column */
 | 
						|
	/* score is the sum of the pivot row "length", plus the size of the */
 | 
						|
	/* set differences of each row in the column minus the pattern of the */
 | 
						|
	/* pivot row itself.  The column ("thickness") itself is also */
 | 
						|
	/* excluded from the column score (we thus use an approximate */
 | 
						|
	/* external degree). */
 | 
						|
 | 
						|
	/* The time taken by the following code (compute set differences, and */
 | 
						|
	/* add them up) is proportional to the size of the data structure */
 | 
						|
	/* being scanned - that is, the sum of the sizes of each column in */
 | 
						|
	/* the pivot row.  Thus, the amortized time to compute a column score */
 | 
						|
	/* is proportional to the size of that column (where size, in this */
 | 
						|
	/* context, is the column "length", or the number of row indices */
 | 
						|
	/* in that column).  The number of row indices in a column is */
 | 
						|
	/* monotonically non-decreasing, from the length of the original */
 | 
						|
	/* column on input to colamd. */
 | 
						|
 | 
						|
	/* === Compute set differences ====================================== */
 | 
						|
 | 
						|
	DEBUG3 (("** Computing set differences phase. **\n")) ;
 | 
						|
 | 
						|
	/* pivot row is currently dead - it will be revived later. */
 | 
						|
 | 
						|
	DEBUG3 (("Pivot row: ")) ;
 | 
						|
	/* for each column in pivot row */
 | 
						|
	rp = &A [pivot_row_start] ;
 | 
						|
	rp_end = rp + pivot_row_length ;
 | 
						|
	while (rp < rp_end)
 | 
						|
	{
 | 
						|
	    col = *rp++ ;
 | 
						|
	    ASSERT (COL_IS_ALIVE (col) && col != pivot_col) ;
 | 
						|
	    DEBUG3 (("Col: %d\n", col)) ;
 | 
						|
 | 
						|
	    /* clear tags used to construct pivot row pattern */
 | 
						|
	    col_thickness = -Col [col].shared1.thickness ;
 | 
						|
	    ASSERT (col_thickness > 0) ;
 | 
						|
	    Col [col].shared1.thickness = col_thickness ;
 | 
						|
 | 
						|
	    /* === Remove column from degree list =========================== */
 | 
						|
 | 
						|
	    cur_score = Col [col].shared2.score ;
 | 
						|
	    prev_col = Col [col].shared3.prev ;
 | 
						|
	    next_col = Col [col].shared4.degree_next ;
 | 
						|
	    ASSERT (cur_score >= 0) ;
 | 
						|
	    ASSERT (cur_score <= n_col) ;
 | 
						|
	    ASSERT (cur_score >= EMPTY) ;
 | 
						|
	    if (prev_col == EMPTY)
 | 
						|
	    {
 | 
						|
		head [cur_score] = next_col ;
 | 
						|
	    }
 | 
						|
	    else
 | 
						|
	    {
 | 
						|
		Col [prev_col].shared4.degree_next = next_col ;
 | 
						|
	    }
 | 
						|
	    if (next_col != EMPTY)
 | 
						|
	    {
 | 
						|
		Col [next_col].shared3.prev = prev_col ;
 | 
						|
	    }
 | 
						|
 | 
						|
	    /* === Scan the column ========================================== */
 | 
						|
 | 
						|
	    cp = &A [Col [col].start] ;
 | 
						|
	    cp_end = cp + Col [col].length ;
 | 
						|
	    while (cp < cp_end)
 | 
						|
	    {
 | 
						|
		/* get a row */
 | 
						|
		row = *cp++ ;
 | 
						|
		row_mark = Row [row].shared2.mark ;
 | 
						|
		/* skip if dead */
 | 
						|
		if (ROW_IS_MARKED_DEAD (row_mark))
 | 
						|
		{
 | 
						|
		    continue ;
 | 
						|
		}
 | 
						|
		ASSERT (row != pivot_row) ;
 | 
						|
		set_difference = row_mark - tag_mark ;
 | 
						|
		/* check if the row has been seen yet */
 | 
						|
		if (set_difference < 0)
 | 
						|
		{
 | 
						|
		    ASSERT (Row [row].shared1.degree <= max_deg) ;
 | 
						|
		    set_difference = Row [row].shared1.degree ;
 | 
						|
		}
 | 
						|
		/* subtract column thickness from this row's set difference */
 | 
						|
		set_difference -= col_thickness ;
 | 
						|
		ASSERT (set_difference >= 0) ;
 | 
						|
		/* absorb this row if the set difference becomes zero */
 | 
						|
		if (set_difference == 0 && aggressive)
 | 
						|
		{
 | 
						|
		    DEBUG3 (("aggressive absorption. Row: %d\n", row)) ;
 | 
						|
		    KILL_ROW (row) ;
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
		    /* save the new mark */
 | 
						|
		    Row [row].shared2.mark = set_difference + tag_mark ;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
	debug_deg_lists (n_row, n_col, Row, Col, head,
 | 
						|
		min_score, n_col2-k-pivot_row_degree, max_deg) ;
 | 
						|
#endif /* NDEBUG */
 | 
						|
 | 
						|
	/* === Add up set differences for each column ======================= */
 | 
						|
 | 
						|
	DEBUG3 (("** Adding set differences phase. **\n")) ;
 | 
						|
 | 
						|
	/* for each column in pivot row */
 | 
						|
	rp = &A [pivot_row_start] ;
 | 
						|
	rp_end = rp + pivot_row_length ;
 | 
						|
	while (rp < rp_end)
 | 
						|
	{
 | 
						|
	    /* get a column */
 | 
						|
	    col = *rp++ ;
 | 
						|
	    ASSERT (COL_IS_ALIVE (col) && col != pivot_col) ;
 | 
						|
	    hash = 0 ;
 | 
						|
	    cur_score = 0 ;
 | 
						|
	    cp = &A [Col [col].start] ;
 | 
						|
	    /* compact the column */
 | 
						|
	    new_cp = cp ;
 | 
						|
	    cp_end = cp + Col [col].length ;
 | 
						|
 | 
						|
	    DEBUG4 (("Adding set diffs for Col: %d.\n", col)) ;
 | 
						|
 | 
						|
	    while (cp < cp_end)
 | 
						|
	    {
 | 
						|
		/* get a row */
 | 
						|
		row = *cp++ ;
 | 
						|
		ASSERT(row >= 0 && row < n_row) ;
 | 
						|
		row_mark = Row [row].shared2.mark ;
 | 
						|
		/* skip if dead */
 | 
						|
		if (ROW_IS_MARKED_DEAD (row_mark))
 | 
						|
		{
 | 
						|
		    DEBUG4 ((" Row %d, dead\n", row)) ;
 | 
						|
		    continue ;
 | 
						|
		}
 | 
						|
		DEBUG4 ((" Row %d, set diff %d\n", row, row_mark-tag_mark));
 | 
						|
		ASSERT (row_mark >= tag_mark) ;
 | 
						|
		/* compact the column */
 | 
						|
		*new_cp++ = row ;
 | 
						|
		/* compute hash function */
 | 
						|
		hash += row ;
 | 
						|
		/* add set difference */
 | 
						|
		cur_score += row_mark - tag_mark ;
 | 
						|
		/* integer overflow... */
 | 
						|
		cur_score = MIN (cur_score, n_col) ;
 | 
						|
	    }
 | 
						|
 | 
						|
	    /* recompute the column's length */
 | 
						|
	    Col [col].length = (Int) (new_cp - &A [Col [col].start]) ;
 | 
						|
 | 
						|
	    /* === Further mass elimination ================================= */
 | 
						|
 | 
						|
	    if (Col [col].length == 0)
 | 
						|
	    {
 | 
						|
		DEBUG4 (("further mass elimination. Col: %d\n", col)) ;
 | 
						|
		/* nothing left but the pivot row in this column */
 | 
						|
		KILL_PRINCIPAL_COL (col) ;
 | 
						|
		pivot_row_degree -= Col [col].shared1.thickness ;
 | 
						|
		ASSERT (pivot_row_degree >= 0) ;
 | 
						|
		/* order it */
 | 
						|
		Col [col].shared2.order = k ;
 | 
						|
		/* increment order count by column thickness */
 | 
						|
		k += Col [col].shared1.thickness ;
 | 
						|
	    }
 | 
						|
	    else
 | 
						|
	    {
 | 
						|
		/* === Prepare for supercolumn detection ==================== */
 | 
						|
 | 
						|
		DEBUG4 (("Preparing supercol detection for Col: %d.\n", col)) ;
 | 
						|
 | 
						|
		/* save score so far */
 | 
						|
		Col [col].shared2.score = cur_score ;
 | 
						|
 | 
						|
		/* add column to hash table, for supercolumn detection */
 | 
						|
		hash %= n_col + 1 ;
 | 
						|
 | 
						|
		DEBUG4 ((" Hash = %d, n_col = %d.\n", hash, n_col)) ;
 | 
						|
		ASSERT (((Int) hash) <= n_col) ;
 | 
						|
 | 
						|
		head_column = head [hash] ;
 | 
						|
		if (head_column > EMPTY)
 | 
						|
		{
 | 
						|
		    /* degree list "hash" is non-empty, use prev (shared3) of */
 | 
						|
		    /* first column in degree list as head of hash bucket */
 | 
						|
		    first_col = Col [head_column].shared3.headhash ;
 | 
						|
		    Col [head_column].shared3.headhash = col ;
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
		    /* degree list "hash" is empty, use head as hash bucket */
 | 
						|
		    first_col = - (head_column + 2) ;
 | 
						|
		    head [hash] = - (col + 2) ;
 | 
						|
		}
 | 
						|
		Col [col].shared4.hash_next = first_col ;
 | 
						|
 | 
						|
		/* save hash function in Col [col].shared3.hash */
 | 
						|
		Col [col].shared3.hash = (Int) hash ;
 | 
						|
		ASSERT (COL_IS_ALIVE (col)) ;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
	/* The approximate external column degree is now computed.  */
 | 
						|
 | 
						|
	/* === Supercolumn detection ======================================== */
 | 
						|
 | 
						|
	DEBUG3 (("** Supercolumn detection phase. **\n")) ;
 | 
						|
 | 
						|
	detect_super_cols (
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
		n_col, Row,
 | 
						|
#endif /* NDEBUG */
 | 
						|
 | 
						|
		Col, A, head, pivot_row_start, pivot_row_length) ;
 | 
						|
 | 
						|
	/* === Kill the pivotal column ====================================== */
 | 
						|
 | 
						|
	KILL_PRINCIPAL_COL (pivot_col) ;
 | 
						|
 | 
						|
	/* === Clear mark =================================================== */
 | 
						|
 | 
						|
	tag_mark = clear_mark (tag_mark+max_deg+1, max_mark, n_row, Row) ;
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
	DEBUG3 (("check3\n")) ;
 | 
						|
	debug_mark (n_row, Row, tag_mark, max_mark) ;
 | 
						|
#endif /* NDEBUG */
 | 
						|
 | 
						|
	/* === Finalize the new pivot row, and column scores ================ */
 | 
						|
 | 
						|
	DEBUG3 (("** Finalize scores phase. **\n")) ;
 | 
						|
 | 
						|
	/* for each column in pivot row */
 | 
						|
	rp = &A [pivot_row_start] ;
 | 
						|
	/* compact the pivot row */
 | 
						|
	new_rp = rp ;
 | 
						|
	rp_end = rp + pivot_row_length ;
 | 
						|
	while (rp < rp_end)
 | 
						|
	{
 | 
						|
	    col = *rp++ ;
 | 
						|
	    /* skip dead columns */
 | 
						|
	    if (COL_IS_DEAD (col))
 | 
						|
	    {
 | 
						|
		continue ;
 | 
						|
	    }
 | 
						|
	    *new_rp++ = col ;
 | 
						|
	    /* add new pivot row to column */
 | 
						|
	    A [Col [col].start + (Col [col].length++)] = pivot_row ;
 | 
						|
 | 
						|
	    /* retrieve score so far and add on pivot row's degree. */
 | 
						|
	    /* (we wait until here for this in case the pivot */
 | 
						|
	    /* row's degree was reduced due to mass elimination). */
 | 
						|
	    cur_score = Col [col].shared2.score + pivot_row_degree ;
 | 
						|
 | 
						|
	    /* calculate the max possible score as the number of */
 | 
						|
	    /* external columns minus the 'k' value minus the */
 | 
						|
	    /* columns thickness */
 | 
						|
	    max_score = n_col - k - Col [col].shared1.thickness ;
 | 
						|
 | 
						|
	    /* make the score the external degree of the union-of-rows */
 | 
						|
	    cur_score -= Col [col].shared1.thickness ;
 | 
						|
 | 
						|
	    /* make sure score is less or equal than the max score */
 | 
						|
	    cur_score = MIN (cur_score, max_score) ;
 | 
						|
	    ASSERT (cur_score >= 0) ;
 | 
						|
 | 
						|
	    /* store updated score */
 | 
						|
	    Col [col].shared2.score = cur_score ;
 | 
						|
 | 
						|
	    /* === Place column back in degree list ========================= */
 | 
						|
 | 
						|
	    ASSERT (min_score >= 0) ;
 | 
						|
	    ASSERT (min_score <= n_col) ;
 | 
						|
	    ASSERT (cur_score >= 0) ;
 | 
						|
	    ASSERT (cur_score <= n_col) ;
 | 
						|
	    ASSERT (head [cur_score] >= EMPTY) ;
 | 
						|
	    next_col = head [cur_score] ;
 | 
						|
	    Col [col].shared4.degree_next = next_col ;
 | 
						|
	    Col [col].shared3.prev = EMPTY ;
 | 
						|
	    if (next_col != EMPTY)
 | 
						|
	    {
 | 
						|
		Col [next_col].shared3.prev = col ;
 | 
						|
	    }
 | 
						|
	    head [cur_score] = col ;
 | 
						|
 | 
						|
	    /* see if this score is less than current min */
 | 
						|
	    min_score = MIN (min_score, cur_score) ;
 | 
						|
 | 
						|
	}
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
	debug_deg_lists (n_row, n_col, Row, Col, head,
 | 
						|
		min_score, n_col2-k, max_deg) ;
 | 
						|
#endif /* NDEBUG */
 | 
						|
 | 
						|
	/* === Resurrect the new pivot row ================================== */
 | 
						|
 | 
						|
	if (pivot_row_degree > 0)
 | 
						|
	{
 | 
						|
	    /* update pivot row length to reflect any cols that were killed */
 | 
						|
	    /* during super-col detection and mass elimination */
 | 
						|
	    Row [pivot_row].start  = pivot_row_start ;
 | 
						|
	    Row [pivot_row].length = (Int) (new_rp - &A[pivot_row_start]) ;
 | 
						|
	    ASSERT (Row [pivot_row].length > 0) ;
 | 
						|
	    Row [pivot_row].shared1.degree = pivot_row_degree ;
 | 
						|
	    Row [pivot_row].shared2.mark = 0 ;
 | 
						|
	    /* pivot row is no longer dead */
 | 
						|
 | 
						|
	    DEBUG1 (("Resurrect Pivot_row %d deg: %d\n",
 | 
						|
			pivot_row, pivot_row_degree)) ;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    /* === All principal columns have now been ordered ====================== */
 | 
						|
 | 
						|
    return (ngarbage) ;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === order_children ======================================================= */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/*
 | 
						|
    The find_ordering routine has ordered all of the principal columns (the
 | 
						|
    representatives of the supercolumns).  The non-principal columns have not
 | 
						|
    yet been ordered.  This routine orders those columns by walking up the
 | 
						|
    parent tree (a column is a child of the column which absorbed it).  The
 | 
						|
    final permutation vector is then placed in p [0 ... n_col-1], with p [0]
 | 
						|
    being the first column, and p [n_col-1] being the last.  It doesn't look
 | 
						|
    like it at first glance, but be assured that this routine takes time linear
 | 
						|
    in the number of columns.  Although not immediately obvious, the time
 | 
						|
    taken by this routine is O (n_col), that is, linear in the number of
 | 
						|
    columns.  Not user-callable.
 | 
						|
*/
 | 
						|
 | 
						|
PRIVATE void order_children
 | 
						|
(
 | 
						|
    /* === Parameters ======================================================= */
 | 
						|
 | 
						|
    Int n_col,			/* number of columns of A */
 | 
						|
    Colamd_Col Col [],		/* of size n_col+1 */
 | 
						|
    Int p []			/* p [0 ... n_col-1] is the column permutation*/
 | 
						|
)
 | 
						|
{
 | 
						|
    /* === Local variables ================================================== */
 | 
						|
 | 
						|
    Int i ;			/* loop counter for all columns */
 | 
						|
    Int c ;			/* column index */
 | 
						|
    Int parent ;		/* index of column's parent */
 | 
						|
    Int order ;			/* column's order */
 | 
						|
 | 
						|
    /* === Order each non-principal column ================================== */
 | 
						|
 | 
						|
    for (i = 0 ; i < n_col ; i++)
 | 
						|
    {
 | 
						|
	/* find an un-ordered non-principal column */
 | 
						|
	ASSERT (COL_IS_DEAD (i)) ;
 | 
						|
	if (!COL_IS_DEAD_PRINCIPAL (i) && Col [i].shared2.order == EMPTY)
 | 
						|
	{
 | 
						|
	    parent = i ;
 | 
						|
	    /* once found, find its principal parent */
 | 
						|
	    do
 | 
						|
	    {
 | 
						|
		parent = Col [parent].shared1.parent ;
 | 
						|
	    } while (!COL_IS_DEAD_PRINCIPAL (parent)) ;
 | 
						|
 | 
						|
	    /* now, order all un-ordered non-principal columns along path */
 | 
						|
	    /* to this parent.  collapse tree at the same time */
 | 
						|
	    c = i ;
 | 
						|
	    /* get order of parent */
 | 
						|
	    order = Col [parent].shared2.order ;
 | 
						|
 | 
						|
	    do
 | 
						|
	    {
 | 
						|
		ASSERT (Col [c].shared2.order == EMPTY) ;
 | 
						|
 | 
						|
		/* order this column */
 | 
						|
		Col [c].shared2.order = order++ ;
 | 
						|
		/* collaps tree */
 | 
						|
		Col [c].shared1.parent = parent ;
 | 
						|
 | 
						|
		/* get immediate parent of this column */
 | 
						|
		c = Col [c].shared1.parent ;
 | 
						|
 | 
						|
		/* continue until we hit an ordered column.  There are */
 | 
						|
		/* guarranteed not to be anymore unordered columns */
 | 
						|
		/* above an ordered column */
 | 
						|
	    } while (Col [c].shared2.order == EMPTY) ;
 | 
						|
 | 
						|
	    /* re-order the super_col parent to largest order for this group */
 | 
						|
	    Col [parent].shared2.order = order ;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    /* === Generate the permutation ========================================= */
 | 
						|
 | 
						|
    for (c = 0 ; c < n_col ; c++)
 | 
						|
    {
 | 
						|
	p [Col [c].shared2.order] = c ;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === detect_super_cols ==================================================== */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/*
 | 
						|
    Detects supercolumns by finding matches between columns in the hash buckets.
 | 
						|
    Check amongst columns in the set A [row_start ... row_start + row_length-1].
 | 
						|
    The columns under consideration are currently *not* in the degree lists,
 | 
						|
    and have already been placed in the hash buckets.
 | 
						|
 | 
						|
    The hash bucket for columns whose hash function is equal to h is stored
 | 
						|
    as follows:
 | 
						|
 | 
						|
	if head [h] is >= 0, then head [h] contains a degree list, so:
 | 
						|
 | 
						|
		head [h] is the first column in degree bucket h.
 | 
						|
		Col [head [h]].headhash gives the first column in hash bucket h.
 | 
						|
 | 
						|
	otherwise, the degree list is empty, and:
 | 
						|
 | 
						|
		-(head [h] + 2) is the first column in hash bucket h.
 | 
						|
 | 
						|
    For a column c in a hash bucket, Col [c].shared3.prev is NOT a "previous
 | 
						|
    column" pointer.  Col [c].shared3.hash is used instead as the hash number
 | 
						|
    for that column.  The value of Col [c].shared4.hash_next is the next column
 | 
						|
    in the same hash bucket.
 | 
						|
 | 
						|
    Assuming no, or "few" hash collisions, the time taken by this routine is
 | 
						|
    linear in the sum of the sizes (lengths) of each column whose score has
 | 
						|
    just been computed in the approximate degree computation.
 | 
						|
    Not user-callable.
 | 
						|
*/
 | 
						|
 | 
						|
PRIVATE void detect_super_cols
 | 
						|
(
 | 
						|
    /* === Parameters ======================================================= */
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
    /* these two parameters are only needed when debugging is enabled: */
 | 
						|
    Int n_col,			/* number of columns of A */
 | 
						|
    Colamd_Row Row [],		/* of size n_row+1 */
 | 
						|
#endif /* NDEBUG */
 | 
						|
 | 
						|
    Colamd_Col Col [],		/* of size n_col+1 */
 | 
						|
    Int A [],			/* row indices of A */
 | 
						|
    Int head [],		/* head of degree lists and hash buckets */
 | 
						|
    Int row_start,		/* pointer to set of columns to check */
 | 
						|
    Int row_length		/* number of columns to check */
 | 
						|
)
 | 
						|
{
 | 
						|
    /* === Local variables ================================================== */
 | 
						|
 | 
						|
    Int hash ;			/* hash value for a column */
 | 
						|
    Int *rp ;			/* pointer to a row */
 | 
						|
    Int c ;			/* a column index */
 | 
						|
    Int super_c ;		/* column index of the column to absorb into */
 | 
						|
    Int *cp1 ;			/* column pointer for column super_c */
 | 
						|
    Int *cp2 ;			/* column pointer for column c */
 | 
						|
    Int length ;		/* length of column super_c */
 | 
						|
    Int prev_c ;		/* column preceding c in hash bucket */
 | 
						|
    Int i ;			/* loop counter */
 | 
						|
    Int *rp_end ;		/* pointer to the end of the row */
 | 
						|
    Int col ;			/* a column index in the row to check */
 | 
						|
    Int head_column ;		/* first column in hash bucket or degree list */
 | 
						|
    Int first_col ;		/* first column in hash bucket */
 | 
						|
 | 
						|
    /* === Consider each column in the row ================================== */
 | 
						|
 | 
						|
    rp = &A [row_start] ;
 | 
						|
    rp_end = rp + row_length ;
 | 
						|
    while (rp < rp_end)
 | 
						|
    {
 | 
						|
	col = *rp++ ;
 | 
						|
	if (COL_IS_DEAD (col))
 | 
						|
	{
 | 
						|
	    continue ;
 | 
						|
	}
 | 
						|
 | 
						|
	/* get hash number for this column */
 | 
						|
	hash = Col [col].shared3.hash ;
 | 
						|
	ASSERT (hash <= n_col) ;
 | 
						|
 | 
						|
	/* === Get the first column in this hash bucket ===================== */
 | 
						|
 | 
						|
	head_column = head [hash] ;
 | 
						|
	if (head_column > EMPTY)
 | 
						|
	{
 | 
						|
	    first_col = Col [head_column].shared3.headhash ;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
	    first_col = - (head_column + 2) ;
 | 
						|
	}
 | 
						|
 | 
						|
	/* === Consider each column in the hash bucket ====================== */
 | 
						|
 | 
						|
	for (super_c = first_col ; super_c != EMPTY ;
 | 
						|
	    super_c = Col [super_c].shared4.hash_next)
 | 
						|
	{
 | 
						|
	    ASSERT (COL_IS_ALIVE (super_c)) ;
 | 
						|
	    ASSERT (Col [super_c].shared3.hash == hash) ;
 | 
						|
	    length = Col [super_c].length ;
 | 
						|
 | 
						|
	    /* prev_c is the column preceding column c in the hash bucket */
 | 
						|
	    prev_c = super_c ;
 | 
						|
 | 
						|
	    /* === Compare super_c with all columns after it ================ */
 | 
						|
 | 
						|
	    for (c = Col [super_c].shared4.hash_next ;
 | 
						|
		 c != EMPTY ; c = Col [c].shared4.hash_next)
 | 
						|
	    {
 | 
						|
		ASSERT (c != super_c) ;
 | 
						|
		ASSERT (COL_IS_ALIVE (c)) ;
 | 
						|
		ASSERT (Col [c].shared3.hash == hash) ;
 | 
						|
 | 
						|
		/* not identical if lengths or scores are different */
 | 
						|
		if (Col [c].length != length ||
 | 
						|
		    Col [c].shared2.score != Col [super_c].shared2.score)
 | 
						|
		{
 | 
						|
		    prev_c = c ;
 | 
						|
		    continue ;
 | 
						|
		}
 | 
						|
 | 
						|
		/* compare the two columns */
 | 
						|
		cp1 = &A [Col [super_c].start] ;
 | 
						|
		cp2 = &A [Col [c].start] ;
 | 
						|
 | 
						|
		for (i = 0 ; i < length ; i++)
 | 
						|
		{
 | 
						|
		    /* the columns are "clean" (no dead rows) */
 | 
						|
		    ASSERT (ROW_IS_ALIVE (*cp1))  ;
 | 
						|
		    ASSERT (ROW_IS_ALIVE (*cp2))  ;
 | 
						|
		    /* row indices will same order for both supercols, */
 | 
						|
		    /* no gather scatter nessasary */
 | 
						|
		    if (*cp1++ != *cp2++)
 | 
						|
		    {
 | 
						|
			break ;
 | 
						|
		    }
 | 
						|
		}
 | 
						|
 | 
						|
		/* the two columns are different if the for-loop "broke" */
 | 
						|
		if (i != length)
 | 
						|
		{
 | 
						|
		    prev_c = c ;
 | 
						|
		    continue ;
 | 
						|
		}
 | 
						|
 | 
						|
		/* === Got it!  two columns are identical =================== */
 | 
						|
 | 
						|
		ASSERT (Col [c].shared2.score == Col [super_c].shared2.score) ;
 | 
						|
 | 
						|
		Col [super_c].shared1.thickness += Col [c].shared1.thickness ;
 | 
						|
		Col [c].shared1.parent = super_c ;
 | 
						|
		KILL_NON_PRINCIPAL_COL (c) ;
 | 
						|
		/* order c later, in order_children() */
 | 
						|
		Col [c].shared2.order = EMPTY ;
 | 
						|
		/* remove c from hash bucket */
 | 
						|
		Col [prev_c].shared4.hash_next = Col [c].shared4.hash_next ;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
 | 
						|
	/* === Empty this hash bucket ======================================= */
 | 
						|
 | 
						|
	if (head_column > EMPTY)
 | 
						|
	{
 | 
						|
	    /* corresponding degree list "hash" is not empty */
 | 
						|
	    Col [head_column].shared3.headhash = EMPTY ;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
	    /* corresponding degree list "hash" is empty */
 | 
						|
	    head [hash] = EMPTY ;
 | 
						|
	}
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === garbage_collection =================================================== */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/*
 | 
						|
    Defragments and compacts columns and rows in the workspace A.  Used when
 | 
						|
    all avaliable memory has been used while performing row merging.  Returns
 | 
						|
    the index of the first free position in A, after garbage collection.  The
 | 
						|
    time taken by this routine is linear is the size of the array A, which is
 | 
						|
    itself linear in the number of nonzeros in the input matrix.
 | 
						|
    Not user-callable.
 | 
						|
*/
 | 
						|
 | 
						|
PRIVATE Int garbage_collection  /* returns the new value of pfree */
 | 
						|
(
 | 
						|
    /* === Parameters ======================================================= */
 | 
						|
 | 
						|
    Int n_row,			/* number of rows */
 | 
						|
    Int n_col,			/* number of columns */
 | 
						|
    Colamd_Row Row [],		/* row info */
 | 
						|
    Colamd_Col Col [],		/* column info */
 | 
						|
    Int A [],			/* A [0 ... Alen-1] holds the matrix */
 | 
						|
    Int *pfree			/* &A [0] ... pfree is in use */
 | 
						|
)
 | 
						|
{
 | 
						|
    /* === Local variables ================================================== */
 | 
						|
 | 
						|
    Int *psrc ;			/* source pointer */
 | 
						|
    Int *pdest ;		/* destination pointer */
 | 
						|
    Int j ;			/* counter */
 | 
						|
    Int r ;			/* a row index */
 | 
						|
    Int c ;			/* a column index */
 | 
						|
    Int length ;		/* length of a row or column */
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
    Int debug_rows ;
 | 
						|
    DEBUG2 (("Defrag..\n")) ;
 | 
						|
    for (psrc = &A[0] ; psrc < pfree ; psrc++) ASSERT (*psrc >= 0) ;
 | 
						|
    debug_rows = 0 ;
 | 
						|
#endif /* NDEBUG */
 | 
						|
 | 
						|
    /* === Defragment the columns =========================================== */
 | 
						|
 | 
						|
    pdest = &A[0] ;
 | 
						|
    for (c = 0 ; c < n_col ; c++)
 | 
						|
    {
 | 
						|
	if (COL_IS_ALIVE (c))
 | 
						|
	{
 | 
						|
	    psrc = &A [Col [c].start] ;
 | 
						|
 | 
						|
	    /* move and compact the column */
 | 
						|
	    ASSERT (pdest <= psrc) ;
 | 
						|
	    Col [c].start = (Int) (pdest - &A [0]) ;
 | 
						|
	    length = Col [c].length ;
 | 
						|
	    for (j = 0 ; j < length ; j++)
 | 
						|
	    {
 | 
						|
		r = *psrc++ ;
 | 
						|
		if (ROW_IS_ALIVE (r))
 | 
						|
		{
 | 
						|
		    *pdest++ = r ;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	    Col [c].length = (Int) (pdest - &A [Col [c].start]) ;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    /* === Prepare to defragment the rows =================================== */
 | 
						|
 | 
						|
    for (r = 0 ; r < n_row ; r++)
 | 
						|
    {
 | 
						|
	if (ROW_IS_DEAD (r) || (Row [r].length == 0))
 | 
						|
	{
 | 
						|
	    /* This row is already dead, or is of zero length.  Cannot compact
 | 
						|
	     * a row of zero length, so kill it.  NOTE: in the current version,
 | 
						|
	     * there are no zero-length live rows.  Kill the row (for the first
 | 
						|
	     * time, or again) just to be safe. */
 | 
						|
	    KILL_ROW (r) ;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
	    /* save first column index in Row [r].shared2.first_column */
 | 
						|
	    psrc = &A [Row [r].start] ;
 | 
						|
	    Row [r].shared2.first_column = *psrc ;
 | 
						|
	    ASSERT (ROW_IS_ALIVE (r)) ;
 | 
						|
	    /* flag the start of the row with the one's complement of row */
 | 
						|
	    *psrc = ONES_COMPLEMENT (r) ;
 | 
						|
#ifndef NDEBUG
 | 
						|
	    debug_rows++ ;
 | 
						|
#endif /* NDEBUG */
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    /* === Defragment the rows ============================================== */
 | 
						|
 | 
						|
    psrc = pdest ;
 | 
						|
    while (psrc < pfree)
 | 
						|
    {
 | 
						|
	/* find a negative number ... the start of a row */
 | 
						|
	if (*psrc++ < 0)
 | 
						|
	{
 | 
						|
	    psrc-- ;
 | 
						|
	    /* get the row index */
 | 
						|
	    r = ONES_COMPLEMENT (*psrc) ;
 | 
						|
	    ASSERT (r >= 0 && r < n_row) ;
 | 
						|
	    /* restore first column index */
 | 
						|
	    *psrc = Row [r].shared2.first_column ;
 | 
						|
	    ASSERT (ROW_IS_ALIVE (r)) ;
 | 
						|
	    ASSERT (Row [r].length > 0) ;
 | 
						|
	    /* move and compact the row */
 | 
						|
	    ASSERT (pdest <= psrc) ;
 | 
						|
	    Row [r].start = (Int) (pdest - &A [0]) ;
 | 
						|
	    length = Row [r].length ;
 | 
						|
	    for (j = 0 ; j < length ; j++)
 | 
						|
	    {
 | 
						|
		c = *psrc++ ;
 | 
						|
		if (COL_IS_ALIVE (c))
 | 
						|
		{
 | 
						|
		    *pdest++ = c ;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	    Row [r].length = (Int) (pdest - &A [Row [r].start]) ;
 | 
						|
	    ASSERT (Row [r].length > 0) ;
 | 
						|
#ifndef NDEBUG
 | 
						|
	    debug_rows-- ;
 | 
						|
#endif /* NDEBUG */
 | 
						|
	}
 | 
						|
    }
 | 
						|
    /* ensure we found all the rows */
 | 
						|
    ASSERT (debug_rows == 0) ;
 | 
						|
 | 
						|
    /* === Return the new value of pfree ==================================== */
 | 
						|
 | 
						|
    return ((Int) (pdest - &A [0])) ;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === clear_mark =========================================================== */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/*
 | 
						|
    Clears the Row [].shared2.mark array, and returns the new tag_mark.
 | 
						|
    Return value is the new tag_mark.  Not user-callable.
 | 
						|
*/
 | 
						|
 | 
						|
PRIVATE Int clear_mark	/* return the new value for tag_mark */
 | 
						|
(
 | 
						|
    /* === Parameters ======================================================= */
 | 
						|
 | 
						|
    Int tag_mark,	/* new value of tag_mark */
 | 
						|
    Int max_mark,	/* max allowed value of tag_mark */
 | 
						|
 | 
						|
    Int n_row,		/* number of rows in A */
 | 
						|
    Colamd_Row Row []	/* Row [0 ... n_row-1].shared2.mark is set to zero */
 | 
						|
)
 | 
						|
{
 | 
						|
    /* === Local variables ================================================== */
 | 
						|
 | 
						|
    Int r ;
 | 
						|
 | 
						|
    if (tag_mark <= 0 || tag_mark >= max_mark)
 | 
						|
    {
 | 
						|
	for (r = 0 ; r < n_row ; r++)
 | 
						|
	{
 | 
						|
	    if (ROW_IS_ALIVE (r))
 | 
						|
	    {
 | 
						|
		Row [r].shared2.mark = 0 ;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	tag_mark = 1 ;
 | 
						|
    }
 | 
						|
 | 
						|
    return (tag_mark) ;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === print_report ========================================================= */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
PRIVATE void print_report
 | 
						|
(
 | 
						|
    char *method,
 | 
						|
    Int stats [COLAMD_STATS]
 | 
						|
)
 | 
						|
{
 | 
						|
 | 
						|
    Int i1, i2, i3 ;
 | 
						|
 | 
						|
    PRINTF (("\n%s version %d.%d, %s: ", method,
 | 
						|
	    COLAMD_MAIN_VERSION, COLAMD_SUB_VERSION, COLAMD_DATE)) ;
 | 
						|
 | 
						|
    if (!stats)
 | 
						|
    {
 | 
						|
    	PRINTF (("No statistics available.\n")) ;
 | 
						|
	return ;
 | 
						|
    }
 | 
						|
 | 
						|
    i1 = stats [COLAMD_INFO1] ;
 | 
						|
    i2 = stats [COLAMD_INFO2] ;
 | 
						|
    i3 = stats [COLAMD_INFO3] ;
 | 
						|
 | 
						|
    if (stats [COLAMD_STATUS] >= 0)
 | 
						|
    {
 | 
						|
    	PRINTF (("OK.  ")) ;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
    	PRINTF (("ERROR.  ")) ;
 | 
						|
    }
 | 
						|
 | 
						|
    switch (stats [COLAMD_STATUS])
 | 
						|
    {
 | 
						|
 | 
						|
	case COLAMD_OK_BUT_JUMBLED:
 | 
						|
 | 
						|
	    PRINTF(("Matrix has unsorted or duplicate row indices.\n")) ;
 | 
						|
 | 
						|
	    PRINTF(("%s: number of duplicate or out-of-order row indices: %d\n",
 | 
						|
	    method, i3)) ;
 | 
						|
 | 
						|
	    PRINTF(("%s: last seen duplicate or out-of-order row index:   %d\n",
 | 
						|
	    method, INDEX (i2))) ;
 | 
						|
 | 
						|
	    PRINTF(("%s: last seen in column:                             %d",
 | 
						|
	    method, INDEX (i1))) ;
 | 
						|
 | 
						|
	    /* no break - fall through to next case instead */
 | 
						|
 | 
						|
	case COLAMD_OK:
 | 
						|
 | 
						|
	    PRINTF(("\n")) ;
 | 
						|
 | 
						|
 	    PRINTF(("%s: number of dense or empty rows ignored:           %d\n",
 | 
						|
	    method, stats [COLAMD_DENSE_ROW])) ;
 | 
						|
 | 
						|
	    PRINTF(("%s: number of dense or empty columns ignored:        %d\n",
 | 
						|
	    method, stats [COLAMD_DENSE_COL])) ;
 | 
						|
 | 
						|
	    PRINTF(("%s: number of garbage collections performed:         %d\n",
 | 
						|
	    method, stats [COLAMD_DEFRAG_COUNT])) ;
 | 
						|
	    break ;
 | 
						|
 | 
						|
	case COLAMD_ERROR_A_not_present:
 | 
						|
 | 
						|
	    PRINTF(("Array A (row indices of matrix) not present.\n")) ;
 | 
						|
	    break ;
 | 
						|
 | 
						|
	case COLAMD_ERROR_p_not_present:
 | 
						|
 | 
						|
	    PRINTF(("Array p (column pointers for matrix) not present.\n")) ;
 | 
						|
	    break ;
 | 
						|
 | 
						|
	case COLAMD_ERROR_nrow_negative:
 | 
						|
 | 
						|
	    PRINTF(("Invalid number of rows (%d).\n", i1)) ;
 | 
						|
	    break ;
 | 
						|
 | 
						|
	case COLAMD_ERROR_ncol_negative:
 | 
						|
 | 
						|
	    PRINTF(("Invalid number of columns (%d).\n", i1)) ;
 | 
						|
	    break ;
 | 
						|
 | 
						|
	case COLAMD_ERROR_nnz_negative:
 | 
						|
 | 
						|
	    PRINTF(("Invalid number of nonzero entries (%d).\n", i1)) ;
 | 
						|
	    break ;
 | 
						|
 | 
						|
	case COLAMD_ERROR_p0_nonzero:
 | 
						|
 | 
						|
	    PRINTF(("Invalid column pointer, p [0] = %d, must be zero.\n", i1));
 | 
						|
	    break ;
 | 
						|
 | 
						|
	case COLAMD_ERROR_A_too_small:
 | 
						|
 | 
						|
	    PRINTF(("Array A too small.\n")) ;
 | 
						|
	    PRINTF(("        Need Alen >= %d, but given only Alen = %d.\n",
 | 
						|
	    i1, i2)) ;
 | 
						|
	    break ;
 | 
						|
 | 
						|
	case COLAMD_ERROR_col_length_negative:
 | 
						|
 | 
						|
	    PRINTF
 | 
						|
	    (("Column %d has a negative number of nonzero entries (%d).\n",
 | 
						|
	    INDEX (i1), i2)) ;
 | 
						|
	    break ;
 | 
						|
 | 
						|
	case COLAMD_ERROR_row_index_out_of_bounds:
 | 
						|
 | 
						|
	    PRINTF
 | 
						|
	    (("Row index (row %d) out of bounds (%d to %d) in column %d.\n",
 | 
						|
	    INDEX (i2), INDEX (0), INDEX (i3-1), INDEX (i1))) ;
 | 
						|
	    break ;
 | 
						|
 | 
						|
	case COLAMD_ERROR_out_of_memory:
 | 
						|
 | 
						|
	    PRINTF(("Out of memory.\n")) ;
 | 
						|
	    break ;
 | 
						|
 | 
						|
	/* v2.4: internal-error case deleted */
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === colamd debugging routines ============================================ */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/* When debugging is disabled, the remainder of this file is ignored. */
 | 
						|
 | 
						|
#ifndef NDEBUG
 | 
						|
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === debug_structures ===================================================== */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/*
 | 
						|
    At this point, all empty rows and columns are dead.  All live columns
 | 
						|
    are "clean" (containing no dead rows) and simplicial (no supercolumns
 | 
						|
    yet).  Rows may contain dead columns, but all live rows contain at
 | 
						|
    least one live column.
 | 
						|
*/
 | 
						|
 | 
						|
PRIVATE void debug_structures
 | 
						|
(
 | 
						|
    /* === Parameters ======================================================= */
 | 
						|
 | 
						|
    Int n_row,
 | 
						|
    Int n_col,
 | 
						|
    Colamd_Row Row [],
 | 
						|
    Colamd_Col Col [],
 | 
						|
    Int A [],
 | 
						|
    Int n_col2
 | 
						|
)
 | 
						|
{
 | 
						|
    /* === Local variables ================================================== */
 | 
						|
 | 
						|
    Int i ;
 | 
						|
    Int c ;
 | 
						|
    Int *cp ;
 | 
						|
    Int *cp_end ;
 | 
						|
    Int len ;
 | 
						|
    Int score ;
 | 
						|
    Int r ;
 | 
						|
    Int *rp ;
 | 
						|
    Int *rp_end ;
 | 
						|
    Int deg ;
 | 
						|
 | 
						|
    /* === Check A, Row, and Col ============================================ */
 | 
						|
 | 
						|
    for (c = 0 ; c < n_col ; c++)
 | 
						|
    {
 | 
						|
	if (COL_IS_ALIVE (c))
 | 
						|
	{
 | 
						|
	    len = Col [c].length ;
 | 
						|
	    score = Col [c].shared2.score ;
 | 
						|
	    DEBUG4 (("initial live col %5d %5d %5d\n", c, len, score)) ;
 | 
						|
	    ASSERT (len > 0) ;
 | 
						|
	    ASSERT (score >= 0) ;
 | 
						|
	    ASSERT (Col [c].shared1.thickness == 1) ;
 | 
						|
	    cp = &A [Col [c].start] ;
 | 
						|
	    cp_end = cp + len ;
 | 
						|
	    while (cp < cp_end)
 | 
						|
	    {
 | 
						|
		r = *cp++ ;
 | 
						|
		ASSERT (ROW_IS_ALIVE (r)) ;
 | 
						|
	    }
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
	    i = Col [c].shared2.order ;
 | 
						|
	    ASSERT (i >= n_col2 && i < n_col) ;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    for (r = 0 ; r < n_row ; r++)
 | 
						|
    {
 | 
						|
	if (ROW_IS_ALIVE (r))
 | 
						|
	{
 | 
						|
	    i = 0 ;
 | 
						|
	    len = Row [r].length ;
 | 
						|
	    deg = Row [r].shared1.degree ;
 | 
						|
	    ASSERT (len > 0) ;
 | 
						|
	    ASSERT (deg > 0) ;
 | 
						|
	    rp = &A [Row [r].start] ;
 | 
						|
	    rp_end = rp + len ;
 | 
						|
	    while (rp < rp_end)
 | 
						|
	    {
 | 
						|
		c = *rp++ ;
 | 
						|
		if (COL_IS_ALIVE (c))
 | 
						|
		{
 | 
						|
		    i++ ;
 | 
						|
		}
 | 
						|
	    }
 | 
						|
	    ASSERT (i > 0) ;
 | 
						|
	}
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === debug_deg_lists ====================================================== */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/*
 | 
						|
    Prints the contents of the degree lists.  Counts the number of columns
 | 
						|
    in the degree list and compares it to the total it should have.  Also
 | 
						|
    checks the row degrees.
 | 
						|
*/
 | 
						|
 | 
						|
PRIVATE void debug_deg_lists
 | 
						|
(
 | 
						|
    /* === Parameters ======================================================= */
 | 
						|
 | 
						|
    Int n_row,
 | 
						|
    Int n_col,
 | 
						|
    Colamd_Row Row [],
 | 
						|
    Colamd_Col Col [],
 | 
						|
    Int head [],
 | 
						|
    Int min_score,
 | 
						|
    Int should,
 | 
						|
    Int max_deg
 | 
						|
)
 | 
						|
{
 | 
						|
    /* === Local variables ================================================== */
 | 
						|
 | 
						|
    Int deg ;
 | 
						|
    Int col ;
 | 
						|
    Int have ;
 | 
						|
    Int row ;
 | 
						|
 | 
						|
    /* === Check the degree lists =========================================== */
 | 
						|
 | 
						|
    if (n_col > 10000 && colamd_debug <= 0)
 | 
						|
    {
 | 
						|
	return ;
 | 
						|
    }
 | 
						|
    have = 0 ;
 | 
						|
    DEBUG4 (("Degree lists: %d\n", min_score)) ;
 | 
						|
    for (deg = 0 ; deg <= n_col ; deg++)
 | 
						|
    {
 | 
						|
	col = head [deg] ;
 | 
						|
	if (col == EMPTY)
 | 
						|
	{
 | 
						|
	    continue ;
 | 
						|
	}
 | 
						|
	DEBUG4 (("%d:", deg)) ;
 | 
						|
	while (col != EMPTY)
 | 
						|
	{
 | 
						|
	    DEBUG4 ((" %d", col)) ;
 | 
						|
	    have += Col [col].shared1.thickness ;
 | 
						|
	    ASSERT (COL_IS_ALIVE (col)) ;
 | 
						|
	    col = Col [col].shared4.degree_next ;
 | 
						|
	}
 | 
						|
	DEBUG4 (("\n")) ;
 | 
						|
    }
 | 
						|
    DEBUG4 (("should %d have %d\n", should, have)) ;
 | 
						|
    ASSERT (should == have) ;
 | 
						|
 | 
						|
    /* === Check the row degrees ============================================ */
 | 
						|
 | 
						|
    if (n_row > 10000 && colamd_debug <= 0)
 | 
						|
    {
 | 
						|
	return ;
 | 
						|
    }
 | 
						|
    for (row = 0 ; row < n_row ; row++)
 | 
						|
    {
 | 
						|
	if (ROW_IS_ALIVE (row))
 | 
						|
	{
 | 
						|
	    ASSERT (Row [row].shared1.degree <= max_deg) ;
 | 
						|
	}
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === debug_mark =========================================================== */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/*
 | 
						|
    Ensures that the tag_mark is less that the maximum and also ensures that
 | 
						|
    each entry in the mark array is less than the tag mark.
 | 
						|
*/
 | 
						|
 | 
						|
PRIVATE void debug_mark
 | 
						|
(
 | 
						|
    /* === Parameters ======================================================= */
 | 
						|
 | 
						|
    Int n_row,
 | 
						|
    Colamd_Row Row [],
 | 
						|
    Int tag_mark,
 | 
						|
    Int max_mark
 | 
						|
)
 | 
						|
{
 | 
						|
    /* === Local variables ================================================== */
 | 
						|
 | 
						|
    Int r ;
 | 
						|
 | 
						|
    /* === Check the Row marks ============================================== */
 | 
						|
 | 
						|
    ASSERT (tag_mark > 0 && tag_mark <= max_mark) ;
 | 
						|
    if (n_row > 10000 && colamd_debug <= 0)
 | 
						|
    {
 | 
						|
	return ;
 | 
						|
    }
 | 
						|
    for (r = 0 ; r < n_row ; r++)
 | 
						|
    {
 | 
						|
	ASSERT (Row [r].shared2.mark < tag_mark) ;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* ========================================================================== */
 | 
						|
/* === debug_matrix ========================================================= */
 | 
						|
/* ========================================================================== */
 | 
						|
 | 
						|
/*
 | 
						|
    Prints out the contents of the columns and the rows.
 | 
						|
*/
 | 
						|
 | 
						|
PRIVATE void debug_matrix
 | 
						|
(
 | 
						|
    /* === Parameters ======================================================= */
 | 
						|
 | 
						|
    Int n_row,
 | 
						|
    Int n_col,
 | 
						|
    Colamd_Row Row [],
 | 
						|
    Colamd_Col Col [],
 | 
						|
    Int A []
 | 
						|
)
 | 
						|
{
 | 
						|
    /* === Local variables ================================================== */
 | 
						|
 | 
						|
    Int r ;
 | 
						|
    Int c ;
 | 
						|
    Int *rp ;
 | 
						|
    Int *rp_end ;
 | 
						|
    Int *cp ;
 | 
						|
    Int *cp_end ;
 | 
						|
 | 
						|
    /* === Dump the rows and columns of the matrix ========================== */
 | 
						|
 | 
						|
    if (colamd_debug < 3)
 | 
						|
    {
 | 
						|
	return ;
 | 
						|
    }
 | 
						|
    DEBUG3 (("DUMP MATRIX:\n")) ;
 | 
						|
    for (r = 0 ; r < n_row ; r++)
 | 
						|
    {
 | 
						|
	DEBUG3 (("Row %d alive? %d\n", r, ROW_IS_ALIVE (r))) ;
 | 
						|
	if (ROW_IS_DEAD (r))
 | 
						|
	{
 | 
						|
	    continue ;
 | 
						|
	}
 | 
						|
	DEBUG3 (("start %d length %d degree %d\n",
 | 
						|
		Row [r].start, Row [r].length, Row [r].shared1.degree)) ;
 | 
						|
	rp = &A [Row [r].start] ;
 | 
						|
	rp_end = rp + Row [r].length ;
 | 
						|
	while (rp < rp_end)
 | 
						|
	{
 | 
						|
	    c = *rp++ ;
 | 
						|
	    DEBUG4 (("	%d col %d\n", COL_IS_ALIVE (c), c)) ;
 | 
						|
	}
 | 
						|
    }
 | 
						|
 | 
						|
    for (c = 0 ; c < n_col ; c++)
 | 
						|
    {
 | 
						|
	DEBUG3 (("Col %d alive? %d\n", c, COL_IS_ALIVE (c))) ;
 | 
						|
	if (COL_IS_DEAD (c))
 | 
						|
	{
 | 
						|
	    continue ;
 | 
						|
	}
 | 
						|
	DEBUG3 (("start %d length %d shared1 %d shared2 %d\n",
 | 
						|
		Col [c].start, Col [c].length,
 | 
						|
		Col [c].shared1.thickness, Col [c].shared2.score)) ;
 | 
						|
	cp = &A [Col [c].start] ;
 | 
						|
	cp_end = cp + Col [c].length ;
 | 
						|
	while (cp < cp_end)
 | 
						|
	{
 | 
						|
	    r = *cp++ ;
 | 
						|
	    DEBUG4 (("	%d row %d\n", ROW_IS_ALIVE (r), r)) ;
 | 
						|
	}
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
PRIVATE void colamd_get_debug
 | 
						|
(
 | 
						|
    char *method
 | 
						|
)
 | 
						|
{
 | 
						|
    FILE *f ;
 | 
						|
    colamd_debug = 0 ;		/* no debug printing */
 | 
						|
    f = fopen ("debug", "r") ;
 | 
						|
    if (f == (FILE *) NULL)
 | 
						|
    {
 | 
						|
	colamd_debug = 0 ;
 | 
						|
    }
 | 
						|
    else
 | 
						|
    {
 | 
						|
	fscanf (f, "%d", &colamd_debug) ;
 | 
						|
	fclose (f) ;
 | 
						|
    }
 | 
						|
    DEBUG0 (("%s: debug version, D = %d (THIS WILL BE SLOW!)\n",
 | 
						|
    	method, colamd_debug)) ;
 | 
						|
}
 | 
						|
 | 
						|
#endif /* NDEBUG */
 |