Home | History | Annotate | Download | only in OrderingMethods
      1 // // This file is part of Eigen, a lightweight C++ template library
      2 // for linear algebra.
      3 //
      4 // Copyright (C) 2012 Desire Nuentsa Wakam <desire.nuentsa_wakam (at) inria.fr>
      5 //
      6 // This Source Code Form is subject to the terms of the Mozilla
      7 // Public License v. 2.0. If a copy of the MPL was not distributed
      8 // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
      9 
     10 // This file is modified from the colamd/symamd library. The copyright is below
     11 
     12 //   The authors of the code itself are Stefan I. Larimore and Timothy A.
     13 //   Davis (davis (at) cise.ufl.edu), University of Florida.  The algorithm was
     14 //   developed in collaboration with John Gilbert, Xerox PARC, and Esmond
     15 //   Ng, Oak Ridge National Laboratory.
     16 //
     17 //     Date:
     18 //
     19 //   September 8, 2003.  Version 2.3.
     20 //
     21 //     Acknowledgements:
     22 //
     23 //   This work was supported by the National Science Foundation, under
     24 //   grants DMS-9504974 and DMS-9803599.
     25 //
     26 //     Notice:
     27 //
     28 //   Copyright (c) 1998-2003 by the University of Florida.
     29 //   All Rights Reserved.
     30 //
     31 //   THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY
     32 //   EXPRESSED OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
     33 //
     34 //   Permission is hereby granted to use, copy, modify, and/or distribute
     35 //   this program, provided that the Copyright, this License, and the
     36 //   Availability of the original version is retained on all copies and made
     37 //   accessible to the end-user of any code or package that includes COLAMD
     38 //   or any modified version of COLAMD.
     39 //
     40 //     Availability:
     41 //
     42 //   The colamd/symamd library is available at
     43 //
     44 //       http://www.cise.ufl.edu/research/sparse/colamd/
     45 
     46 //   This is the http://www.cise.ufl.edu/research/sparse/colamd/colamd.h
     47 //   file.  It is required by the colamd.c, colamdmex.c, and symamdmex.c
     48 //   files, and by any C code that calls the routines whose prototypes are
     49 //   listed below, or that uses the colamd/symamd definitions listed below.
     50 
     51 #ifndef EIGEN_COLAMD_H
     52 #define EIGEN_COLAMD_H
     53 
     54 namespace internal {
     55 /* Ensure that debugging is turned off: */
     56 #ifndef COLAMD_NDEBUG
     57 #define COLAMD_NDEBUG
     58 #endif /* NDEBUG */
     59 /* ========================================================================== */
     60 /* === Knob and statistics definitions ====================================== */
     61 /* ========================================================================== */
     62 
     63 /* size of the knobs [ ] array.  Only knobs [0..1] are currently used. */
     64 #define COLAMD_KNOBS 20
     65 
     66 /* number of output statistics.  Only stats [0..6] are currently used. */
     67 #define COLAMD_STATS 20
     68 
     69 /* knobs [0] and stats [0]: dense row knob and output statistic. */
     70 #define COLAMD_DENSE_ROW 0
     71 
     72 /* knobs [1] and stats [1]: dense column knob and output statistic. */
     73 #define COLAMD_DENSE_COL 1
     74 
     75 /* stats [2]: memory defragmentation count output statistic */
     76 #define COLAMD_DEFRAG_COUNT 2
     77 
     78 /* stats [3]: colamd status:  zero OK, > 0 warning or notice, < 0 error */
     79 #define COLAMD_STATUS 3
     80 
     81 /* stats [4..6]: error info, or info on jumbled columns */
     82 #define COLAMD_INFO1 4
     83 #define COLAMD_INFO2 5
     84 #define COLAMD_INFO3 6
     85 
     86 /* error codes returned in stats [3]: */
     87 #define COLAMD_OK       (0)
     88 #define COLAMD_OK_BUT_JUMBLED     (1)
     89 #define COLAMD_ERROR_A_not_present    (-1)
     90 #define COLAMD_ERROR_p_not_present    (-2)
     91 #define COLAMD_ERROR_nrow_negative    (-3)
     92 #define COLAMD_ERROR_ncol_negative    (-4)
     93 #define COLAMD_ERROR_nnz_negative   (-5)
     94 #define COLAMD_ERROR_p0_nonzero     (-6)
     95 #define COLAMD_ERROR_A_too_small    (-7)
     96 #define COLAMD_ERROR_col_length_negative  (-8)
     97 #define COLAMD_ERROR_row_index_out_of_bounds  (-9)
     98 #define COLAMD_ERROR_out_of_memory    (-10)
     99 #define COLAMD_ERROR_internal_error   (-999)
    100 
    101 /* ========================================================================== */
    102 /* === Definitions ========================================================== */
    103 /* ========================================================================== */
    104 
    105 #define COLAMD_MAX(a,b) (((a) > (b)) ? (a) : (b))
    106 #define COLAMD_MIN(a,b) (((a) < (b)) ? (a) : (b))
    107 
    108 #define ONES_COMPLEMENT(r) (-(r)-1)
    109 
    110 /* -------------------------------------------------------------------------- */
    111 
    112 #define COLAMD_EMPTY (-1)
    113 
    114 /* Row and column status */
    115 #define ALIVE (0)
    116 #define DEAD  (-1)
    117 
    118 /* Column status */
    119 #define DEAD_PRINCIPAL    (-1)
    120 #define DEAD_NON_PRINCIPAL  (-2)
    121 
    122 /* Macros for row and column status update and checking. */
    123 #define ROW_IS_DEAD(r)      ROW_IS_MARKED_DEAD (Row[r].shared2.mark)
    124 #define ROW_IS_MARKED_DEAD(row_mark)  (row_mark < ALIVE)
    125 #define ROW_IS_ALIVE(r)     (Row [r].shared2.mark >= ALIVE)
    126 #define COL_IS_DEAD(c)      (Col [c].start < ALIVE)
    127 #define COL_IS_ALIVE(c)     (Col [c].start >= ALIVE)
    128 #define COL_IS_DEAD_PRINCIPAL(c)  (Col [c].start == DEAD_PRINCIPAL)
    129 #define KILL_ROW(r)     { Row [r].shared2.mark = DEAD ; }
    130 #define KILL_PRINCIPAL_COL(c)   { Col [c].start = DEAD_PRINCIPAL ; }
    131 #define KILL_NON_PRINCIPAL_COL(c) { Col [c].start = DEAD_NON_PRINCIPAL ; }
    132 
    133 /* ========================================================================== */
    134 /* === Colamd reporting mechanism =========================================== */
    135 /* ========================================================================== */
    136 
    137 // == Row and Column structures ==
    138 template <typename Index>
    139 struct colamd_col
    140 {
    141   Index start ;   /* index for A of first row in this column, or DEAD */
    142   /* if column is dead */
    143   Index length ;  /* number of rows in this column */
    144   union
    145   {
    146     Index thickness ; /* number of original columns represented by this */
    147     /* col, if the column is alive */
    148     Index parent ;  /* parent in parent tree super-column structure, if */
    149     /* the column is dead */
    150   } shared1 ;
    151   union
    152   {
    153     Index score ; /* the score used to maintain heap, if col is alive */
    154     Index order ; /* pivot ordering of this column, if col is dead */
    155   } shared2 ;
    156   union
    157   {
    158     Index headhash ;  /* head of a hash bucket, if col is at the head of */
    159     /* a degree list */
    160     Index hash ;  /* hash value, if col is not in a degree list */
    161     Index prev ;  /* previous column in degree list, if col is in a */
    162     /* degree list (but not at the head of a degree list) */
    163   } shared3 ;
    164   union
    165   {
    166     Index degree_next ; /* next column, if col is in a degree list */
    167     Index hash_next ;   /* next column, if col is in a hash list */
    168   } shared4 ;
    169 
    170 };
    171 
    172 template <typename Index>
    173 struct Colamd_Row
    174 {
    175   Index start ;   /* index for A of first col in this row */
    176   Index length ;  /* number of principal columns in this row */
    177   union
    178   {
    179     Index degree ;  /* number of principal & non-principal columns in row */
    180     Index p ;   /* used as a row pointer in init_rows_cols () */
    181   } shared1 ;
    182   union
    183   {
    184     Index mark ;  /* for computing set differences and marking dead rows*/
    185     Index first_column ;/* first column in row (used in garbage collection) */
    186   } shared2 ;
    187 
    188 };
    189 
    190 /* ========================================================================== */
    191 /* === Colamd recommended memory size ======================================= */
    192 /* ========================================================================== */
    193 
    194 /*
    195   The recommended length Alen of the array A passed to colamd is given by
    196   the COLAMD_RECOMMENDED (nnz, n_row, n_col) macro.  It returns -1 if any
    197   argument is negative.  2*nnz space is required for the row and column
    198   indices of the matrix. colamd_c (n_col) + colamd_r (n_row) space is
    199   required for the Col and Row arrays, respectively, which are internal to
    200   colamd.  An additional n_col space is the minimal amount of "elbow room",
    201   and nnz/5 more space is recommended for run time efficiency.
    202 
    203   This macro is not needed when using symamd.
    204 
    205   Explicit typecast to Index added Sept. 23, 2002, COLAMD version 2.2, to avoid
    206   gcc -pedantic warning messages.
    207 */
    208 template <typename Index>
    209 inline Index colamd_c(Index n_col)
    210 { return Index( ((n_col) + 1) * sizeof (colamd_col<Index>) / sizeof (Index) ) ; }
    211 
    212 template <typename Index>
    213 inline Index  colamd_r(Index n_row)
    214 { return Index(((n_row) + 1) * sizeof (Colamd_Row<Index>) / sizeof (Index)); }
    215 
    216 // Prototypes of non-user callable routines
    217 template <typename Index>
    218 static Index init_rows_cols (Index n_row, Index n_col, Colamd_Row<Index> Row [], colamd_col<Index> col [], Index A [], Index p [], Index stats[COLAMD_STATS] );
    219 
    220 template <typename Index>
    221 static void init_scoring (Index n_row, Index n_col, Colamd_Row<Index> Row [], colamd_col<Index> Col [], Index A [], Index head [], double knobs[COLAMD_KNOBS], Index *p_n_row2, Index *p_n_col2, Index *p_max_deg);
    222 
    223 template <typename Index>
    224 static Index find_ordering (Index n_row, Index n_col, Index Alen, Colamd_Row<Index> Row [], colamd_col<Index> Col [], Index A [], Index head [], Index n_col2, Index max_deg, Index pfree);
    225 
    226 template <typename Index>
    227 static void order_children (Index n_col, colamd_col<Index> Col [], Index p []);
    228 
    229 template <typename Index>
    230 static void detect_super_cols (colamd_col<Index> Col [], Index A [], Index head [], Index row_start, Index row_length ) ;
    231 
    232 template <typename Index>
    233 static Index garbage_collection (Index n_row, Index n_col, Colamd_Row<Index> Row [], colamd_col<Index> Col [], Index A [], Index *pfree) ;
    234 
    235 template <typename Index>
    236 static inline  Index clear_mark (Index n_row, Colamd_Row<Index> Row [] ) ;
    237 
    238 /* === No debugging ========================================================= */
    239 
    240 #define COLAMD_DEBUG0(params) ;
    241 #define COLAMD_DEBUG1(params) ;
    242 #define COLAMD_DEBUG2(params) ;
    243 #define COLAMD_DEBUG3(params) ;
    244 #define COLAMD_DEBUG4(params) ;
    245 
    246 #define COLAMD_ASSERT(expression) ((void) 0)
    247 
    248 
    249 /**
    250  * \brief Returns the recommended value of Alen
    251  *
    252  * Returns recommended value of Alen for use by colamd.
    253  * Returns -1 if any input argument is negative.
    254  * The use of this routine or macro is optional.
    255  * Note that the macro uses its arguments   more than once,
    256  * so be careful for side effects, if you pass expressions as arguments to COLAMD_RECOMMENDED.
    257  *
    258  * \param nnz nonzeros in A
    259  * \param n_row number of rows in A
    260  * \param n_col number of columns in A
    261  * \return recommended value of Alen for use by colamd
    262  */
    263 template <typename Index>
    264 inline Index colamd_recommended ( Index nnz, Index n_row, Index n_col)
    265 {
    266   if ((nnz) < 0 || (n_row) < 0 || (n_col) < 0)
    267     return (-1);
    268   else
    269     return (2 * (nnz) + colamd_c (n_col) + colamd_r (n_row) + (n_col) + ((nnz) / 5));
    270 }
    271 
    272 /**
    273  * \brief set default parameters  The use of this routine is optional.
    274  *
    275  * Colamd: rows with more than (knobs [COLAMD_DENSE_ROW] * n_col)
    276  * entries are removed prior to ordering.  Columns with more than
    277  * (knobs [COLAMD_DENSE_COL] * n_row) entries are removed prior to
    278  * ordering, and placed last in the output column ordering.
    279  *
    280  * COLAMD_DENSE_ROW and COLAMD_DENSE_COL are defined as 0 and 1,
    281  * respectively, in colamd.h.  Default values of these two knobs
    282  * are both 0.5.  Currently, only knobs [0] and knobs [1] are
    283  * used, but future versions may use more knobs.  If so, they will
    284  * be properly set to their defaults by the future version of
    285  * colamd_set_defaults, so that the code that calls colamd will
    286  * not need to change, assuming that you either use
    287  * colamd_set_defaults, or pass a (double *) NULL pointer as the
    288  * knobs array to colamd or symamd.
    289  *
    290  * \param knobs parameter settings for colamd
    291  */
    292 
    293 static inline void colamd_set_defaults(double knobs[COLAMD_KNOBS])
    294 {
    295   /* === Local variables ================================================== */
    296 
    297   int i ;
    298 
    299   if (!knobs)
    300   {
    301     return ;      /* no knobs to initialize */
    302   }
    303   for (i = 0 ; i < COLAMD_KNOBS ; i++)
    304   {
    305     knobs [i] = 0 ;
    306   }
    307   knobs [COLAMD_DENSE_ROW] = 0.5 ;  /* ignore rows over 50% dense */
    308   knobs [COLAMD_DENSE_COL] = 0.5 ;  /* ignore columns over 50% dense */
    309 }
    310 
    311 /**
    312  * \brief  Computes a column ordering using the column approximate minimum degree ordering
    313  *
    314  * Computes a column ordering (Q) of A such that P(AQ)=LU or
    315  * (AQ)'AQ=LL' have less fill-in and require fewer floating point
    316  * operations than factorizing the unpermuted matrix A or A'A,
    317  * respectively.
    318  *
    319  *
    320  * \param n_row number of rows in A
    321  * \param n_col number of columns in A
    322  * \param Alen, size of the array A
    323  * \param A row indices of the matrix, of size ALen
    324  * \param p column pointers of A, of size n_col+1
    325  * \param knobs parameter settings for colamd
    326  * \param stats colamd output statistics and error codes
    327  */
    328 template <typename Index>
    329 static bool colamd(Index n_row, Index n_col, Index Alen, Index *A, Index *p, double knobs[COLAMD_KNOBS], Index stats[COLAMD_STATS])
    330 {
    331   /* === Local variables ================================================== */
    332 
    333   Index i ;     /* loop index */
    334   Index nnz ;     /* nonzeros in A */
    335   Index Row_size ;    /* size of Row [], in integers */
    336   Index Col_size ;    /* size of Col [], in integers */
    337   Index need ;      /* minimum required length of A */
    338   Colamd_Row<Index> *Row ;   /* pointer into A of Row [0..n_row] array */
    339   colamd_col<Index> *Col ;   /* pointer into A of Col [0..n_col] array */
    340   Index n_col2 ;    /* number of non-dense, non-empty columns */
    341   Index n_row2 ;    /* number of non-dense, non-empty rows */
    342   Index ngarbage ;    /* number of garbage collections performed */
    343   Index max_deg ;   /* maximum row degree */
    344   double default_knobs [COLAMD_KNOBS] ; /* default knobs array */
    345 
    346 
    347   /* === Check the input arguments ======================================== */
    348 
    349   if (!stats)
    350   {
    351     COLAMD_DEBUG0 (("colamd: stats not present\n")) ;
    352     return (false) ;
    353   }
    354   for (i = 0 ; i < COLAMD_STATS ; i++)
    355   {
    356     stats [i] = 0 ;
    357   }
    358   stats [COLAMD_STATUS] = COLAMD_OK ;
    359   stats [COLAMD_INFO1] = -1 ;
    360   stats [COLAMD_INFO2] = -1 ;
    361 
    362   if (!A)   /* A is not present */
    363   {
    364     stats [COLAMD_STATUS] = COLAMD_ERROR_A_not_present ;
    365     COLAMD_DEBUG0 (("colamd: A not present\n")) ;
    366     return (false) ;
    367   }
    368 
    369   if (!p)   /* p is not present */
    370   {
    371     stats [COLAMD_STATUS] = COLAMD_ERROR_p_not_present ;
    372     COLAMD_DEBUG0 (("colamd: p not present\n")) ;
    373     return (false) ;
    374   }
    375 
    376   if (n_row < 0)  /* n_row must be >= 0 */
    377   {
    378     stats [COLAMD_STATUS] = COLAMD_ERROR_nrow_negative ;
    379     stats [COLAMD_INFO1] = n_row ;
    380     COLAMD_DEBUG0 (("colamd: nrow negative %d\n", n_row)) ;
    381     return (false) ;
    382   }
    383 
    384   if (n_col < 0)  /* n_col must be >= 0 */
    385   {
    386     stats [COLAMD_STATUS] = COLAMD_ERROR_ncol_negative ;
    387     stats [COLAMD_INFO1] = n_col ;
    388     COLAMD_DEBUG0 (("colamd: ncol negative %d\n", n_col)) ;
    389     return (false) ;
    390   }
    391 
    392   nnz = p [n_col] ;
    393   if (nnz < 0)  /* nnz must be >= 0 */
    394   {
    395     stats [COLAMD_STATUS] = COLAMD_ERROR_nnz_negative ;
    396     stats [COLAMD_INFO1] = nnz ;
    397     COLAMD_DEBUG0 (("colamd: number of entries negative %d\n", nnz)) ;
    398     return (false) ;
    399   }
    400 
    401   if (p [0] != 0)
    402   {
    403     stats [COLAMD_STATUS] = COLAMD_ERROR_p0_nonzero ;
    404     stats [COLAMD_INFO1] = p [0] ;
    405     COLAMD_DEBUG0 (("colamd: p[0] not zero %d\n", p [0])) ;
    406     return (false) ;
    407   }
    408 
    409   /* === If no knobs, set default knobs =================================== */
    410 
    411   if (!knobs)
    412   {
    413     colamd_set_defaults (default_knobs) ;
    414     knobs = default_knobs ;
    415   }
    416 
    417   /* === Allocate the Row and Col arrays from array A ===================== */
    418 
    419   Col_size = colamd_c (n_col) ;
    420   Row_size = colamd_r (n_row) ;
    421   need = 2*nnz + n_col + Col_size + Row_size ;
    422 
    423   if (need > Alen)
    424   {
    425     /* not enough space in array A to perform the ordering */
    426     stats [COLAMD_STATUS] = COLAMD_ERROR_A_too_small ;
    427     stats [COLAMD_INFO1] = need ;
    428     stats [COLAMD_INFO2] = Alen ;
    429     COLAMD_DEBUG0 (("colamd: Need Alen >= %d, given only Alen = %d\n", need,Alen));
    430     return (false) ;
    431   }
    432 
    433   Alen -= Col_size + Row_size ;
    434   Col = (colamd_col<Index> *) &A [Alen] ;
    435   Row = (Colamd_Row<Index> *) &A [Alen + Col_size] ;
    436 
    437   /* === Construct the row and column data structures ===================== */
    438 
    439   if (!Eigen::internal::init_rows_cols (n_row, n_col, Row, Col, A, p, stats))
    440   {
    441     /* input matrix is invalid */
    442     COLAMD_DEBUG0 (("colamd: Matrix invalid\n")) ;
    443     return (false) ;
    444   }
    445 
    446   /* === Initialize scores, kill dense rows/columns ======================= */
    447 
    448   Eigen::internal::init_scoring (n_row, n_col, Row, Col, A, p, knobs,
    449 		&n_row2, &n_col2, &max_deg) ;
    450 
    451   /* === Order the supercolumns =========================================== */
    452 
    453   ngarbage = Eigen::internal::find_ordering (n_row, n_col, Alen, Row, Col, A, p,
    454 			    n_col2, max_deg, 2*nnz) ;
    455 
    456   /* === Order the non-principal columns ================================== */
    457 
    458   Eigen::internal::order_children (n_col, Col, p) ;
    459 
    460   /* === Return statistics in stats ======================================= */
    461 
    462   stats [COLAMD_DENSE_ROW] = n_row - n_row2 ;
    463   stats [COLAMD_DENSE_COL] = n_col - n_col2 ;
    464   stats [COLAMD_DEFRAG_COUNT] = ngarbage ;
    465   COLAMD_DEBUG0 (("colamd: done.\n")) ;
    466   return (true) ;
    467 }
    468 
    469 /* ========================================================================== */
    470 /* === NON-USER-CALLABLE ROUTINES: ========================================== */
    471 /* ========================================================================== */
    472 
    473 /* There are no user-callable routines beyond this point in the file */
    474 
    475 
    476 /* ========================================================================== */
    477 /* === init_rows_cols ======================================================= */
    478 /* ========================================================================== */
    479 
    480 /*
    481   Takes the column form of the matrix in A and creates the row form of the
    482   matrix.  Also, row and column attributes are stored in the Col and Row
    483   structs.  If the columns are un-sorted or contain duplicate row indices,
    484   this routine will also sort and remove duplicate row indices from the
    485   column form of the matrix.  Returns false if the matrix is invalid,
    486   true otherwise.  Not user-callable.
    487 */
    488 template <typename Index>
    489 static Index init_rows_cols  /* returns true if OK, or false otherwise */
    490   (
    491     /* === Parameters ======================================================= */
    492 
    493     Index n_row,      /* number of rows of A */
    494     Index n_col,      /* number of columns of A */
    495     Colamd_Row<Index> Row [],    /* of size n_row+1 */
    496     colamd_col<Index> Col [],    /* of size n_col+1 */
    497     Index A [],     /* row indices of A, of size Alen */
    498     Index p [],     /* pointers to columns in A, of size n_col+1 */
    499     Index stats [COLAMD_STATS]  /* colamd statistics */
    500     )
    501 {
    502   /* === Local variables ================================================== */
    503 
    504   Index col ;     /* a column index */
    505   Index row ;     /* a row index */
    506   Index *cp ;     /* a column pointer */
    507   Index *cp_end ;   /* a pointer to the end of a column */
    508   Index *rp ;     /* a row pointer */
    509   Index *rp_end ;   /* a pointer to the end of a row */
    510   Index last_row ;    /* previous row */
    511 
    512   /* === Initialize columns, and check column pointers ==================== */
    513 
    514   for (col = 0 ; col < n_col ; col++)
    515   {
    516     Col [col].start = p [col] ;
    517     Col [col].length = p [col+1] - p [col] ;
    518 
    519     if (Col [col].length < 0)
    520     {
    521       /* column pointers must be non-decreasing */
    522       stats [COLAMD_STATUS] = COLAMD_ERROR_col_length_negative ;
    523       stats [COLAMD_INFO1] = col ;
    524       stats [COLAMD_INFO2] = Col [col].length ;
    525       COLAMD_DEBUG0 (("colamd: col %d length %d < 0\n", col, Col [col].length)) ;
    526       return (false) ;
    527     }
    528 
    529     Col [col].shared1.thickness = 1 ;
    530     Col [col].shared2.score = 0 ;
    531     Col [col].shared3.prev = COLAMD_EMPTY ;
    532     Col [col].shared4.degree_next = COLAMD_EMPTY ;
    533   }
    534 
    535   /* p [0..n_col] no longer needed, used as "head" in subsequent routines */
    536 
    537   /* === Scan columns, compute row degrees, and check row indices ========= */
    538 
    539   stats [COLAMD_INFO3] = 0 ;  /* number of duplicate or unsorted row indices*/
    540 
    541   for (row = 0 ; row < n_row ; row++)
    542   {
    543     Row [row].length = 0 ;
    544     Row [row].shared2.mark = -1 ;
    545   }
    546 
    547   for (col = 0 ; col < n_col ; col++)
    548   {
    549     last_row = -1 ;
    550 
    551     cp = &A [p [col]] ;
    552     cp_end = &A [p [col+1]] ;
    553 
    554     while (cp < cp_end)
    555     {
    556       row = *cp++ ;
    557 
    558       /* make sure row indices within range */
    559       if (row < 0 || row >= n_row)
    560       {
    561 	stats [COLAMD_STATUS] = COLAMD_ERROR_row_index_out_of_bounds ;
    562 	stats [COLAMD_INFO1] = col ;
    563 	stats [COLAMD_INFO2] = row ;
    564 	stats [COLAMD_INFO3] = n_row ;
    565 	COLAMD_DEBUG0 (("colamd: row %d col %d out of bounds\n", row, col)) ;
    566 	return (false) ;
    567       }
    568 
    569       if (row <= last_row || Row [row].shared2.mark == col)
    570       {
    571 	/* row index are unsorted or repeated (or both), thus col */
    572 	/* is jumbled.  This is a notice, not an error condition. */
    573 	stats [COLAMD_STATUS] = COLAMD_OK_BUT_JUMBLED ;
    574 	stats [COLAMD_INFO1] = col ;
    575 	stats [COLAMD_INFO2] = row ;
    576 	(stats [COLAMD_INFO3]) ++ ;
    577 	COLAMD_DEBUG1 (("colamd: row %d col %d unsorted/duplicate\n",row,col));
    578       }
    579 
    580       if (Row [row].shared2.mark != col)
    581       {
    582 	Row [row].length++ ;
    583       }
    584       else
    585       {
    586 	/* this is a repeated entry in the column, */
    587 	/* it will be removed */
    588 	Col [col].length-- ;
    589       }
    590 
    591       /* mark the row as having been seen in this column */
    592       Row [row].shared2.mark = col ;
    593 
    594       last_row = row ;
    595     }
    596   }
    597 
    598   /* === Compute row pointers ============================================= */
    599 
    600   /* row form of the matrix starts directly after the column */
    601   /* form of matrix in A */
    602   Row [0].start = p [n_col] ;
    603   Row [0].shared1.p = Row [0].start ;
    604   Row [0].shared2.mark = -1 ;
    605   for (row = 1 ; row < n_row ; row++)
    606   {
    607     Row [row].start = Row [row-1].start + Row [row-1].length ;
    608     Row [row].shared1.p = Row [row].start ;
    609     Row [row].shared2.mark = -1 ;
    610   }
    611 
    612   /* === Create row form ================================================== */
    613 
    614   if (stats [COLAMD_STATUS] == COLAMD_OK_BUT_JUMBLED)
    615   {
    616     /* if cols jumbled, watch for repeated row indices */
    617     for (col = 0 ; col < n_col ; col++)
    618     {
    619       cp = &A [p [col]] ;
    620       cp_end = &A [p [col+1]] ;
    621       while (cp < cp_end)
    622       {
    623 	row = *cp++ ;
    624 	if (Row [row].shared2.mark != col)
    625 	{
    626 	  A [(Row [row].shared1.p)++] = col ;
    627 	  Row [row].shared2.mark = col ;
    628 	}
    629       }
    630     }
    631   }
    632   else
    633   {
    634     /* if cols not jumbled, we don't need the mark (this is faster) */
    635     for (col = 0 ; col < n_col ; col++)
    636     {
    637       cp = &A [p [col]] ;
    638       cp_end = &A [p [col+1]] ;
    639       while (cp < cp_end)
    640       {
    641 	A [(Row [*cp++].shared1.p)++] = col ;
    642       }
    643     }
    644   }
    645 
    646   /* === Clear the row marks and set row degrees ========================== */
    647 
    648   for (row = 0 ; row < n_row ; row++)
    649   {
    650     Row [row].shared2.mark = 0 ;
    651     Row [row].shared1.degree = Row [row].length ;
    652   }
    653 
    654   /* === See if we need to re-create columns ============================== */
    655 
    656   if (stats [COLAMD_STATUS] == COLAMD_OK_BUT_JUMBLED)
    657   {
    658     COLAMD_DEBUG0 (("colamd: reconstructing column form, matrix jumbled\n")) ;
    659 
    660 
    661     /* === Compute col pointers ========================================= */
    662 
    663     /* col form of the matrix starts at A [0]. */
    664     /* Note, we may have a gap between the col form and the row */
    665     /* form if there were duplicate entries, if so, it will be */
    666     /* removed upon the first garbage collection */
    667     Col [0].start = 0 ;
    668     p [0] = Col [0].start ;
    669     for (col = 1 ; col < n_col ; col++)
    670     {
    671       /* note that the lengths here are for pruned columns, i.e. */
    672       /* no duplicate row indices will exist for these columns */
    673       Col [col].start = Col [col-1].start + Col [col-1].length ;
    674       p [col] = Col [col].start ;
    675     }
    676 
    677     /* === Re-create col form =========================================== */
    678 
    679     for (row = 0 ; row < n_row ; row++)
    680     {
    681       rp = &A [Row [row].start] ;
    682       rp_end = rp + Row [row].length ;
    683       while (rp < rp_end)
    684       {
    685 	A [(p [*rp++])++] = row ;
    686       }
    687     }
    688   }
    689 
    690   /* === Done.  Matrix is not (or no longer) jumbled ====================== */
    691 
    692   return (true) ;
    693 }
    694 
    695 
    696 /* ========================================================================== */
    697 /* === init_scoring ========================================================= */
    698 /* ========================================================================== */
    699 
    700 /*
    701   Kills dense or empty columns and rows, calculates an initial score for
    702   each column, and places all columns in the degree lists.  Not user-callable.
    703 */
    704 template <typename Index>
    705 static void init_scoring
    706   (
    707     /* === Parameters ======================================================= */
    708 
    709     Index n_row,      /* number of rows of A */
    710     Index n_col,      /* number of columns of A */
    711     Colamd_Row<Index> Row [],    /* of size n_row+1 */
    712     colamd_col<Index> Col [],    /* of size n_col+1 */
    713     Index A [],     /* column form and row form of A */
    714     Index head [],    /* of size n_col+1 */
    715     double knobs [COLAMD_KNOBS],/* parameters */
    716     Index *p_n_row2,    /* number of non-dense, non-empty rows */
    717     Index *p_n_col2,    /* number of non-dense, non-empty columns */
    718     Index *p_max_deg    /* maximum row degree */
    719     )
    720 {
    721   /* === Local variables ================================================== */
    722 
    723   Index c ;     /* a column index */
    724   Index r, row ;    /* a row index */
    725   Index *cp ;     /* a column pointer */
    726   Index deg ;     /* degree of a row or column */
    727   Index *cp_end ;   /* a pointer to the end of a column */
    728   Index *new_cp ;   /* new column pointer */
    729   Index col_length ;    /* length of pruned column */
    730   Index score ;     /* current column score */
    731   Index n_col2 ;    /* number of non-dense, non-empty columns */
    732   Index n_row2 ;    /* number of non-dense, non-empty rows */
    733   Index dense_row_count ; /* remove rows with more entries than this */
    734   Index dense_col_count ; /* remove cols with more entries than this */
    735   Index min_score ;   /* smallest column score */
    736   Index max_deg ;   /* maximum row degree */
    737   Index next_col ;    /* Used to add to degree list.*/
    738 
    739 
    740   /* === Extract knobs ==================================================== */
    741 
    742   dense_row_count = COLAMD_MAX (0, COLAMD_MIN (knobs [COLAMD_DENSE_ROW] * n_col, n_col)) ;
    743   dense_col_count = COLAMD_MAX (0, COLAMD_MIN (knobs [COLAMD_DENSE_COL] * n_row, n_row)) ;
    744   COLAMD_DEBUG1 (("colamd: densecount: %d %d\n", dense_row_count, dense_col_count)) ;
    745   max_deg = 0 ;
    746   n_col2 = n_col ;
    747   n_row2 = n_row ;
    748 
    749   /* === Kill empty columns =============================================== */
    750 
    751   /* Put the empty columns at the end in their natural order, so that LU */
    752   /* factorization can proceed as far as possible. */
    753   for (c = n_col-1 ; c >= 0 ; c--)
    754   {
    755     deg = Col [c].length ;
    756     if (deg == 0)
    757     {
    758       /* this is a empty column, kill and order it last */
    759       Col [c].shared2.order = --n_col2 ;
    760       KILL_PRINCIPAL_COL (c) ;
    761     }
    762   }
    763   COLAMD_DEBUG1 (("colamd: null columns killed: %d\n", n_col - n_col2)) ;
    764 
    765   /* === Kill dense columns =============================================== */
    766 
    767   /* Put the dense columns at the end, in their natural order */
    768   for (c = n_col-1 ; c >= 0 ; c--)
    769   {
    770     /* skip any dead columns */
    771     if (COL_IS_DEAD (c))
    772     {
    773       continue ;
    774     }
    775     deg = Col [c].length ;
    776     if (deg > dense_col_count)
    777     {
    778       /* this is a dense column, kill and order it last */
    779       Col [c].shared2.order = --n_col2 ;
    780       /* decrement the row degrees */
    781       cp = &A [Col [c].start] ;
    782       cp_end = cp + Col [c].length ;
    783       while (cp < cp_end)
    784       {
    785 	Row [*cp++].shared1.degree-- ;
    786       }
    787       KILL_PRINCIPAL_COL (c) ;
    788     }
    789   }
    790   COLAMD_DEBUG1 (("colamd: Dense and null columns killed: %d\n", n_col - n_col2)) ;
    791 
    792   /* === Kill dense and empty rows ======================================== */
    793 
    794   for (r = 0 ; r < n_row ; r++)
    795   {
    796     deg = Row [r].shared1.degree ;
    797     COLAMD_ASSERT (deg >= 0 && deg <= n_col) ;
    798     if (deg > dense_row_count || deg == 0)
    799     {
    800       /* kill a dense or empty row */
    801       KILL_ROW (r) ;
    802       --n_row2 ;
    803     }
    804     else
    805     {
    806       /* keep track of max degree of remaining rows */
    807       max_deg = COLAMD_MAX (max_deg, deg) ;
    808     }
    809   }
    810   COLAMD_DEBUG1 (("colamd: Dense and null rows killed: %d\n", n_row - n_row2)) ;
    811 
    812   /* === Compute initial column scores ==================================== */
    813 
    814   /* At this point the row degrees are accurate.  They reflect the number */
    815   /* of "live" (non-dense) columns in each row.  No empty rows exist. */
    816   /* Some "live" columns may contain only dead rows, however.  These are */
    817   /* pruned in the code below. */
    818 
    819   /* now find the initial matlab score for each column */
    820   for (c = n_col-1 ; c >= 0 ; c--)
    821   {
    822     /* skip dead column */
    823     if (COL_IS_DEAD (c))
    824     {
    825       continue ;
    826     }
    827     score = 0 ;
    828     cp = &A [Col [c].start] ;
    829     new_cp = cp ;
    830     cp_end = cp + Col [c].length ;
    831     while (cp < cp_end)
    832     {
    833       /* get a row */
    834       row = *cp++ ;
    835       /* skip if dead */
    836       if (ROW_IS_DEAD (row))
    837       {
    838 	continue ;
    839       }
    840       /* compact the column */
    841       *new_cp++ = row ;
    842       /* add row's external degree */
    843       score += Row [row].shared1.degree - 1 ;
    844       /* guard against integer overflow */
    845       score = COLAMD_MIN (score, n_col) ;
    846     }
    847     /* determine pruned column length */
    848     col_length = (Index) (new_cp - &A [Col [c].start]) ;
    849     if (col_length == 0)
    850     {
    851       /* a newly-made null column (all rows in this col are "dense" */
    852       /* and have already been killed) */
    853       COLAMD_DEBUG2 (("Newly null killed: %d\n", c)) ;
    854       Col [c].shared2.order = --n_col2 ;
    855       KILL_PRINCIPAL_COL (c) ;
    856     }
    857     else
    858     {
    859       /* set column length and set score */
    860       COLAMD_ASSERT (score >= 0) ;
    861       COLAMD_ASSERT (score <= n_col) ;
    862       Col [c].length = col_length ;
    863       Col [c].shared2.score = score ;
    864     }
    865   }
    866   COLAMD_DEBUG1 (("colamd: Dense, null, and newly-null columns killed: %d\n",
    867 		  n_col-n_col2)) ;
    868 
    869   /* At this point, all empty rows and columns are dead.  All live columns */
    870   /* are "clean" (containing no dead rows) and simplicial (no supercolumns */
    871   /* yet).  Rows may contain dead columns, but all live rows contain at */
    872   /* least one live column. */
    873 
    874   /* === Initialize degree lists ========================================== */
    875 
    876 
    877   /* clear the hash buckets */
    878   for (c = 0 ; c <= n_col ; c++)
    879   {
    880     head [c] = COLAMD_EMPTY ;
    881   }
    882   min_score = n_col ;
    883   /* place in reverse order, so low column indices are at the front */
    884   /* of the lists.  This is to encourage natural tie-breaking */
    885   for (c = n_col-1 ; c >= 0 ; c--)
    886   {
    887     /* only add principal columns to degree lists */
    888     if (COL_IS_ALIVE (c))
    889     {
    890       COLAMD_DEBUG4 (("place %d score %d minscore %d ncol %d\n",
    891 		      c, Col [c].shared2.score, min_score, n_col)) ;
    892 
    893       /* === Add columns score to DList =============================== */
    894 
    895       score = Col [c].shared2.score ;
    896 
    897       COLAMD_ASSERT (min_score >= 0) ;
    898       COLAMD_ASSERT (min_score <= n_col) ;
    899       COLAMD_ASSERT (score >= 0) ;
    900       COLAMD_ASSERT (score <= n_col) ;
    901       COLAMD_ASSERT (head [score] >= COLAMD_EMPTY) ;
    902 
    903       /* now add this column to dList at proper score location */
    904       next_col = head [score] ;
    905       Col [c].shared3.prev = COLAMD_EMPTY ;
    906       Col [c].shared4.degree_next = next_col ;
    907 
    908       /* if there already was a column with the same score, set its */
    909       /* previous pointer to this new column */
    910       if (next_col != COLAMD_EMPTY)
    911       {
    912 	Col [next_col].shared3.prev = c ;
    913       }
    914       head [score] = c ;
    915 
    916       /* see if this score is less than current min */
    917       min_score = COLAMD_MIN (min_score, score) ;
    918 
    919 
    920     }
    921   }
    922 
    923 
    924   /* === Return number of remaining columns, and max row degree =========== */
    925 
    926   *p_n_col2 = n_col2 ;
    927   *p_n_row2 = n_row2 ;
    928   *p_max_deg = max_deg ;
    929 }
    930 
    931 
    932 /* ========================================================================== */
    933 /* === find_ordering ======================================================== */
    934 /* ========================================================================== */
    935 
    936 /*
    937   Order the principal columns of the supercolumn form of the matrix
    938   (no supercolumns on input).  Uses a minimum approximate column minimum
    939   degree ordering method.  Not user-callable.
    940 */
    941 template <typename Index>
    942 static Index find_ordering /* return the number of garbage collections */
    943   (
    944     /* === Parameters ======================================================= */
    945 
    946     Index n_row,      /* number of rows of A */
    947     Index n_col,      /* number of columns of A */
    948     Index Alen,     /* size of A, 2*nnz + n_col or larger */
    949     Colamd_Row<Index> Row [],    /* of size n_row+1 */
    950     colamd_col<Index> Col [],    /* of size n_col+1 */
    951     Index A [],     /* column form and row form of A */
    952     Index head [],    /* of size n_col+1 */
    953     Index n_col2,     /* Remaining columns to order */
    954     Index max_deg,    /* Maximum row degree */
    955     Index pfree     /* index of first free slot (2*nnz on entry) */
    956     )
    957 {
    958   /* === Local variables ================================================== */
    959 
    960   Index k ;     /* current pivot ordering step */
    961   Index pivot_col ;   /* current pivot column */
    962   Index *cp ;     /* a column pointer */
    963   Index *rp ;     /* a row pointer */
    964   Index pivot_row ;   /* current pivot row */
    965   Index *new_cp ;   /* modified column pointer */
    966   Index *new_rp ;   /* modified row pointer */
    967   Index pivot_row_start ; /* pointer to start of pivot row */
    968   Index pivot_row_degree ;  /* number of columns in pivot row */
    969   Index pivot_row_length ;  /* number of supercolumns in pivot row */
    970   Index pivot_col_score ; /* score of pivot column */
    971   Index needed_memory ;   /* free space needed for pivot row */
    972   Index *cp_end ;   /* pointer to the end of a column */
    973   Index *rp_end ;   /* pointer to the end of a row */
    974   Index row ;     /* a row index */
    975   Index col ;     /* a column index */
    976   Index max_score ;   /* maximum possible score */
    977   Index cur_score ;   /* score of current column */
    978   unsigned int hash ;   /* hash value for supernode detection */
    979   Index head_column ;   /* head of hash bucket */
    980   Index first_col ;   /* first column in hash bucket */
    981   Index tag_mark ;    /* marker value for mark array */
    982   Index row_mark ;    /* Row [row].shared2.mark */
    983   Index set_difference ;  /* set difference size of row with pivot row */
    984   Index min_score ;   /* smallest column score */
    985   Index col_thickness ;   /* "thickness" (no. of columns in a supercol) */
    986   Index max_mark ;    /* maximum value of tag_mark */
    987   Index pivot_col_thickness ; /* number of columns represented by pivot col */
    988   Index prev_col ;    /* Used by Dlist operations. */
    989   Index next_col ;    /* Used by Dlist operations. */
    990   Index ngarbage ;    /* number of garbage collections performed */
    991 
    992 
    993   /* === Initialization and clear mark ==================================== */
    994 
    995   max_mark = INT_MAX - n_col ;  /* INT_MAX defined in <limits.h> */
    996   tag_mark = Eigen::internal::clear_mark (n_row, Row) ;
    997   min_score = 0 ;
    998   ngarbage = 0 ;
    999   COLAMD_DEBUG1 (("colamd: Ordering, n_col2=%d\n", n_col2)) ;
   1000 
   1001   /* === Order the columns ================================================ */
   1002 
   1003   for (k = 0 ; k < n_col2 ; /* 'k' is incremented below */)
   1004   {
   1005 
   1006     /* === Select pivot column, and order it ============================ */
   1007 
   1008     /* make sure degree list isn't empty */
   1009     COLAMD_ASSERT (min_score >= 0) ;
   1010     COLAMD_ASSERT (min_score <= n_col) ;
   1011     COLAMD_ASSERT (head [min_score] >= COLAMD_EMPTY) ;
   1012 
   1013     /* get pivot column from head of minimum degree list */
   1014     while (head [min_score] == COLAMD_EMPTY && min_score < n_col)
   1015     {
   1016       min_score++ ;
   1017     }
   1018     pivot_col = head [min_score] ;
   1019     COLAMD_ASSERT (pivot_col >= 0 && pivot_col <= n_col) ;
   1020     next_col = Col [pivot_col].shared4.degree_next ;
   1021     head [min_score] = next_col ;
   1022     if (next_col != COLAMD_EMPTY)
   1023     {
   1024       Col [next_col].shared3.prev = COLAMD_EMPTY ;
   1025     }
   1026 
   1027     COLAMD_ASSERT (COL_IS_ALIVE (pivot_col)) ;
   1028     COLAMD_DEBUG3 (("Pivot col: %d\n", pivot_col)) ;
   1029 
   1030     /* remember score for defrag check */
   1031     pivot_col_score = Col [pivot_col].shared2.score ;
   1032 
   1033     /* the pivot column is the kth column in the pivot order */
   1034     Col [pivot_col].shared2.order = k ;
   1035 
   1036     /* increment order count by column thickness */
   1037     pivot_col_thickness = Col [pivot_col].shared1.thickness ;
   1038     k += pivot_col_thickness ;
   1039     COLAMD_ASSERT (pivot_col_thickness > 0) ;
   1040 
   1041     /* === Garbage_collection, if necessary ============================= */
   1042 
   1043     needed_memory = COLAMD_MIN (pivot_col_score, n_col - k) ;
   1044     if (pfree + needed_memory >= Alen)
   1045     {
   1046       pfree = Eigen::internal::garbage_collection (n_row, n_col, Row, Col, A, &A [pfree]) ;
   1047       ngarbage++ ;
   1048       /* after garbage collection we will have enough */
   1049       COLAMD_ASSERT (pfree + needed_memory < Alen) ;
   1050       /* garbage collection has wiped out the Row[].shared2.mark array */
   1051       tag_mark = Eigen::internal::clear_mark (n_row, Row) ;
   1052 
   1053     }
   1054 
   1055     /* === Compute pivot row pattern ==================================== */
   1056 
   1057     /* get starting location for this new merged row */
   1058     pivot_row_start = pfree ;
   1059 
   1060     /* initialize new row counts to zero */
   1061     pivot_row_degree = 0 ;
   1062 
   1063     /* tag pivot column as having been visited so it isn't included */
   1064     /* in merged pivot row */
   1065     Col [pivot_col].shared1.thickness = -pivot_col_thickness ;
   1066 
   1067     /* pivot row is the union of all rows in the pivot column pattern */
   1068     cp = &A [Col [pivot_col].start] ;
   1069     cp_end = cp + Col [pivot_col].length ;
   1070     while (cp < cp_end)
   1071     {
   1072       /* get a row */
   1073       row = *cp++ ;
   1074       COLAMD_DEBUG4 (("Pivot col pattern %d %d\n", ROW_IS_ALIVE (row), row)) ;
   1075       /* skip if row is dead */
   1076       if (ROW_IS_DEAD (row))
   1077       {
   1078 	continue ;
   1079       }
   1080       rp = &A [Row [row].start] ;
   1081       rp_end = rp + Row [row].length ;
   1082       while (rp < rp_end)
   1083       {
   1084 	/* get a column */
   1085 	col = *rp++ ;
   1086 	/* add the column, if alive and untagged */
   1087 	col_thickness = Col [col].shared1.thickness ;
   1088 	if (col_thickness > 0 && COL_IS_ALIVE (col))
   1089 	{
   1090 	  /* tag column in pivot row */
   1091 	  Col [col].shared1.thickness = -col_thickness ;
   1092 	  COLAMD_ASSERT (pfree < Alen) ;
   1093 	  /* place column in pivot row */
   1094 	  A [pfree++] = col ;
   1095 	  pivot_row_degree += col_thickness ;
   1096 	}
   1097       }
   1098     }
   1099 
   1100     /* clear tag on pivot column */
   1101     Col [pivot_col].shared1.thickness = pivot_col_thickness ;
   1102     max_deg = COLAMD_MAX (max_deg, pivot_row_degree) ;
   1103 
   1104 
   1105     /* === Kill all rows used to construct pivot row ==================== */
   1106 
   1107     /* also kill pivot row, temporarily */
   1108     cp = &A [Col [pivot_col].start] ;
   1109     cp_end = cp + Col [pivot_col].length ;
   1110     while (cp < cp_end)
   1111     {
   1112       /* may be killing an already dead row */
   1113       row = *cp++ ;
   1114       COLAMD_DEBUG3 (("Kill row in pivot col: %d\n", row)) ;
   1115       KILL_ROW (row) ;
   1116     }
   1117 
   1118     /* === Select a row index to use as the new pivot row =============== */
   1119 
   1120     pivot_row_length = pfree - pivot_row_start ;
   1121     if (pivot_row_length > 0)
   1122     {
   1123       /* pick the "pivot" row arbitrarily (first row in col) */
   1124       pivot_row = A [Col [pivot_col].start] ;
   1125       COLAMD_DEBUG3 (("Pivotal row is %d\n", pivot_row)) ;
   1126     }
   1127     else
   1128     {
   1129       /* there is no pivot row, since it is of zero length */
   1130       pivot_row = COLAMD_EMPTY ;
   1131       COLAMD_ASSERT (pivot_row_length == 0) ;
   1132     }
   1133     COLAMD_ASSERT (Col [pivot_col].length > 0 || pivot_row_length == 0) ;
   1134 
   1135     /* === Approximate degree computation =============================== */
   1136 
   1137     /* Here begins the computation of the approximate degree.  The column */
   1138     /* score is the sum of the pivot row "length", plus the size of the */
   1139     /* set differences of each row in the column minus the pattern of the */
   1140     /* pivot row itself.  The column ("thickness") itself is also */
   1141     /* excluded from the column score (we thus use an approximate */
   1142     /* external degree). */
   1143 
   1144     /* The time taken by the following code (compute set differences, and */
   1145     /* add them up) is proportional to the size of the data structure */
   1146     /* being scanned - that is, the sum of the sizes of each column in */
   1147     /* the pivot row.  Thus, the amortized time to compute a column score */
   1148     /* is proportional to the size of that column (where size, in this */
   1149     /* context, is the column "length", or the number of row indices */
   1150     /* in that column).  The number of row indices in a column is */
   1151     /* monotonically non-decreasing, from the length of the original */
   1152     /* column on input to colamd. */
   1153 
   1154     /* === Compute set differences ====================================== */
   1155 
   1156     COLAMD_DEBUG3 (("** Computing set differences phase. **\n")) ;
   1157 
   1158     /* pivot row is currently dead - it will be revived later. */
   1159 
   1160     COLAMD_DEBUG3 (("Pivot row: ")) ;
   1161     /* for each column in pivot row */
   1162     rp = &A [pivot_row_start] ;
   1163     rp_end = rp + pivot_row_length ;
   1164     while (rp < rp_end)
   1165     {
   1166       col = *rp++ ;
   1167       COLAMD_ASSERT (COL_IS_ALIVE (col) && col != pivot_col) ;
   1168       COLAMD_DEBUG3 (("Col: %d\n", col)) ;
   1169 
   1170       /* clear tags used to construct pivot row pattern */
   1171       col_thickness = -Col [col].shared1.thickness ;
   1172       COLAMD_ASSERT (col_thickness > 0) ;
   1173       Col [col].shared1.thickness = col_thickness ;
   1174 
   1175       /* === Remove column from degree list =========================== */
   1176 
   1177       cur_score = Col [col].shared2.score ;
   1178       prev_col = Col [col].shared3.prev ;
   1179       next_col = Col [col].shared4.degree_next ;
   1180       COLAMD_ASSERT (cur_score >= 0) ;
   1181       COLAMD_ASSERT (cur_score <= n_col) ;
   1182       COLAMD_ASSERT (cur_score >= COLAMD_EMPTY) ;
   1183       if (prev_col == COLAMD_EMPTY)
   1184       {
   1185 	head [cur_score] = next_col ;
   1186       }
   1187       else
   1188       {
   1189 	Col [prev_col].shared4.degree_next = next_col ;
   1190       }
   1191       if (next_col != COLAMD_EMPTY)
   1192       {
   1193 	Col [next_col].shared3.prev = prev_col ;
   1194       }
   1195 
   1196       /* === Scan the column ========================================== */
   1197 
   1198       cp = &A [Col [col].start] ;
   1199       cp_end = cp + Col [col].length ;
   1200       while (cp < cp_end)
   1201       {
   1202 	/* get a row */
   1203 	row = *cp++ ;
   1204 	row_mark = Row [row].shared2.mark ;
   1205 	/* skip if dead */
   1206 	if (ROW_IS_MARKED_DEAD (row_mark))
   1207 	{
   1208 	  continue ;
   1209 	}
   1210 	COLAMD_ASSERT (row != pivot_row) ;
   1211 	set_difference = row_mark - tag_mark ;
   1212 	/* check if the row has been seen yet */
   1213 	if (set_difference < 0)
   1214 	{
   1215 	  COLAMD_ASSERT (Row [row].shared1.degree <= max_deg) ;
   1216 	  set_difference = Row [row].shared1.degree ;
   1217 	}
   1218 	/* subtract column thickness from this row's set difference */
   1219 	set_difference -= col_thickness ;
   1220 	COLAMD_ASSERT (set_difference >= 0) ;
   1221 	/* absorb this row if the set difference becomes zero */
   1222 	if (set_difference == 0)
   1223 	{
   1224 	  COLAMD_DEBUG3 (("aggressive absorption. Row: %d\n", row)) ;
   1225 	  KILL_ROW (row) ;
   1226 	}
   1227 	else
   1228 	{
   1229 	  /* save the new mark */
   1230 	  Row [row].shared2.mark = set_difference + tag_mark ;
   1231 	}
   1232       }
   1233     }
   1234 
   1235 
   1236     /* === Add up set differences for each column ======================= */
   1237 
   1238     COLAMD_DEBUG3 (("** Adding set differences phase. **\n")) ;
   1239 
   1240     /* for each column in pivot row */
   1241     rp = &A [pivot_row_start] ;
   1242     rp_end = rp + pivot_row_length ;
   1243     while (rp < rp_end)
   1244     {
   1245       /* get a column */
   1246       col = *rp++ ;
   1247       COLAMD_ASSERT (COL_IS_ALIVE (col) && col != pivot_col) ;
   1248       hash = 0 ;
   1249       cur_score = 0 ;
   1250       cp = &A [Col [col].start] ;
   1251       /* compact the column */
   1252       new_cp = cp ;
   1253       cp_end = cp + Col [col].length ;
   1254 
   1255       COLAMD_DEBUG4 (("Adding set diffs for Col: %d.\n", col)) ;
   1256 
   1257       while (cp < cp_end)
   1258       {
   1259 	/* get a row */
   1260 	row = *cp++ ;
   1261 	COLAMD_ASSERT(row >= 0 && row < n_row) ;
   1262 	row_mark = Row [row].shared2.mark ;
   1263 	/* skip if dead */
   1264 	if (ROW_IS_MARKED_DEAD (row_mark))
   1265 	{
   1266 	  continue ;
   1267 	}
   1268 	COLAMD_ASSERT (row_mark > tag_mark) ;
   1269 	/* compact the column */
   1270 	*new_cp++ = row ;
   1271 	/* compute hash function */
   1272 	hash += row ;
   1273 	/* add set difference */
   1274 	cur_score += row_mark - tag_mark ;
   1275 	/* integer overflow... */
   1276 	cur_score = COLAMD_MIN (cur_score, n_col) ;
   1277       }
   1278 
   1279       /* recompute the column's length */
   1280       Col [col].length = (Index) (new_cp - &A [Col [col].start]) ;
   1281 
   1282       /* === Further mass elimination ================================= */
   1283 
   1284       if (Col [col].length == 0)
   1285       {
   1286 	COLAMD_DEBUG4 (("further mass elimination. Col: %d\n", col)) ;
   1287 	/* nothing left but the pivot row in this column */
   1288 	KILL_PRINCIPAL_COL (col) ;
   1289 	pivot_row_degree -= Col [col].shared1.thickness ;
   1290 	COLAMD_ASSERT (pivot_row_degree >= 0) ;
   1291 	/* order it */
   1292 	Col [col].shared2.order = k ;
   1293 	/* increment order count by column thickness */
   1294 	k += Col [col].shared1.thickness ;
   1295       }
   1296       else
   1297       {
   1298 	/* === Prepare for supercolumn detection ==================== */
   1299 
   1300 	COLAMD_DEBUG4 (("Preparing supercol detection for Col: %d.\n", col)) ;
   1301 
   1302 	/* save score so far */
   1303 	Col [col].shared2.score = cur_score ;
   1304 
   1305 	/* add column to hash table, for supercolumn detection */
   1306 	hash %= n_col + 1 ;
   1307 
   1308 	COLAMD_DEBUG4 ((" Hash = %d, n_col = %d.\n", hash, n_col)) ;
   1309 	COLAMD_ASSERT (hash <= n_col) ;
   1310 
   1311 	head_column = head [hash] ;
   1312 	if (head_column > COLAMD_EMPTY)
   1313 	{
   1314 	  /* degree list "hash" is non-empty, use prev (shared3) of */
   1315 	  /* first column in degree list as head of hash bucket */
   1316 	  first_col = Col [head_column].shared3.headhash ;
   1317 	  Col [head_column].shared3.headhash = col ;
   1318 	}
   1319 	else
   1320 	{
   1321 	  /* degree list "hash" is empty, use head as hash bucket */
   1322 	  first_col = - (head_column + 2) ;
   1323 	  head [hash] = - (col + 2) ;
   1324 	}
   1325 	Col [col].shared4.hash_next = first_col ;
   1326 
   1327 	/* save hash function in Col [col].shared3.hash */
   1328 	Col [col].shared3.hash = (Index) hash ;
   1329 	COLAMD_ASSERT (COL_IS_ALIVE (col)) ;
   1330       }
   1331     }
   1332 
   1333     /* The approximate external column degree is now computed.  */
   1334 
   1335     /* === Supercolumn detection ======================================== */
   1336 
   1337     COLAMD_DEBUG3 (("** Supercolumn detection phase. **\n")) ;
   1338 
   1339     Eigen::internal::detect_super_cols (Col, A, head, pivot_row_start, pivot_row_length) ;
   1340 
   1341     /* === Kill the pivotal column ====================================== */
   1342 
   1343     KILL_PRINCIPAL_COL (pivot_col) ;
   1344 
   1345     /* === Clear mark =================================================== */
   1346 
   1347     tag_mark += (max_deg + 1) ;
   1348     if (tag_mark >= max_mark)
   1349     {
   1350       COLAMD_DEBUG2 (("clearing tag_mark\n")) ;
   1351       tag_mark = Eigen::internal::clear_mark (n_row, Row) ;
   1352     }
   1353 
   1354     /* === Finalize the new pivot row, and column scores ================ */
   1355 
   1356     COLAMD_DEBUG3 (("** Finalize scores phase. **\n")) ;
   1357 
   1358     /* for each column in pivot row */
   1359     rp = &A [pivot_row_start] ;
   1360     /* compact the pivot row */
   1361     new_rp = rp ;
   1362     rp_end = rp + pivot_row_length ;
   1363     while (rp < rp_end)
   1364     {
   1365       col = *rp++ ;
   1366       /* skip dead columns */
   1367       if (COL_IS_DEAD (col))
   1368       {
   1369 	continue ;
   1370       }
   1371       *new_rp++ = col ;
   1372       /* add new pivot row to column */
   1373       A [Col [col].start + (Col [col].length++)] = pivot_row ;
   1374 
   1375       /* retrieve score so far and add on pivot row's degree. */
   1376       /* (we wait until here for this in case the pivot */
   1377       /* row's degree was reduced due to mass elimination). */
   1378       cur_score = Col [col].shared2.score + pivot_row_degree ;
   1379 
   1380       /* calculate the max possible score as the number of */
   1381       /* external columns minus the 'k' value minus the */
   1382       /* columns thickness */
   1383       max_score = n_col - k - Col [col].shared1.thickness ;
   1384 
   1385       /* make the score the external degree of the union-of-rows */
   1386       cur_score -= Col [col].shared1.thickness ;
   1387 
   1388       /* make sure score is less or equal than the max score */
   1389       cur_score = COLAMD_MIN (cur_score, max_score) ;
   1390       COLAMD_ASSERT (cur_score >= 0) ;
   1391 
   1392       /* store updated score */
   1393       Col [col].shared2.score = cur_score ;
   1394 
   1395       /* === Place column back in degree list ========================= */
   1396 
   1397       COLAMD_ASSERT (min_score >= 0) ;
   1398       COLAMD_ASSERT (min_score <= n_col) ;
   1399       COLAMD_ASSERT (cur_score >= 0) ;
   1400       COLAMD_ASSERT (cur_score <= n_col) ;
   1401       COLAMD_ASSERT (head [cur_score] >= COLAMD_EMPTY) ;
   1402       next_col = head [cur_score] ;
   1403       Col [col].shared4.degree_next = next_col ;
   1404       Col [col].shared3.prev = COLAMD_EMPTY ;
   1405       if (next_col != COLAMD_EMPTY)
   1406       {
   1407 	Col [next_col].shared3.prev = col ;
   1408       }
   1409       head [cur_score] = col ;
   1410 
   1411       /* see if this score is less than current min */
   1412       min_score = COLAMD_MIN (min_score, cur_score) ;
   1413 
   1414     }
   1415 
   1416     /* === Resurrect the new pivot row ================================== */
   1417 
   1418     if (pivot_row_degree > 0)
   1419     {
   1420       /* update pivot row length to reflect any cols that were killed */
   1421       /* during super-col detection and mass elimination */
   1422       Row [pivot_row].start  = pivot_row_start ;
   1423       Row [pivot_row].length = (Index) (new_rp - &A[pivot_row_start]) ;
   1424       Row [pivot_row].shared1.degree = pivot_row_degree ;
   1425       Row [pivot_row].shared2.mark = 0 ;
   1426       /* pivot row is no longer dead */
   1427     }
   1428   }
   1429 
   1430   /* === All principal columns have now been ordered ====================== */
   1431 
   1432   return (ngarbage) ;
   1433 }
   1434 
   1435 
   1436 /* ========================================================================== */
   1437 /* === order_children ======================================================= */
   1438 /* ========================================================================== */
   1439 
   1440 /*
   1441   The find_ordering routine has ordered all of the principal columns (the
   1442   representatives of the supercolumns).  The non-principal columns have not
   1443   yet been ordered.  This routine orders those columns by walking up the
   1444   parent tree (a column is a child of the column which absorbed it).  The
   1445   final permutation vector is then placed in p [0 ... n_col-1], with p [0]
   1446   being the first column, and p [n_col-1] being the last.  It doesn't look
   1447   like it at first glance, but be assured that this routine takes time linear
   1448   in the number of columns.  Although not immediately obvious, the time
   1449   taken by this routine is O (n_col), that is, linear in the number of
   1450   columns.  Not user-callable.
   1451 */
   1452 template <typename Index>
   1453 static inline  void order_children
   1454 (
   1455   /* === Parameters ======================================================= */
   1456 
   1457   Index n_col,      /* number of columns of A */
   1458   colamd_col<Index> Col [],    /* of size n_col+1 */
   1459   Index p []      /* p [0 ... n_col-1] is the column permutation*/
   1460   )
   1461 {
   1462   /* === Local variables ================================================== */
   1463 
   1464   Index i ;     /* loop counter for all columns */
   1465   Index c ;     /* column index */
   1466   Index parent ;    /* index of column's parent */
   1467   Index order ;     /* column's order */
   1468 
   1469   /* === Order each non-principal column ================================== */
   1470 
   1471   for (i = 0 ; i < n_col ; i++)
   1472   {
   1473     /* find an un-ordered non-principal column */
   1474     COLAMD_ASSERT (COL_IS_DEAD (i)) ;
   1475     if (!COL_IS_DEAD_PRINCIPAL (i) && Col [i].shared2.order == COLAMD_EMPTY)
   1476     {
   1477       parent = i ;
   1478       /* once found, find its principal parent */
   1479       do
   1480       {
   1481 	parent = Col [parent].shared1.parent ;
   1482       } while (!COL_IS_DEAD_PRINCIPAL (parent)) ;
   1483 
   1484       /* now, order all un-ordered non-principal columns along path */
   1485       /* to this parent.  collapse tree at the same time */
   1486       c = i ;
   1487       /* get order of parent */
   1488       order = Col [parent].shared2.order ;
   1489 
   1490       do
   1491       {
   1492 	COLAMD_ASSERT (Col [c].shared2.order == COLAMD_EMPTY) ;
   1493 
   1494 	/* order this column */
   1495 	Col [c].shared2.order = order++ ;
   1496 	/* collaps tree */
   1497 	Col [c].shared1.parent = parent ;
   1498 
   1499 	/* get immediate parent of this column */
   1500 	c = Col [c].shared1.parent ;
   1501 
   1502 	/* continue until we hit an ordered column.  There are */
   1503 	/* guarranteed not to be anymore unordered columns */
   1504 	/* above an ordered column */
   1505       } while (Col [c].shared2.order == COLAMD_EMPTY) ;
   1506 
   1507       /* re-order the super_col parent to largest order for this group */
   1508       Col [parent].shared2.order = order ;
   1509     }
   1510   }
   1511 
   1512   /* === Generate the permutation ========================================= */
   1513 
   1514   for (c = 0 ; c < n_col ; c++)
   1515   {
   1516     p [Col [c].shared2.order] = c ;
   1517   }
   1518 }
   1519 
   1520 
   1521 /* ========================================================================== */
   1522 /* === detect_super_cols ==================================================== */
   1523 /* ========================================================================== */
   1524 
   1525 /*
   1526   Detects supercolumns by finding matches between columns in the hash buckets.
   1527   Check amongst columns in the set A [row_start ... row_start + row_length-1].
   1528   The columns under consideration are currently *not* in the degree lists,
   1529   and have already been placed in the hash buckets.
   1530 
   1531   The hash bucket for columns whose hash function is equal to h is stored
   1532   as follows:
   1533 
   1534   if head [h] is >= 0, then head [h] contains a degree list, so:
   1535 
   1536   head [h] is the first column in degree bucket h.
   1537   Col [head [h]].headhash gives the first column in hash bucket h.
   1538 
   1539   otherwise, the degree list is empty, and:
   1540 
   1541   -(head [h] + 2) is the first column in hash bucket h.
   1542 
   1543   For a column c in a hash bucket, Col [c].shared3.prev is NOT a "previous
   1544   column" pointer.  Col [c].shared3.hash is used instead as the hash number
   1545   for that column.  The value of Col [c].shared4.hash_next is the next column
   1546   in the same hash bucket.
   1547 
   1548   Assuming no, or "few" hash collisions, the time taken by this routine is
   1549   linear in the sum of the sizes (lengths) of each column whose score has
   1550   just been computed in the approximate degree computation.
   1551   Not user-callable.
   1552 */
   1553 template <typename Index>
   1554 static void detect_super_cols
   1555 (
   1556   /* === Parameters ======================================================= */
   1557 
   1558   colamd_col<Index> Col [],    /* of size n_col+1 */
   1559   Index A [],     /* row indices of A */
   1560   Index head [],    /* head of degree lists and hash buckets */
   1561   Index row_start,    /* pointer to set of columns to check */
   1562   Index row_length    /* number of columns to check */
   1563 )
   1564 {
   1565   /* === Local variables ================================================== */
   1566 
   1567   Index hash ;      /* hash value for a column */
   1568   Index *rp ;     /* pointer to a row */
   1569   Index c ;     /* a column index */
   1570   Index super_c ;   /* column index of the column to absorb into */
   1571   Index *cp1 ;      /* column pointer for column super_c */
   1572   Index *cp2 ;      /* column pointer for column c */
   1573   Index length ;    /* length of column super_c */
   1574   Index prev_c ;    /* column preceding c in hash bucket */
   1575   Index i ;     /* loop counter */
   1576   Index *rp_end ;   /* pointer to the end of the row */
   1577   Index col ;     /* a column index in the row to check */
   1578   Index head_column ;   /* first column in hash bucket or degree list */
   1579   Index first_col ;   /* first column in hash bucket */
   1580 
   1581   /* === Consider each column in the row ================================== */
   1582 
   1583   rp = &A [row_start] ;
   1584   rp_end = rp + row_length ;
   1585   while (rp < rp_end)
   1586   {
   1587     col = *rp++ ;
   1588     if (COL_IS_DEAD (col))
   1589     {
   1590       continue ;
   1591     }
   1592 
   1593     /* get hash number for this column */
   1594     hash = Col [col].shared3.hash ;
   1595     COLAMD_ASSERT (hash <= n_col) ;
   1596 
   1597     /* === Get the first column in this hash bucket ===================== */
   1598 
   1599     head_column = head [hash] ;
   1600     if (head_column > COLAMD_EMPTY)
   1601     {
   1602       first_col = Col [head_column].shared3.headhash ;
   1603     }
   1604     else
   1605     {
   1606       first_col = - (head_column + 2) ;
   1607     }
   1608 
   1609     /* === Consider each column in the hash bucket ====================== */
   1610 
   1611     for (super_c = first_col ; super_c != COLAMD_EMPTY ;
   1612 	 super_c = Col [super_c].shared4.hash_next)
   1613     {
   1614       COLAMD_ASSERT (COL_IS_ALIVE (super_c)) ;
   1615       COLAMD_ASSERT (Col [super_c].shared3.hash == hash) ;
   1616       length = Col [super_c].length ;
   1617 
   1618       /* prev_c is the column preceding column c in the hash bucket */
   1619       prev_c = super_c ;
   1620 
   1621       /* === Compare super_c with all columns after it ================ */
   1622 
   1623       for (c = Col [super_c].shared4.hash_next ;
   1624 	   c != COLAMD_EMPTY ; c = Col [c].shared4.hash_next)
   1625       {
   1626 	COLAMD_ASSERT (c != super_c) ;
   1627 	COLAMD_ASSERT (COL_IS_ALIVE (c)) ;
   1628 	COLAMD_ASSERT (Col [c].shared3.hash == hash) ;
   1629 
   1630 	/* not identical if lengths or scores are different */
   1631 	if (Col [c].length != length ||
   1632 	    Col [c].shared2.score != Col [super_c].shared2.score)
   1633 	{
   1634 	  prev_c = c ;
   1635 	  continue ;
   1636 	}
   1637 
   1638 	/* compare the two columns */
   1639 	cp1 = &A [Col [super_c].start] ;
   1640 	cp2 = &A [Col [c].start] ;
   1641 
   1642 	for (i = 0 ; i < length ; i++)
   1643 	{
   1644 	  /* the columns are "clean" (no dead rows) */
   1645 	  COLAMD_ASSERT (ROW_IS_ALIVE (*cp1))  ;
   1646 	  COLAMD_ASSERT (ROW_IS_ALIVE (*cp2))  ;
   1647 	  /* row indices will same order for both supercols, */
   1648 	  /* no gather scatter nessasary */
   1649 	  if (*cp1++ != *cp2++)
   1650 	  {
   1651 	    break ;
   1652 	  }
   1653 	}
   1654 
   1655 	/* the two columns are different if the for-loop "broke" */
   1656 	if (i != length)
   1657 	{
   1658 	  prev_c = c ;
   1659 	  continue ;
   1660 	}
   1661 
   1662 	/* === Got it!  two columns are identical =================== */
   1663 
   1664 	COLAMD_ASSERT (Col [c].shared2.score == Col [super_c].shared2.score) ;
   1665 
   1666 	Col [super_c].shared1.thickness += Col [c].shared1.thickness ;
   1667 	Col [c].shared1.parent = super_c ;
   1668 	KILL_NON_PRINCIPAL_COL (c) ;
   1669 	/* order c later, in order_children() */
   1670 	Col [c].shared2.order = COLAMD_EMPTY ;
   1671 	/* remove c from hash bucket */
   1672 	Col [prev_c].shared4.hash_next = Col [c].shared4.hash_next ;
   1673       }
   1674     }
   1675 
   1676     /* === Empty this hash bucket ======================================= */
   1677 
   1678     if (head_column > COLAMD_EMPTY)
   1679     {
   1680       /* corresponding degree list "hash" is not empty */
   1681       Col [head_column].shared3.headhash = COLAMD_EMPTY ;
   1682     }
   1683     else
   1684     {
   1685       /* corresponding degree list "hash" is empty */
   1686       head [hash] = COLAMD_EMPTY ;
   1687     }
   1688   }
   1689 }
   1690 
   1691 
   1692 /* ========================================================================== */
   1693 /* === garbage_collection =================================================== */
   1694 /* ========================================================================== */
   1695 
   1696 /*
   1697   Defragments and compacts columns and rows in the workspace A.  Used when
   1698   all avaliable memory has been used while performing row merging.  Returns
   1699   the index of the first free position in A, after garbage collection.  The
   1700   time taken by this routine is linear is the size of the array A, which is
   1701   itself linear in the number of nonzeros in the input matrix.
   1702   Not user-callable.
   1703 */
   1704 template <typename Index>
   1705 static Index garbage_collection  /* returns the new value of pfree */
   1706   (
   1707     /* === Parameters ======================================================= */
   1708 
   1709     Index n_row,      /* number of rows */
   1710     Index n_col,      /* number of columns */
   1711     Colamd_Row<Index> Row [],    /* row info */
   1712     colamd_col<Index> Col [],    /* column info */
   1713     Index A [],     /* A [0 ... Alen-1] holds the matrix */
   1714     Index *pfree      /* &A [0] ... pfree is in use */
   1715     )
   1716 {
   1717   /* === Local variables ================================================== */
   1718 
   1719   Index *psrc ;     /* source pointer */
   1720   Index *pdest ;    /* destination pointer */
   1721   Index j ;     /* counter */
   1722   Index r ;     /* a row index */
   1723   Index c ;     /* a column index */
   1724   Index length ;    /* length of a row or column */
   1725 
   1726   /* === Defragment the columns =========================================== */
   1727 
   1728   pdest = &A[0] ;
   1729   for (c = 0 ; c < n_col ; c++)
   1730   {
   1731     if (COL_IS_ALIVE (c))
   1732     {
   1733       psrc = &A [Col [c].start] ;
   1734 
   1735       /* move and compact the column */
   1736       COLAMD_ASSERT (pdest <= psrc) ;
   1737       Col [c].start = (Index) (pdest - &A [0]) ;
   1738       length = Col [c].length ;
   1739       for (j = 0 ; j < length ; j++)
   1740       {
   1741 	r = *psrc++ ;
   1742 	if (ROW_IS_ALIVE (r))
   1743 	{
   1744 	  *pdest++ = r ;
   1745 	}
   1746       }
   1747       Col [c].length = (Index) (pdest - &A [Col [c].start]) ;
   1748     }
   1749   }
   1750 
   1751   /* === Prepare to defragment the rows =================================== */
   1752 
   1753   for (r = 0 ; r < n_row ; r++)
   1754   {
   1755     if (ROW_IS_ALIVE (r))
   1756     {
   1757       if (Row [r].length == 0)
   1758       {
   1759 	/* this row is of zero length.  cannot compact it, so kill it */
   1760 	COLAMD_DEBUG3 (("Defrag row kill\n")) ;
   1761 	KILL_ROW (r) ;
   1762       }
   1763       else
   1764       {
   1765 	/* save first column index in Row [r].shared2.first_column */
   1766 	psrc = &A [Row [r].start] ;
   1767 	Row [r].shared2.first_column = *psrc ;
   1768 	COLAMD_ASSERT (ROW_IS_ALIVE (r)) ;
   1769 	/* flag the start of the row with the one's complement of row */
   1770 	*psrc = ONES_COMPLEMENT (r) ;
   1771 
   1772       }
   1773     }
   1774   }
   1775 
   1776   /* === Defragment the rows ============================================== */
   1777 
   1778   psrc = pdest ;
   1779   while (psrc < pfree)
   1780   {
   1781     /* find a negative number ... the start of a row */
   1782     if (*psrc++ < 0)
   1783     {
   1784       psrc-- ;
   1785       /* get the row index */
   1786       r = ONES_COMPLEMENT (*psrc) ;
   1787       COLAMD_ASSERT (r >= 0 && r < n_row) ;
   1788       /* restore first column index */
   1789       *psrc = Row [r].shared2.first_column ;
   1790       COLAMD_ASSERT (ROW_IS_ALIVE (r)) ;
   1791 
   1792       /* move and compact the row */
   1793       COLAMD_ASSERT (pdest <= psrc) ;
   1794       Row [r].start = (Index) (pdest - &A [0]) ;
   1795       length = Row [r].length ;
   1796       for (j = 0 ; j < length ; j++)
   1797       {
   1798 	c = *psrc++ ;
   1799 	if (COL_IS_ALIVE (c))
   1800 	{
   1801 	  *pdest++ = c ;
   1802 	}
   1803       }
   1804       Row [r].length = (Index) (pdest - &A [Row [r].start]) ;
   1805 
   1806     }
   1807   }
   1808   /* ensure we found all the rows */
   1809   COLAMD_ASSERT (debug_rows == 0) ;
   1810 
   1811   /* === Return the new value of pfree ==================================== */
   1812 
   1813   return ((Index) (pdest - &A [0])) ;
   1814 }
   1815 
   1816 
   1817 /* ========================================================================== */
   1818 /* === clear_mark =========================================================== */
   1819 /* ========================================================================== */
   1820 
   1821 /*
   1822   Clears the Row [].shared2.mark array, and returns the new tag_mark.
   1823   Return value is the new tag_mark.  Not user-callable.
   1824 */
   1825 template <typename Index>
   1826 static inline  Index clear_mark  /* return the new value for tag_mark */
   1827   (
   1828       /* === Parameters ======================================================= */
   1829 
   1830     Index n_row,    /* number of rows in A */
   1831     Colamd_Row<Index> Row [] /* Row [0 ... n_row-1].shared2.mark is set to zero */
   1832     )
   1833 {
   1834   /* === Local variables ================================================== */
   1835 
   1836   Index r ;
   1837 
   1838   for (r = 0 ; r < n_row ; r++)
   1839   {
   1840     if (ROW_IS_ALIVE (r))
   1841     {
   1842       Row [r].shared2.mark = 0 ;
   1843     }
   1844   }
   1845   return (1) ;
   1846 }
   1847 
   1848 
   1849 } // namespace internal
   1850 #endif
   1851